[memdisk] Add drive shift limit logic
[profile/ivi/syslinux.git] / memdisk / memdisk.inc
1 ; -*- fundamental -*- (asm-mode sucks)
2 ; ****************************************************************************
3 ;
4 ;  memdisk.inc
5 ;
6 ;  A program to emulate an INT 13h disk BIOS from a "disk" in extended
7 ;  memory.
8 ;
9 ;   Copyright 2001-2009 H. Peter Anvin - All Rights Reserved
10 ;   Copyright 2009 Intel Corporation; author: H. Peter Anvin
11 ;
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.
17 ;
18 ; ****************************************************************************
19
20 %include "../version.gen"
21
22 ; %define DEBUG_TRACERS                 ; Uncomment to get debugging tracers
23
24 %ifdef DEBUG_TRACERS
25
26 %macro TRACER   1
27         call debug_tracer
28         db %1
29 %endmacro
30 %macro WRITEHEX2 0-1 al
31 %ifnidni %1,al
32         push ax
33         mov al,%1
34         call writehex2
35         pop ax
36 %else
37         call writehex2
38 %endif
39 %endmacro
40 %macro WRITEHEX4 0-1 ax
41 %ifnidni %1,ax
42         push ax
43         mov ax,%1
44         call writehex4
45         pop ax
46 %else
47         call writehex4
48 %endif
49 %endmacro
50 %macro WRITEHEX8 0-1 eax
51 %ifnidni %1,eax
52         push eax
53         mov eax,%1
54         call writehex8
55         pop eax
56 %else
57         call writehex8
58 %endif
59 %endmacro
60
61 %else   ; DEBUG_TRACERS
62
63 %macro  TRACER  1
64 %endmacro
65 %macro WRITEHEX2 0-1
66 %endmacro
67 %macro WRITEHEX4 0-1
68 %endmacro
69 %macro WRITEHEX8 0-1
70 %endmacro
71
72 %endif  ; DEBUG_TRACERS
73
74 ; Flags we test our configuration against
75 %define CONFIG_READONLY 0x01
76 %define CONFIG_RAW      0x02
77 %define CONFIG_SAFEINT  0x04
78 %define CONFIG_BIGRAW   0x08            ; MUST be 8!
79
80                 org 0h
81
82 %define SECTORSIZE_LG2  9               ; log2(sector size)
83 %define SECTORSIZE      (1 << SECTORSIZE_LG2)
84
85                 ; Parameter registers definition; this is the definition
86                 ; of the stack frame.
87 %define         P_DS            word [bp+34]
88 %define         P_ES            word [bp+32]
89 %define         P_EAX           dword [bp+28]
90 %define         P_HAX           word [bp+30]
91 %define         P_AX            word [bp+28]
92 %define         P_AL            byte [bp+28]
93 %define         P_AH            byte [bp+29]
94 %define         P_ECX           dword [bp+24]
95 %define         P_HCX           word [bp+26]
96 %define         P_CX            word [bp+24]
97 %define         P_CL            byte [bp+24]
98 %define         P_CH            byte [bp+25]
99 %define         P_EDX           dword [bp+20]
100 %define         P_HDX           word [bp+22]
101 %define         P_DX            word [bp+20]
102 %define         P_DL            byte [bp+20]
103 %define         P_DH            byte [bp+21]
104 %define         P_EBX           dword [bp+16]
105 %define         P_HBX           word [bp+18]
106 %define         P_HBXL          byte [bp+18]
107 %define         P_BX            word [bp+16]
108 %define         P_BL            byte [bp+16]
109 %define         P_BH            byte [bp+17]
110 %define         P_EBP           dword [bp+8]
111 %define         P_BP            word [bp+8]
112 %define         P_ESI           dword [bp+4]
113 %define         P_SI            word [bp+4]
114 %define         P_EDI           dword [bp]
115 %define         P_DI            word [bp]
116
117                 section .text
118                 ; These pointers are used by the installer and
119                 ; must be first in the binary
120 Pointers:       dw Int13Start
121                 dw Int15Start
122                 dw PatchArea
123                 dw TotalSize
124                 dw IretPtr
125
126 IretPtr         equ Int13Start.iret
127 Int13Start:
128                 cmp word [cs:Recursive],0
129                 jne recursive
130
131                 ; Swap stack
132                 mov [cs:Stack],esp
133                 mov [cs:Stack+4],ss
134                 mov [cs:SavedAX],ax
135                 mov ax,cs
136                 mov ss,ax
137                 mov sp,[cs:MyStack]
138
139                 ; See if DL points to our class of device (FD, HD)
140                 push dx
141                 push dx
142                 xor dl,[cs:DriveNo]
143                 pop dx
144                 js .nomatch             ; If SF=0, we have a class match here
145                                         ; 0x00 the sign bit for FD
146                                         ; 0x80 the sign bit for HD
147                 jz our_drive            ; If ZF=1, we have an exact match
148                 cmp dl,[cs:DriveNo]
149                 jb .nomatch             ; Drive < Our drive
150                 cmp dl,[cs:DriveShiftLimit]
151                 jae .nomatch            ; Drive > The maximum drive
152                                         ; number that we will shift for.
153                                         ; This leaves any higher-up BIOS
154                                         ; drives alone, such as an optical
155                                         ; disc drive 0xA0 or 0xE0
156                 dec dl                  ; Drive > Our drive, adjust drive #
157 .nomatch:
158                 TRACER '!'
159                 WRITEHEX2 dl
160                 TRACER ','
161                 mov ax,[cs:SavedAX]
162                 WRITEHEX4
163                 inc word [cs:Recursive]
164                 pushf
165                 call far [cs:OldInt13]
166                 pushf
167                 dec word [cs:Recursive]
168                 push bp
169                 mov bp,sp
170                 cmp byte [cs:SavedAX+1],08h     ; Get drive params function?
171                 je .norestoredl                 ; DL = number of drives
172                 cmp byte [cs:SavedAX+1],15h     ; Get disk type function?
173                 je .norestoredl                 ; CX:DX = size of device
174 .restoredl:
175                 mov dl,[bp+4]
176 .norestoredl:
177                 push ax
178                 push ebx
179                 push ds
180                 mov ax,[bp+2]           ; Flags
181                 lds ebx,[cs:Stack]
182                 mov [bx+4],al           ; Arithmetic flags
183                 pop ds
184                 pop ebx
185                 pop ax
186                 pop bp
187                 lss esp,[cs:Stack]
188 .iret:          iret
189
190 recursive:
191                 TRACER '@'
192 jmp_oldint13:
193                 jmp far [cs:OldInt13]
194
195 our_drive:
196                 ; Set up standard entry frame
197                 push ds
198                 push es
199                 mov ds,ax
200                 mov es,ax
201                 mov ax,[SavedAX]
202                 pushad
203                 mov bp,sp               ; Point BP to the entry stack frame
204                 TRACER 'F'
205                 WRITEHEX4
206                 ; Note: AX == P_AX here
207                 cmp ah,Int13FuncsCnt-1
208                 ja Invalid_jump
209                 xor al,al               ; AL = 0 is standard entry condition
210                 mov di,ax
211                 shr di,7                ; Convert AH to an offset in DI
212                 call [Int13Funcs+di]
213
214 Done:           ; Standard routine for return
215                 mov P_AX,ax
216 DoneWeird:
217                 TRACER 'D'
218                 xor bx,bx
219                 mov es,bx
220                 mov bx,[StatusPtr]
221                 mov [es:bx],ah          ; Save status
222                 and ah,ah
223
224                 lds ebx,[Stack]
225                 ; This sets the low byte (the arithmetic flags) of the
226                 ; FLAGS on stack to either 00h (no flags) or 01h (CF)
227                 ; depending on if AH was zero or not.
228                 setnz [bx+4]            ; Set CF iff error
229                 popad
230                 pop es
231                 pop ds
232                 lss esp,[cs:Stack]
233                 iret
234
235 Reset:
236                 ; Reset affects multiple drives, so we need to pass it on
237                 TRACER 'R'
238                 xor ax,ax               ; Bottom of memory
239                 mov es,ax
240                 test dl,dl              ; Always pass it on if we are
241                                         ; resetting HD
242                 js .hard_disk           ; Bit 7 set
243                 ; Some BIOSes get very unhappy if we pass a reset floppy
244                 ; command to them and don't actually have any floppies.
245                 ; This is a bug, but we have to deal with it nontheless.
246                 ; Therefore, if we are the *ONLY* floppy drive, and the
247                 ; user didn't request HD reset, then just drop the command.
248                 ; BIOS equipment byte, top two bits + 1 == total # of floppies
249                 test byte [es:0x410],0C0h
250                 jz success
251                 jmp .pass_on            ; ... otherwise pass it to the BIOS
252 .hard_disk:
253                 ; ... same thing for hard disks, sigh ...
254                 cmp byte [es:0x475],1   ; BIOS variable for number of hard
255                                         ; disks
256                 jbe success
257
258 .pass_on:
259                 pop ax                  ; Drop return address
260                 popad                   ; Restore all registers
261                 pop es
262                 pop ds
263                 lss esp,[cs:Stack]      ; Restore the stack
264                 and dl,80h              ; Clear all but the type bit
265                 jmp jmp_oldint13
266
267
268 Invalid:
269                 pop dx                  ; Drop return address
270 Invalid_jump:
271                 TRACER 'I'
272                 mov ah,01h              ; Unsupported function
273                 jmp short Done
274
275 GetDriveType:
276                 test byte [DriveNo],80h
277                 mov bl,02h              ; Type 02h = floppy with changeline
278                 jz .floppy
279                 ; Hard disks only!  DO NOT set CX:DX for floppies...
280                 ; it apparently causes Win98SE DOS to go into an loop
281                 ; resetting the drive over and over.  Sigh.
282                 inc bx                  ; Type = 03h
283                 mov dx,[DiskSize]       ; Return the disk size in sectors
284                 mov P_DX,dx
285                 mov cx,[DiskSize+2]
286                 mov P_CX,cx
287 .floppy:
288                 mov P_AH,bl             ; 02h floppy, 03h hard disk
289                 pop ax                  ; Drop return address
290                 xor ax,ax               ; Success...
291                 jmp DoneWeird           ; But don't stick it into P_AX
292
293 GetStatus:
294                 xor ax,ax
295                 mov es,ax
296                 mov bx,[StatusPtr]
297                 mov ah,[bx]             ; Copy last status
298                 ret
299
300 ReadMult:
301                 TRACER 'm'
302 Read:
303                 TRACER 'R'
304                 call setup_regs
305 do_copy:
306                 TRACER '<'
307                 call bcopy
308                 TRACER '>'
309                 movzx ax,P_AL           ; AH = 0, AL = transfer count
310                 ret
311
312 WriteMult:
313                 TRACER 'M'
314 Write:
315                 TRACER 'W'
316                 test byte [ConfigFlags],CONFIG_READONLY
317                 jnz .readonly
318                 call setup_regs
319                 xchg esi,edi            ; Opposite direction of a Read!
320                 jmp short do_copy
321 .readonly:      mov ah,03h              ; Write protected medium
322                 ret
323
324                 ; Verify integrity; just bounds-check
325 Seek:
326 Verify:
327                 call setup_regs         ; Returns error if appropriate
328                 ; And fall through to success
329
330 CheckIfReady:                           ; These are always-successful noop functions
331 Recalibrate:
332 InitWithParms:
333 DetectChange:
334 EDDDetectChange:
335 EDDLock:
336 SetMode:
337 success:
338                 xor ax,ax               ; Always successful
339                 ret
340
341 GetParms:
342                 TRACER 'G'
343                 mov dl,[DriveCnt]       ; Cached data
344                 mov P_DL,dl
345                 test byte [DriveNo],80h
346                 jnz .hd
347                 mov P_DI,DPT
348                 mov P_ES,cs
349                 mov bl,[DriveType]
350                 mov P_BL,bl
351 .hd:
352                 mov ax,[Cylinders]
353                 dec ax                  ; We report the highest #, not the count
354                 xchg al,ah
355                 shl al,6
356                 or al,[Sectors]
357                 mov P_CX,ax
358                 mov ax,[Heads]
359                 dec ax
360                 mov P_DH,al
361
362                 ;
363                 ; Is this MEMDISK installation check?
364                 ;
365                 cmp P_HAX,'ME'
366                 jne .notic
367                 cmp P_HCX,'MD'
368                 jne .notic
369                 cmp P_HDX,'IS'
370                 jne .notic
371                 cmp P_HBX,'K?'
372                 jne .notic
373
374                 ; MEMDISK installation check...
375                 mov P_HAX,'!M'
376                 mov P_HCX,'EM'
377                 mov P_HDX,'DI'
378                 mov P_HBX,'SK'
379                 mov P_ES,cs
380                 mov P_DI,MemDisk_Info
381
382 .notic:
383                 xor ax,ax
384                 ret
385 ;
386 ; EDD functions -- only if enabled
387 ;
388 %if EDD
389 EDDPresence:
390                 TRACER 'E'
391                 TRACER 'c'
392
393                 cmp P_BX,55AAh
394                 jne Invalid
395                 mov P_BX,0AA55h         ; EDD signature
396                 mov P_AX,03000h         ; EDD 3.0
397                 mov P_CX,0003h          ; Bit 0 - Fixed disk access subset
398                                         ; Bit 1 - Locking and ejecting subset
399                 pop ax                  ; Drop return address
400                 xor ax,ax               ; Success
401                 jmp DoneWeird           ; Success, but AH != 0, sigh...
402
403 EDDRead:
404                 TRACER 'E'
405                 TRACER 'r'
406
407                 call edd_setup_regs
408                 call bcopy
409                 xor ax,ax
410                 ret
411
412 EDDWrite:
413                 TRACER 'E'
414                 TRACER 'w'
415
416                 call edd_setup_regs
417                 xchg esi,edi            ; Opposite direction of a Read!
418                 call bcopy
419                 xor ax,ax
420                 ret
421
422 EDDVerify:
423 EDDSeek:
424                 call edd_setup_regs     ; Just bounds checking
425                 xor ax,ax
426                 ret
427
428 EDDGetParms:
429                 TRACER 'E'
430                 TRACER 'p'
431
432                 mov es,P_DS
433                 mov di,P_SI
434                 mov si,EDD_DPT
435
436                 lodsw                   ; Length of our DPT
437                 mov cx,[es:di]
438                 cmp cx,26               ; Minimum size
439                 jb .overrun
440
441                 cmp cx,ax
442                 jb .oksize
443                 mov cx,ax
444
445 .oksize:
446                 mov ax,cx
447                 stosw
448                 dec cx
449                 dec cx
450                 rep movsb
451
452                 xor ax,ax
453                 ret
454
455 .overrun:
456                 mov ax,0100h
457                 ret
458 %endif ; EDD
459
460                 ; Set up registers as for a "Read", and compares against disk
461                 ; size.
462                 ; WARNING: This fails immediately, even if we can transfer some
463                 ; sectors.  This isn't really the correct behaviour.
464 setup_regs:
465
466                 ; Convert a CHS address in P_CX/P_DH into an LBA in eax
467                 ; CH = cyl[7:0]
468                 ; CL[0:5] = sector (1-based)  CL[7:6] = cyl[9:8]
469                 ; DH = head
470                 movzx ecx,P_CX
471                 movzx ebx,cl            ; Sector number
472                 and bl,3Fh
473                 dec ebx                 ; Sector number is 1-based
474                 cmp bx,[Sectors]
475                 jae .overrun
476                 movzx edi,P_DH          ; Head number
477                 movzx eax,word [Heads]
478                 cmp di,ax
479                 jae .overrun
480                 shr cl,6
481                 xchg cl,ch              ; Now (E)CX <- cylinder number
482                 mul ecx                 ; eax <- Heads*cyl# (edx <- 0)
483                 add eax,edi
484                 mul dword [Sectors]
485                 add eax,ebx
486                 ; Now eax = LBA, edx = 0
487
488                 ;
489                 ; setup_regs continues...
490                 ;
491                 ; Note: edi[31:16] and ecx[31:16] = 0 already
492                 mov di,P_BX             ; Get linear address of target buffer
493                 mov cx,P_ES
494                 shl ecx,4
495                 add edi,ecx             ; EDI = address to fetch to
496                 movzx ecx,P_AL          ; Sector count
497                 mov esi,eax
498                 add eax,ecx             ; LBA of final sector + 1
499                 shl esi,SECTORSIZE_LG2  ; LBA -> byte offset
500                 add esi,[DiskBuf]       ; Get address in high memory
501                 cmp eax,[DiskSize]      ; Check the high mark against limit
502                 ja .overrun
503                 shl ecx,SECTORSIZE_LG2-2 ; Convert count to dwords
504                 ret
505
506 .overrun:       pop ax                  ; Drop setup_regs return address
507                 mov ax,0200h            ; Missing address mark
508                 ret                     ; Return to Done
509
510                 ; Set up registers as for an EDD Read, and compares against disk size.
511 %if EDD
512 edd_setup_regs:
513                 push es
514                 mov si,P_SI             ; DS:SI -> DAPA
515                 mov es,P_DS
516
517                 mov dx,[es:si]
518                 cmp dx,16
519                 jb .baddapa
520
521                 cmp dword [es:si+4],-1
522                 je .linear_address
523
524                 movzx ebx,word [es:si+4]        ; Offset
525                 movzx edi,word [es:si+6]        ; Segment
526                 shl edi,4
527                 add ebx,edi
528                 jmp .got_address
529
530 .linear_address:
531                 cmp dx,24               ; Must be large enough to hold
532                                         ; linear address
533                 jb .baddapa
534
535                 cmp dword [es:si+20],0  ; > 4 GB addresses not supported
536                 mov ax,0900h            ; "Data boundary error" - bogus, but
537                                         ; no really better code available
538                 jne .error
539
540                 mov ebx,[es:si+16]
541
542 .got_address:
543                 cmp dword [es:si+12],0          ; LBA too large?
544                 jne .overrun
545
546                 movzx ecx, word [es:si+2]       ; Sectors to transfer
547                 mov esi,[es:si+8]               ; Starting sector
548                 mov eax,esi
549                 add eax,ecx
550                 jc .overrun
551                 cmp eax,[DiskSize]
552                 ja .overrun
553
554                 shl ecx,SECTORSIZE_LG2-2        ; Convert to dwords
555                 shl esi,SECTORSIZE_LG2          ; Convert to an offset
556                 add esi,[DiskBuf]
557                 mov edi,ebx
558                 pop es
559                 ret
560
561 .baddapa:
562                 mov ax,0100h            ; Invalid command
563                 pop es
564                 pop ax                  ; Drop setup_regs return address
565                 ret
566
567 .overrun:
568                 mov ax,0200h            ; "Address mark not found" =
569                                         ; LBA beyond end of disk
570 .error:
571                 and word [es:si+2],0    ; No sectors transferred
572                 pop es
573                 pop ax
574                 ret
575
576 EDDEject:
577                 mov ax,0B200h           ; Volume Not Removable
578                 ret
579
580 %endif ; EDD
581
582
583 ;
584 ; INT 15h intercept routines
585 ;
586 int15_e820:
587                 cmp edx,534D4150h       ; "SMAP"
588                 jne oldint15
589                 cmp ecx,20              ; Need 20 bytes
590                 jb err86
591                 push ds
592                 push cs
593                 pop ds
594                 push edx                ; "SMAP"
595                 and ebx,ebx
596                 jne .renew
597                 mov ebx,E820Table
598 .renew:
599                 add bx,12               ; Advance to next
600                 mov eax,[bx-4]          ; Type
601                 and eax,eax             ; Null type?
602                 jz .renew               ; If so advance to next
603                 mov [es:di+16],eax
604                 mov eax,[bx-12]         ; Start addr (low)
605                 mov edx,[bx-8]          ; Start addr (high)
606                 mov [es:di],eax
607                 mov [es:di+4],edx
608                 mov eax,[bx]            ; End addr (low)
609                 mov edx,[bx+4]          ; End addr (high)
610                 sub eax,[bx-12]         ; Derive the length
611                 sbb edx,[bx-8]
612                 mov [es:di+8],eax       ; Length (low)
613                 mov [es:di+12],edx      ; Length (high)
614                 cmp dword [bx+8],-1     ; Type of next = end?
615                 jne .notdone
616                 xor ebx,ebx             ; Done with table
617 .notdone:
618                 pop eax                 ; "SMAP"
619                 mov edx,eax             ; Some systems expect eax = edx = SMAP
620                 mov ecx,20              ; Bytes loaded
621                 pop ds
622 int15_success:
623                 mov byte [bp+6], 02h    ; Clear CF
624                 pop bp
625                 iret
626
627 err86:
628                 mov byte [bp+6], 03h    ; Set CF
629                 mov ah,86h
630                 pop bp
631                 iret
632
633 Int15Start:
634                 push bp
635                 mov bp,sp
636                 cmp ax,0E820h
637                 je near int15_e820
638                 cmp ax,0E801h
639                 je int15_e801
640                 cmp ax,0E881h
641                 je int15_e881
642                 cmp ah,88h
643                 je int15_88
644 oldint15:       pop bp
645                 jmp far [cs:OldInt15]
646
647 int15_e801:                             ; Get mem size for > 64 MB config
648                 mov ax,[cs:Mem1MB]
649                 mov cx,ax
650                 mov bx,[cs:Mem16MB]
651                 mov dx,bx
652                 jmp short int15_success
653
654 int15_e881:                             ; Get mem size for > 64 MB config
655                                         ; 32-bit code
656                 mov eax,[cs:Mem1MB]
657                 mov ecx,eax
658                 mov ebx,[cs:Mem16MB]
659                 mov edx,ebx
660                 jmp short int15_success
661
662 int15_88:                               ; Get extended mem size
663                 mov ax,[cs:MemInt1588]
664                 jmp short int15_success
665
666 ;
667 ; Routine to copy in/out of high memory
668 ; esi = linear source address
669 ; edi = linear target address
670 ; ecx = 32-bit word count
671 ;
672 ; Assumes cs = ds = es
673 ;
674 bcopy:
675                 push eax
676                 push ebx
677                 push edx
678                 push ebp
679
680                 mov bx, real_int15_stub
681
682                 test byte [ConfigFlags], CONFIG_RAW|CONFIG_SAFEINT
683                 jz .anymode             ; Always do the real INT 15h
684
685                 smsw ax                 ; Unprivileged!
686                 test al,01h
687                 jnz .protmode           ; Protmode -> do real INT 15h
688
689 .realmode:
690                 ; Raw or Safeint mode, and we're in real mode...
691
692                 test byte [ConfigFlags], CONFIG_SAFEINT
693                 jnz .fakeint15
694
695 .raw:
696                 TRACER 'r'
697                 ; We're in real mode, do it outselves
698
699                 pushfd                  ; <A>
700                 push ds                 ; <B>
701                 push es                 ; <C>
702
703                 cli
704                 cld
705
706                 xor ebx,ebx
707                 mov bx,cs
708                 shl ebx,4
709                 lea edx,[Shaker+ebx]
710                 mov [Shaker+2],edx
711
712                 ; Test to see if A20 is enabled or not
713                 xor ax,ax
714                 mov ds,ax
715                 dec ax
716                 mov es,ax
717
718                 mov ax,[0]
719                 mov bx,ax
720                 xor bx,[es:10h]
721                 not ax
722                 mov [0],ax
723                 mov dx,ax
724                 xor dx,[es:10h]
725                 not ax
726                 mov [0],ax
727
728                 or dx,bx
729                 push dx                 ; <D> Save A20 status
730                 jnz .skip_a20e
731
732                 mov ax,2401h            ; Enable A20
733                 int 15h
734 .skip_a20e:
735                 mov dl,[ConfigFlags]
736                 and dx,CONFIG_BIGRAW
737                 add dx,8
738                 ; DX = 16 for BIGRAW, 8 for RAW
739                 ;  8 is selector for a 64K flat segment,
740                 ; 16 is selector for a 4GB flat segment.
741
742                 lgdt [cs:Shaker]
743                 mov eax,cr0
744                 or al,01h
745                 mov cr0,eax
746
747                 mov bx,16               ; Large flat segment
748                 mov ds,bx
749                 mov es,bx
750
751                 a32 rep movsd
752
753                 ; DX has the appropriate value to put in
754                 ; the registers on return
755                 mov ds,dx
756                 mov es,dx
757
758                 and al,~01h
759                 mov cr0,eax
760
761                 pop dx                  ; <D> A20 status
762                 pop es                  ; <C>
763                 pop ds                  ; <B>
764
765                 and dx,dx
766                 jnz .skip_a20d
767                 mov ax,2400h            ; Disable A20
768                 int 15h
769 .skip_a20d:
770                 popfd                   ; <A>
771                 jmp .done
772
773 .fakeint15:
774                 ; We're in real mode with CONFIG_SAFEINT, invoke the
775                 ; original INT 15h vector.  We used to test for the
776                 ; INT 15h vector being unchanged here, but that is
777                 ; *us*; however, the test was wrong for years (always
778                 ; negative) so instead of fixing the test do what we
779                 ; tested and don't bother probing.
780                 mov bx, fake_int15_stub
781
782 .protmode:
783                 TRACER 'p'
784 .anymode:
785
786 .copy_loop:
787                 push esi
788                 push edi
789                 push ecx
790                 cmp ecx,4000h
791                 jna .safe_size
792                 mov ecx,4000h
793 .safe_size:
794                 push ecx        ; Transfer size this cycle
795                 mov eax, esi
796                 mov [Mover_src1], si
797                 shr eax, 16
798                 mov [Mover_src1+2], al
799                 mov [Mover_src2], ah
800                 mov eax, edi
801                 mov [Mover_dst1], di
802                 shr eax, 16
803                 mov [Mover_dst1+2], al
804                 mov [Mover_dst2], ah
805                 mov si,Mover
806                 mov ah, 87h
807                 shl cx,1        ; Convert to 16-bit words
808                 call bx         ; INT 15h stub
809                 pop eax         ; Transfer size this cycle
810                 pop ecx
811                 pop edi
812                 pop esi
813                 jc .error
814                 lea esi,[esi+4*eax]
815                 lea edi,[edi+4*eax]
816                 sub ecx, eax
817                 jnz .copy_loop
818                 ; CF = 0
819 .error:
820 .done:
821                 pop ebp
822                 pop edx
823                 pop ebx
824                 pop eax
825                 ret
826
827 real_int15_stub:
828                 int 15h
829                 cli             ; Some BIOSes enable interrupts on INT 15h
830                 ret
831
832 fake_int15_stub:
833                 pushf
834                 call far [OldInt15]
835                 cli
836                 ret
837
838 %ifdef DEBUG_TRACERS
839 debug_tracer:   pushad
840                 pushfd
841                 mov bp,sp
842                 mov bx,[bp+9*4]
843                 mov al,[cs:bx]
844                 inc word [bp+9*4]
845                 mov ah,0Eh
846                 mov bx,7
847                 int 10h
848                 popfd
849                 popad
850                 ret
851
852 writehex2:      pushad
853                 pushfd
854                 mov cx,2
855                 ror eax,4
856                 jmp writehex_common
857 writehex4:      pushad
858                 pushfd
859                 mov cx,4
860                 ror eax,12
861                 jmp writehex_common
862 writehex8:      pushad
863                 pushfd
864                 mov cx,8
865                 ror eax,28
866 writehex_common:
867 .loop:          push cx
868                 push eax
869                 and al,0Fh
870                 cmp al,10
871                 jb .isdec
872                 add al,'a'-'0'-10
873 .isdec:         add al,'0'
874                 mov ah,0Eh
875                 mov bx,7
876                 int 10h
877                 pop eax
878                 rol eax,4
879                 pop cx
880                 loop .loop
881                 popfd
882                 popad
883                 ret
884 %endif
885
886                 section .data
887                 alignb 2
888 Int13Funcs      dw Reset                ; 00h - RESET
889                 dw GetStatus            ; 01h - GET STATUS
890                 dw Read                 ; 02h - READ
891                 dw Write                ; 03h - WRITE
892                 dw Verify               ; 04h - VERIFY
893                 dw Invalid              ; 05h - FORMAT TRACK
894                 dw Invalid              ; 06h - FORMAT TRACK AND SET BAD FLAGS
895                 dw Invalid              ; 07h - FORMAT DRIVE AT TRACK
896                 dw GetParms             ; 08h - GET PARAMETERS
897                 dw InitWithParms        ; 09h - INITIALIZE CONTROLLER WITH
898                                         ;       DRIVE PARAMETERS
899                 dw Invalid              ; 0Ah
900                 dw Invalid              ; 0Bh
901                 dw Seek                 ; 0Ch - SEEK TO CYLINDER
902                 dw Reset                ; 0Dh - RESET HARD DISKS
903                 dw Invalid              ; 0Eh
904                 dw Invalid              ; 0Fh
905                 dw CheckIfReady         ; 10h - CHECK IF READY
906                 dw Recalibrate          ; 11h - RECALIBRATE
907                 dw Invalid              ; 12h
908                 dw Invalid              ; 13h
909                 dw Invalid              ; 14h
910                 dw GetDriveType         ; 15h - GET DRIVE TYPE
911                 dw DetectChange         ; 16h - DETECT DRIVE CHANGE
912 %if EDD
913                 dw Invalid              ; 17h
914                 dw Invalid              ; 18h
915                 dw Invalid              ; 19h
916                 dw Invalid              ; 1Ah
917                 dw Invalid              ; 1Bh
918                 dw Invalid              ; 1Ch
919                 dw Invalid              ; 1Dh
920                 dw Invalid              ; 1Eh
921                 dw Invalid              ; 1Fh
922                 dw Invalid              ; 20h
923                 dw ReadMult             ; 21h - READ MULTIPLE
924                 dw WriteMult            ; 22h - WRITE MULTIPLE
925                 dw SetMode              ; 23h - SET CONTROLLER FEATURES
926                 dw SetMode              ; 24h - SET MULTIPLE MODE
927                 dw Invalid              ; 25h - IDENTIFY DRIVE
928                 dw Invalid              ; 26h
929                 dw Invalid              ; 27h
930                 dw Invalid              ; 28h
931                 dw Invalid              ; 29h
932                 dw Invalid              ; 2Ah
933                 dw Invalid              ; 2Bh
934                 dw Invalid              ; 2Ch
935                 dw Invalid              ; 2Dh
936                 dw Invalid              ; 2Eh
937                 dw Invalid              ; 2Fh
938                 dw Invalid              ; 30h
939                 dw Invalid              ; 31h
940                 dw Invalid              ; 32h
941                 dw Invalid              ; 33h
942                 dw Invalid              ; 34h
943                 dw Invalid              ; 35h
944                 dw Invalid              ; 36h
945                 dw Invalid              ; 37h
946                 dw Invalid              ; 38h
947                 dw Invalid              ; 39h
948                 dw Invalid              ; 3Ah
949                 dw Invalid              ; 3Bh
950                 dw Invalid              ; 3Ch
951                 dw Invalid              ; 3Dh
952                 dw Invalid              ; 3Eh
953                 dw Invalid              ; 3Fh
954                 dw Invalid              ; 40h
955                 dw EDDPresence          ; 41h - EDD PRESENCE DETECT
956                 dw EDDRead              ; 42h - EDD READ
957                 dw EDDWrite             ; 43h - EDD WRITE
958                 dw EDDVerify            ; 44h - EDD VERIFY
959                 dw EDDLock              ; 45h - EDD LOCK/UNLOCK MEDIA
960                 dw EDDEject             ; 46h - EDD EJECT
961                 dw EDDSeek              ; 47h - EDD SEEK
962                 dw EDDGetParms          ; 48h - EDD GET PARAMETERS
963                 dw EDDDetectChange      ; 49h - EDD MEDIA CHANGE STATUS
964 %endif
965
966 Int13FuncsEnd   equ $
967 Int13FuncsCnt   equ (Int13FuncsEnd-Int13Funcs) >> 1
968
969
970                 alignb 8, db 0
971 Shaker          dw ShakerEnd-$-1        ; Descriptor table limit
972                 dd 0                    ; Pointer to self
973                 dw 0
974
975 Shaker_RMDS:    dd 0x0000ffff           ; 64K data segment
976                 dd 0x00009300
977
978 Shaker_DS:      dd 0x0000ffff           ; 4GB data segment
979                 dd 0x008f9300
980
981 ShakerEnd       equ $
982
983                 alignb 8, db 0
984
985 Mover           dd 0, 0, 0, 0           ; Must be zero
986                 dw 0ffffh               ; 64 K segment size
987 Mover_src1:     db 0, 0, 0              ; Low 24 bits of source addy
988                 db 93h                  ; Access rights
989                 db 00h                  ; Extended access rights
990 Mover_src2:     db 0                    ; High 8 bits of source addy
991                 dw 0ffffh               ; 64 K segment size
992 Mover_dst1:     db 0, 0, 0              ; Low 24 bits of target addy
993                 db 93h                  ; Access rights
994                 db 00h                  ; Extended access rights
995 Mover_dst2:     db 0                    ; High 8 bits of source addy
996 Mover_dummy2:   dd 0, 0, 0, 0           ; More space for the BIOS
997
998                 alignb 4, db 0
999 MemDisk_Info    equ $                   ; Pointed to by installation check
1000 MDI_Bytes       dw MDI_Len              ; Total bytes in MDI structure
1001 MDI_Version     db VERSION_MINOR, VERSION_MAJOR ; MEMDISK version
1002
1003 PatchArea       equ $                   ; This gets filled in by the installer
1004
1005 DiskBuf         dd 0                    ; Linear address of high memory disk
1006 DiskSize        dd 0                    ; Size of disk in blocks
1007 CommandLine     dw 0, 0                 ; Far pointer to saved command line
1008
1009 OldInt13        dd 0                    ; INT 13h in chain
1010 OldInt15        dd 0                    ; INT 15h in chain
1011
1012 OldDosMem       dw 0                    ; Old position of DOS mem end
1013 BootLoaderID    db 0                    ; Boot loader ID from header
1014                 db 0                    ; pad
1015
1016 DPT_ptr         dw 0                    ; If nonzero, pointer to DPT
1017                                         ; Original DPT pointer follows
1018
1019 MDI_Len         equ $-MemDisk_Info
1020
1021 ; ---- MDI structure ends here ---
1022 DriveShiftLimit db 0ffh                 ; Installer will [soon] probe for
1023                                         ; a range of contiguous drives.
1024                                         ; Any BIOS drives above this region
1025                                         ; shall not be impacted by our
1026                                         ; shifting behaviour
1027                 db 0                    ; pad to a DWORD
1028                 dw 0                    ; pad to a QWORD
1029 MemInt1588      dw 0                    ; 1MB-65MB memory amount (1K)
1030
1031 Cylinders       dw 0                    ; Cylinder count
1032 Heads           dw 0                    ; Head count
1033 Sectors         dd 0                    ; Sector count (zero-extended)
1034
1035 Mem1MB          dd 0                    ; 1MB-16MB memory amount (1K)
1036 Mem16MB         dd 0                    ; 16MB-4G memory amount (64K)
1037
1038 DriveNo         db 0                    ; Our drive number
1039 DriveType       db 0                    ; Our drive type (floppies)
1040 DriveCnt        db 0                    ; Drive count (from the BIOS)
1041
1042 ConfigFlags     db 0                    ; Bit 0 - readonly
1043
1044 MyStack         dw 0                    ; Offset of stack
1045 StatusPtr       dw 0                    ; Where to save status (zeroseg ptr)
1046
1047 DPT             times 16 db 0           ; BIOS parameter table pointer (floppies)
1048 OldInt1E        dd 0                    ; Previous INT 1E pointer (DPT)
1049
1050 %if EDD
1051 EDD_DPT:
1052 .length         dw 30
1053 .info           dw 0029h
1054                 ; Bit 0 - DMA boundaries handled transparently
1055                 ; Bit 3 - Device supports write verify
1056                 ; Bit 5 - Media is lockable
1057 .cylinders      dd 0                    ; Filled in by installer
1058 .heads          dd 0                    ; Filled in by installer
1059 .sectors        dd 0                    ; Filled in by installer
1060 .totalsize      dd 0, 0                 ; Filled in by installer
1061 .bytespersec    dw SECTORSIZE
1062 .eddtable       dw -1, -1               ; Invalid DPTE pointer
1063 .dpikey         dw 0BEDDh               ; Device Path Info magic
1064 .dpilen         db 2ch                  ; DPI len
1065 .res1           db 0                    ; Reserved
1066 .res2           dw 0                    ; Reserved
1067 .bustype        dd 'MEM '               ; Host bus type (4 bytes, space padded)
1068 .inttype        dd 'MEMORY  '           ; Interface type (8 bytes, spc. padded)
1069 .intpath        dd 0, 0                 ; Interface path
1070 .devpath        dd 0, 0, 0, 0           ; Device path
1071 .res3           db 0                    ; Reserved
1072 .chksum         db 0                    ; DPI checksum
1073
1074 %endif
1075
1076                 ; End patch area
1077                 alignb 4, db 0
1078 Stack           dd 0                    ; Saved SS:ESP on invocation
1079                 dw 0
1080 SavedAX         dw 0                    ; AX saved on invocation
1081 Recursive       dw 0                    ; Recursion counter
1082
1083                 alignb 4, db 0          ; We *MUST* end on a dword boundary
1084
1085 E820Table       equ $                   ; The installer loads the E820 table here
1086 TotalSize       equ $                   ; End pointer