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