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