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