├── Shsurdrv ├── Readme.txt └── shsurdrv.zip ├── Test ├── copyfile.zip ├── Readme.txt ├── Makefile ├── timerms.inc ├── printf.inc └── copyfile.asm ├── RDiskSX ├── RDiskSX.zip └── RDiskSX.txt ├── Make.bat ├── Makefile ├── Smartdrv └── Readme.txt ├── History.txt ├── Readme.txt ├── XMS35.txt └── HimemSX.asm /Shsurdrv/Readme.txt: -------------------------------------------------------------------------------- 1 | 2 | adjusted shsurdrv utility that uses super-extended memory. 3 | 4 | -------------------------------------------------------------------------------- /Test/copyfile.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Baron-von-Riedesel/HimemSX/HEAD/Test/copyfile.zip -------------------------------------------------------------------------------- /RDiskSX/RDiskSX.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Baron-von-Riedesel/HimemSX/HEAD/RDiskSX/RDiskSX.zip -------------------------------------------------------------------------------- /Shsurdrv/shsurdrv.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Baron-von-Riedesel/HimemSX/HEAD/Shsurdrv/shsurdrv.zip -------------------------------------------------------------------------------- /Test/Readme.txt: -------------------------------------------------------------------------------- 1 | 2 | CopyFile is a simple utility to test the speed of file copies. 3 | It displays the times required to read and write the file, in ms resolution. 4 | -------------------------------------------------------------------------------- /Make.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | rem JWasm used to create HimemSX.exe 3 | if not exist "Release\NUL" mkdir Release 4 | jwasm.exe -nologo -mz -Sg -D?ALTSTRAT=1 -Fl=Release\HimemSX.LST -Fo=Release\HimemSX.exe HimemSX.asm 5 | -------------------------------------------------------------------------------- /Test/Makefile: -------------------------------------------------------------------------------- 1 | 2 | name = copyfile 3 | 4 | $(name).exe: $*.obj 5 | @jwlink format dos file \hx\LibOMF\jmppm32.obj,$*.obj name $*.EXE lib \hx\LibOMF\jmppm32.lib op map=$*.MAP,quiet 6 | 7 | $(name).obj: $(name).asm 8 | @jwasm -nologo -Fl$* -Fo$* $(name).asm 9 | 10 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # HIMEMSX.EXE is build with JWasm. 3 | # 4 | 5 | !ifndef DEBUG 6 | DEBUG=0 7 | !endif 8 | 9 | NAME=HimemSX 10 | !if $(DEBUG) 11 | OUTD=Debug 12 | OPTD=-D_DEBUG 13 | !else 14 | OUTD=Release 15 | OPTD= 16 | !endif 17 | 18 | ALL: $(OUTD) $(OUTD)\$(NAME).exe 19 | 20 | $(OUTD): 21 | @mkdir $(OUTD) 22 | 23 | $(OUTD)\$(NAME).exe: $(NAME).asm Makefile 24 | @jwasm.exe -mz -nologo $(OPTD) -D?ALTSTRAT=1 -Sg -Fl$*.lst -Fo$*.exe $(NAME).asm 25 | 26 | clean: 27 | @erase $(OUTD)\*.exe 28 | -------------------------------------------------------------------------------- /RDiskSX/RDiskSX.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | About 4 | 5 | RDiskSX.COM is a patched version of Jack R. Ellis's RDISK ramdisk driver. 6 | 7 | 8 | How rdisk.asm is to be patched 9 | 10 | 1. XMS function call ah=89h has to be changed to 0C9h 11 | 2. the XMS lock call following the allocation has to be disabled 12 | 3. the XMS unlock call has to be disabled. 13 | 14 | The lock/unlock function pair in RDISK isn't really needed, 15 | because RDISK does not use the physical address returned by the lock. 16 | 17 | Patch 1: 18 | 19 | ; mov ah,089h ;Use V3.0 XMS "allocate memory" code. 20 | mov ah,0C9h ;Use HimemSX "allocate super-extended memory" code. 21 | I_GetX: 22 | call I_XMS ;Request all necessary XMS memory. 23 | jnz I_XmsE ;Error? Display message and quit! 24 | 25 | Patch 2: 26 | 27 | ; mov ah,00Ch ;"Lock" our XMS memory. 28 | ; call I_XMS 29 | ; jnz I_XmsE ;Error? Display msg. & quit! 30 | 31 | Patch 3: 32 | 33 | mov ah,00Dh ;Unlock and "free" our XMS memory. 34 | push dx 35 | ; call I_XMS 36 | mov ah,00Ah 37 | pop dx 38 | 39 | -------------------------------------------------------------------------------- /Smartdrv/Readme.txt: -------------------------------------------------------------------------------- 1 | 2 | Patch for Smartdrv v5.02 ( MS-DOS 7.1, supplied with Windows 98 ) 3 | 4 | This patch makes smartdrv use a super-extended memory block instead of a 5 | standard EMB. 6 | 7 | 8 | Benefits 9 | 10 | 1. memory beyond the 4 GB border is more difficult to access. This makes it 11 | "harder" for protected-mode applications to inadvertently modify extended 12 | memory that is owned by smartdrv. 13 | 14 | 2. The DOS/16M and DOS/4G extender (rational, tenberry) may cause a system 15 | crash if the address of its extended memory block is beyond the 16MB 16 | barrier. This will most likely happen if the unmodified smartdrv is 17 | loaded with a huge cache size (max is 32 MB). The patched version will 18 | avoid this. 19 | 20 | Be aware that the modified smartdrv won't work anymore with a XMM 3.0 host, 21 | so it's a good idea to NOT overwrite the unmodified version. 22 | 23 | 24 | Details 25 | 26 | Modify 7 bytes of file smartdrv.exe (45379 bytes) with a hex editor: 27 | 28 | 072C4: 09 2E FF 1E 72 0E C3 ;old 29 | 072C4: C9 66 0F B7 D2 EB 02 ;new 30 | 31 | The modification changes the code in this way: 32 | 33 | old: 34 | mov ah,09 35 | call far cs:[0E72h] 36 | ret 37 | mov ah,08 38 | call far cs:[0E72h] 39 | ret 40 | 41 | new: 42 | mov ah,0c9h 43 | movzx edx,dx 44 | jmp @F 45 | mov ah,08 46 | @@: 47 | call far cs:[0E72h] 48 | ret 49 | -------------------------------------------------------------------------------- /Test/timerms.inc: -------------------------------------------------------------------------------- 1 | 2 | ;--- gettimer() returns timer in EAX in ms 3 | 4 | _GetTimerValue proc 5 | tryagain: 6 | mov edx,@flat:[046ch] 7 | mov al,0C2h ;read timer 0 status + value low/high 8 | out 43h, al 9 | xchg edx, edx 10 | in al,40h 11 | mov cl,al ;CL = status 12 | xchg edx, edx 13 | in al,40h 14 | mov ah, al ;AH = value low 15 | xchg edx, edx 16 | in al,40h ;AL = value high 17 | test cl,40h ;was latch valid? 18 | jnz tryagain 19 | cmp edx,@flat:[046ch] ;did an interrupt occur in the meantime? 20 | jnz tryagain ;then do it again! 21 | xchg al,ah 22 | 23 | ;--- usually (counter mode 3) the timer is set to count down *twice*! 24 | ;--- however, sometimes counter mode 2 is set! 25 | 26 | mov ch,cl 27 | and ch,0110B ;bit 1+2 relevant 28 | cmp ch,0110B ;counter mode 3? 29 | jnz @F 30 | 31 | ;--- in mode 3, PIN status of OUT0 will become bit 15 32 | 33 | shr ax,1 34 | and cl,80h 35 | or ah, cl 36 | @@: 37 | 38 | ;--- now the counter is in AX (counts from FFFF to 0000) 39 | 40 | neg ax 41 | 42 | ;--- now the count is from 0 to FFFF 43 | 44 | ret 45 | _GetTimerValue endp 46 | 47 | gettimer proc 48 | call _GetTimerValue 49 | 50 | ;--- the timer ticks are in EDX:AX, timer counts down 51 | ;--- a 16bit value with 1,193,180 Hz -> 1193180/65536 = 18.20648 Hz 52 | ;--- which are 54.83 ms 53 | ;--- to convert in ms: 54 | ;--- 1. subticks in ms: AX / 1193 55 | ;--- 2. ticks in ms: EDX * 55 56 | ;--- 3. total 1+2 57 | 58 | push edx 59 | movzx eax,ax ;step 1 60 | cdq 61 | mov ecx, 1193 62 | div ecx 63 | mov ecx, eax 64 | pop eax ;step 2 65 | mov edx, 55 66 | mul edx 67 | add eax, ecx ;step 3 68 | adc edx, 0 69 | ret 70 | align 4 71 | gettimer endp 72 | -------------------------------------------------------------------------------- /History.txt: -------------------------------------------------------------------------------- 1 | 2 | History 3 | 4 | 12.04.2022: v3.54 5 | - reallocating super-extended memory blocks did loose data if 6 | the block's old size was > 4 GB and had to be moved ( that is, 7 | the block's size was increased and there was not enough free space 8 | "behind" the block ). 9 | 09.04.2022: v3.53 10 | - implements interface v3.51. This adds a new block move function, 11 | selected with AH=0CBh, to the XMM. This new function allows to 12 | set 40-bit offsets for the source or destination addresses. 13 | Done by Jason Hood. 14 | 17.12.2020: v3.52 15 | - set RW page flag for page 0, else, if CR0.WP is set, a reboot occurs. 16 | 11.12.2020: v3.51 17 | - xms block move function speed-up. 18 | - optionally place page dir at the end of first memory block. 19 | - return DX=0 if emb allocation fails (XMS spec). 20 | - debug version: exceptions in protected-mode caught. 21 | - bugfix: printf() did assume SS==DS. 22 | - restore rm CS after sx pmode (needed by some machines, but why?) 23 | 29.10.2020: v3.50 24 | - fixed: if a memory block beyond 4 GB was reallocated and had to be 25 | "moved" (because its size increased and there was no free space "behind" 26 | it), its new location was below the 4 GB barrier. 27 | 27.10.2020: 28 | - fixed: block move with length > 2 GB failed due to signed comparison. 29 | 26.10.2020: 30 | - fixed: cpuid check for PSE-36 used bit 16 instead of bit 17. 31 | - cleaned source (use prototypes & invoke) 32 | - added option /SUPERMAX=xxx. 33 | - removed option /NOX2MAX32. 34 | - block move function in V86 mode supports memory beyond 4GB 35 | if the EMM is Jemm386 v5.80+. 36 | 25.10.2020: 37 | - block move overlap check works for entire 40-bit address space. 38 | 25.10.2020: 39 | - ensured that no memory beyond the 1 TB barrier is used. 40 | 23.10.2020: 41 | - fixed: block move crossing a 4 GB barrier may have lost data. 42 | 21.10.2020: 43 | - initial version. 44 | 45 | -------------------------------------------------------------------------------- /Readme.txt: -------------------------------------------------------------------------------- 1 | 2 | 1. About 3 | 4 | HimemSX is a fork of HimemX. Its main feature is that it's able to manage 5 | more than 4 GB of memory. The memory beyond the 4 GB limit is called 6 | "super-extended" in this document. 7 | 8 | 9 | 2. Technical Details 10 | 11 | To access extended memory below the 4 GB barrier HimemSX uses the so-called 12 | "unreal" mode, like most other XMMs. 13 | 14 | Memory beyond the 4 GB barrier can only be accessed thru special paging 15 | mechanisms. HimemSX uses the PSE-36 variant. PSE stands for Page Size 16 | Extension, the 36 refers to the original 36 bit address extension (64 GB), 17 | which was later extended to 40 bit (1 TB). 18 | 19 | The XMS API has to be extended. See XMS35.txt for details. 20 | 21 | In v86 mode, the XMM needs support from the v86-monitor program to access 22 | extended memory, since it cannot run the privileged code required for the 23 | memory access. Currently only Jemm386 provides this support. 24 | 25 | 26 | 3. Restrictions 27 | 28 | - The maximum amount of memory that the XMS API can handle is 4 TB (42 29 | physical address lines). However, since HimemSX currently uses 32-bit 30 | paging with PSE-36 inside its block-move function, the effective limit 31 | is 1 TB (40 address lines). 32 | - The 'move extended memory' function 0Bh understands 32-bit offsets only. 33 | So if a memory block is larger than 4 GB, you can't use this function to 34 | copy memory beyond a 4 GB offset. 35 | Since version 3.53, there's an additional block move function available, 36 | that overcomes this limitation. See XMS35.txt for details. 37 | - if no super-extended memory is found, or the CPU doesn't support PSE-36 38 | paging, HimemSX will still load and behave like a v3 XMM. However, it 39 | searches for extended memory via Int 15h, ax=e820h only, without trying to 40 | fall back to older detection strategies if this call fails. 41 | 42 | 43 | 4. License 44 | 45 | Since HimemSX is a fork of HimemX and HimemX is derived from FD Himem, the 46 | FD Himem copyrights do apply. FD Himem is copyright Till Gerken and Tom 47 | Ehlert, with GPL and/or Artistic license. 48 | 49 | Japheth 50 | -------------------------------------------------------------------------------- /XMS35.txt: -------------------------------------------------------------------------------- 1 | 2 | 1. XMS v3.5 API 3 | 4 | XMS v3.5 has been created to allow accessing extended memory beyond the 4 GB 5 | barrier. 6 | 7 | To achieve this, the XMS v3.0 API has been extended: 8 | 9 | AH=0C8h: query free super-extended memory. Returns in EAX largest free block 10 | in kB, in EDX the total amount (in kB) of free super-extended memory. 11 | AX=0 indicates an error. 12 | 13 | AH=0C9h: allocate block of super-extended memory. Expects in EDX the 14 | requested amount of memory in kB. Returns AX=0 if an error occured, 15 | else AX is 1 and the handle of the block is in DX. 16 | 17 | AH=0CCh: lock a (super-extended) memory block. Expects handle in DX. Returns 18 | 64-bit physical address of locked block in EDX:EBX. Returns AX=0 19 | if an error occured. 20 | 21 | XMS function 00 (Get Version) will return ax=0350h, that is version 3.50. 22 | 23 | 24 | 2. XMS v3.51 API 25 | 26 | XMS v3.51 extends the v3.50 API by another function: 27 | 28 | AH=0CBh: super-extended block move function. Register setup is like XMS 29 | function 0Bh. However, the structure that holds source and 30 | destination of the memory block to copy has been extended by two 31 | 1-byte fields that must be filled with bits 32-39 of the source 32 | and destination address. 33 | 34 | sxms_move struct 35 | len dd ? ; +0: block length in bytes 36 | src_handle dw ? ; +4: source handle 37 | src_offset dd ? ; +6: offset into source 38 | dst_handle dw ? ; +10: destination handle 39 | dst_offset dd ? ; +12: offset into destination 40 | src_high db ? ; +16: NEW: bits 32-39 of source offset 41 | dest_high db ? ; +17: NEW: bits 32-39 of destination offset 42 | sxms_move ends 43 | 44 | 45 | 3. BIOS Interrupt 15h, AX=E820h 46 | 47 | Since the memory beyond the 4 GB limit must be managed exclusively, 48 | Int 15h, ax=E820h should be intercepted in a way that all memory blocks with 49 | addresses >= 100000000h are changed from "available" to "reserved". 50 | 51 | 52 | 4. BIOS Interrupt 15h, AH=87h 53 | 54 | In V86 mode, the XMM's 'move extended memory' functions (AH=0Bh & AH=0CBh) 55 | will need the help of the Expanded Memory Manager (EMM), since privileged 56 | code has to be executed. The only EMMs that currently support accessing 57 | memory beyond 4 GB are Jemm386/JemmEx v5.80+. Their Int 15h API has been 58 | exhanced as well. 59 | 60 | Register setup for Int 15h, AH=87h: 61 | 62 | - AH: 87h 63 | - EAX[bits 16-31]: F00Fh 64 | - CX: F00Fh 65 | - ECX[bits 16-31]: size of block in words 66 | - DS:SI: same as the standard ( pointing to a GDT ), descriptors 2 & 3 67 | defining address bits 0-31 of source/destination region. 68 | - DX: address bits 32-47 of the source region. 69 | - BX: address bits 32-47 of the destination region. 70 | 71 | If the call succeeded, the carry flag is cleared and register AH is 0. 72 | If an error occured ( for example, CPU doesn't support PSE ), the carry 73 | flag is set and AH is != 0. 74 | 75 | Japheth 76 | -------------------------------------------------------------------------------- /Test/printf.inc: -------------------------------------------------------------------------------- 1 | 2 | ;--- simple printf implementation for DOS 32-bit 3 | ;--- supports: 4 | ;--- %x : word (dword pushed) 5 | ;--- %lx : dword 6 | ;--- %u : word (dword pushed) 7 | ;--- %lu : dword 8 | ;--- %s : near32 string out 9 | ;--- %c : character (dword pushed) 10 | 11 | 12 | .386 13 | 14 | strlen proc c uses edi string:ptr sbyte 15 | mov ecx,-1 16 | mov edi,string 17 | mov al,0 18 | cld 19 | repnz scasb 20 | mov eax,ecx 21 | inc eax 22 | not eax 23 | ret 24 | strlen endp 25 | 26 | ;--- convert long to string - always base 16 27 | ;--- stdcall ltoa( long n, char * s, base n ); 28 | 29 | ltoa PROC c uses ebx esi number:dword, tbuffer:ptr byte, base:dword 30 | 31 | mov ecx, tbuffer 32 | mov esi, number 33 | nextitem: 34 | mov eax,esi 35 | xor edx,edx 36 | div base 37 | mov eax,esi 38 | mov ebx,edx 39 | xor edx,edx 40 | div base 41 | cmp ebx,9 42 | mov esi,eax 43 | jbe @F 44 | add bl,57h ;convert to 'a'-'f' 45 | jmp xtoa_2 46 | @@: 47 | add bl,'0' 48 | xtoa_2: 49 | mov byte ptr [ecx],bl 50 | inc ecx 51 | test esi,esi 52 | ja nextitem 53 | and byte ptr [ecx],00 54 | mov ebx, ecx 55 | sub ebx, tbuffer 56 | mov esi, tbuffer 57 | .while esi < ecx 58 | dec ecx 59 | mov al,[esi] 60 | xchg al,[ecx] 61 | mov [esi],al 62 | inc esi 63 | .endw 64 | mov eax,ebx 65 | ret 66 | 67 | ltoa ENDP 68 | 69 | printf PROC c uses ebx esi edi fmt:ptr sbyte, args:VARARG 70 | 71 | local flag:byte 72 | local longarg:byte 73 | local size_:dword 74 | local fillchr:dword 75 | local base:dword 76 | local szTmp[12]:byte 77 | 78 | lea edi,args 79 | @@L335: 80 | mov esi,fmt 81 | nextchar: 82 | lodsb 83 | or al,al 84 | je done 85 | cmp al,'%' 86 | je formatitem 87 | push eax 88 | call handle_char 89 | jmp nextchar 90 | done: 91 | xor eax,eax 92 | ret 93 | 94 | formatitem: 95 | push offset @@L335 96 | xor edx,edx 97 | mov [longarg],dl 98 | mov bl,1 99 | mov cl,' ' 100 | cmp BYTE PTR [esi],'-' 101 | jne @F 102 | dec bl 103 | inc esi 104 | @@: 105 | mov [flag],bl 106 | cmp BYTE PTR [esi],'0' 107 | jne @F 108 | mov cl,'0' 109 | inc esi 110 | @@: 111 | mov [fillchr],ecx 112 | mov [size_],edx 113 | mov ebx,edx 114 | 115 | .while ( byte ptr [esi] >= '0' && byte ptr [esi] <= '9' ) 116 | lodsb 117 | sub al,'0' 118 | movzx eax,al 119 | imul ecx,ebx,10 ;ecx = ebx * 10 120 | add eax,ecx 121 | mov ebx,eax 122 | .endw 123 | 124 | mov [size_],ebx 125 | cmp BYTE PTR [esi],'l' 126 | jne @F 127 | mov [longarg],1 128 | inc esi 129 | @@: 130 | lodsb 131 | mov [fmt],esi 132 | or al,al 133 | je done 134 | cmp al,'x' 135 | je handle_x 136 | cmp al,'X' 137 | je handle_x 138 | cmp al,'u' 139 | je handle_u 140 | cmp al,'s' 141 | je handle_s 142 | handle_c: 143 | push DWORD PTR ss:[edi] 144 | add edi, 4 145 | call handle_char 146 | retn 147 | 148 | handle_s: 149 | mov esi,ss:[edi] 150 | add edi,4 151 | push esi 152 | call strlen 153 | pop ebx 154 | jmp print_string 155 | handle_u: 156 | mov base, 10 157 | jmp @F 158 | handle_x: 159 | mov base, 16 160 | @@: 161 | mov eax,ss:[edi] 162 | add edi,4 163 | cmp [longarg],0 164 | jne @F 165 | movzx eax,ax ;unsigned short 166 | @@: 167 | lea esi, szTmp 168 | invoke ltoa, eax, esi, base 169 | print_string: ;print string ESI 170 | sub [size_],eax 171 | cmp [flag],1 172 | jne print_string_chars 173 | mov ebx,[size_] 174 | jmp @@L363 175 | @@F270: 176 | push [fillchr] 177 | call handle_char ;print leading filler chars 178 | dec ebx 179 | @@L363: 180 | or ebx,ebx 181 | jg @@F270 182 | mov [size_],ebx 183 | 184 | print_string_chars: 185 | 186 | .while (byte ptr [esi]) 187 | lodsb 188 | push eax 189 | call handle_char ;print char of string 190 | .endw 191 | 192 | mov ebx,[size_] 193 | @@: 194 | or ebx,ebx 195 | jle @F 196 | push [fillchr] 197 | call handle_char ;print trailing spaces 198 | dec ebx 199 | jmp @B 200 | @@: 201 | retn 202 | 203 | handle_char: 204 | pop ecx 205 | pop edx 206 | cmp dl,10 207 | jnz @F 208 | mov dl,13 209 | mov ah,2 210 | int 21h 211 | mov dl,10 212 | @@: 213 | mov ah,2 214 | int 21h 215 | jmp ecx 216 | 217 | align 4 218 | 219 | printf ENDP 220 | 221 | 222 | -------------------------------------------------------------------------------- /Test/copyfile.asm: -------------------------------------------------------------------------------- 1 | 2 | ;*** copy a file 3 | 4 | .386 5 | .MODEL SMALL 6 | option proc:private 7 | 8 | ?TRAPI13 equ 0 9 | 10 | CStr macro text:vararg 11 | local sym 12 | .const 13 | sym db text,0 14 | .code 15 | exitm 16 | endm 17 | 18 | public __pMoveHigh ;avoid being moved in extended memory 19 | @flat equ 20 | 21 | if ?TRAPI13 22 | _TEXT16 segment use16 para public 'CODE' 23 | 24 | dwOldInt13rm dd 0 25 | dwReads dd 0 26 | dwRdSecs dd 0 27 | dwWrites dd 0 28 | dwWrSecs dd 0 29 | 30 | myint13rm: 31 | cmp ah,02h 32 | jz readcnt1 33 | cmp ah,42h 34 | jz readcnt2 35 | cmp ah,03h 36 | jz writecnt1 37 | cmp ah,43h 38 | jz writecnt2 39 | jmp dword ptr cs:[dwOldInt13rm] 40 | readcnt1: 41 | inc dword ptr cs:[dwReads] 42 | push eax 43 | movzx eax,al 44 | add cs:[dwRdSecs],eax 45 | pop eax 46 | jmp dword ptr cs:[dwOldInt13rm] 47 | readcnt2: 48 | inc dword ptr cs:[dwReads] 49 | push eax 50 | movzx eax,word ptr [si+2] 51 | add cs:[dwRdSecs],eax 52 | pop eax 53 | jmp dword ptr cs:[dwOldInt13rm] 54 | writecnt1: 55 | inc dword ptr cs:[dwWrites] 56 | push eax 57 | movzx eax,al 58 | add cs:[dwWrSecs],eax 59 | pop eax 60 | jmp dword ptr cs:[dwOldInt13rm] 61 | writecnt2: 62 | inc dword ptr cs:[dwWrites] 63 | push eax 64 | movzx eax,word ptr [si+2] 65 | add cs:[dwWrSecs],eax 66 | pop eax 67 | jmp dword ptr cs:[dwOldInt13rm] 68 | 69 | _TEXT16 ends 70 | endif 71 | 72 | .DATA 73 | 74 | __pMoveHigh dd 0 75 | 76 | .CODE 77 | 78 | include printf.inc 79 | include timerms.inc 80 | 81 | main proc c 82 | 83 | local hFileSrc:DWORD 84 | local hFileDst:DWORD 85 | local dwSize:DWORD 86 | local pMem:DWORD 87 | local time1:DWORD 88 | local time2:DWORD 89 | local bInt13:BYTE 90 | local szSrc[128]:byte 91 | local szDst[128]:byte 92 | 93 | mov bInt13,0 94 | 95 | mov ah,51h 96 | int 21h 97 | mov esi,80h 98 | push ds 99 | mov ds,ebx 100 | lodsb 101 | mov cl,al 102 | .while (cl) 103 | mov al,[esi] 104 | .break .if ((al != 20h) && (al != 9)) 105 | inc esi 106 | dec cl 107 | .endw 108 | lea edi,szSrc 109 | .while (cl) 110 | lodsb 111 | dec cl 112 | .break .if ((al == 20h) || (al == 9)) 113 | stosb 114 | .endw 115 | mov al,0 116 | stosb 117 | .while (cl) 118 | mov al,[esi] 119 | .break .if ((al != 20h) && (al != 9)) 120 | inc esi 121 | dec cl 122 | .endw 123 | lea edi,szDst 124 | .while (cl) 125 | lodsb 126 | dec cl 127 | .break .if ((al == 20h) || (al == 9)) 128 | stosb 129 | .endw 130 | mov al,0 131 | stosb 132 | pop ds 133 | cmp szDst, 0 134 | jz usageerr 135 | 136 | if ?TRAPI13 137 | ;--- get real-mode int 13h 138 | mov bl,13h 139 | mov ax,0200h 140 | int 31h 141 | push cx 142 | push dx 143 | pop esi 144 | 145 | ;--- allocate 1 selector 146 | mov cx,1 147 | mov ax,0 148 | int 31h 149 | mov fs,eax 150 | mov ebx,eax 151 | 152 | mov ax,seg _TEXT16 153 | movzx eax,ax 154 | shl eax,4 155 | push eax 156 | pop dx 157 | pop cx 158 | mov ax,7 159 | int 31h 160 | mov dx,0ffh 161 | xor cx,cx 162 | mov ax,8 163 | int 31h 164 | 165 | assume fs:_TEXT16 166 | mov fs:[dwOldInt13rm],esi 167 | 168 | ;--- set real-mode int 13h 169 | mov cx,seg _TEXT16 170 | mov dx,offset myint13rm 171 | mov bl,13h 172 | mov ax,0201h 173 | int 31h 174 | mov bInt13,1 175 | endif 176 | 177 | ;------------------------ open source 178 | 179 | lea esi, szSrc 180 | mov cx,0 ;normal file 181 | mov di,0 182 | mov dl,1h ;fail if file not exists 183 | mov dh,0 184 | mov bx,0 ;read 185 | mov ax,716Ch ;open 186 | int 21h 187 | jnc @F 188 | cmp ax,7100h 189 | jnz openerr1 190 | mov ax,6C00h 191 | int 21h 192 | jc openerr1 193 | @@: 194 | mov hFileSrc, eax 195 | 196 | ;------------------------ open destination 197 | 198 | lea esi, szDst 199 | mov cx,0 ;normal file 200 | mov di,0 201 | mov dl,10h ;fail if file exists 202 | mov dl,11h ;open if file exists, create if file not exists 203 | mov dh,0 204 | mov bx,1 ;write 205 | mov ax,716Ch ;open 206 | int 21h 207 | jnc @F 208 | cmp ax,7100h 209 | jnz openerr2 210 | mov ax,6C00h 211 | int 21h 212 | jc openerr2 213 | @@: 214 | mov hFileDst, eax 215 | 216 | ;------------------------ get file size 217 | 218 | mov ebx,hFileSrc 219 | mov ax,4202h 220 | xor cx,cx 221 | xor dx,dx 222 | int 21h 223 | push dx 224 | push ax 225 | pop eax 226 | mov dwSize, eax 227 | mov ax,4200h 228 | xor cx,cx 229 | xor dx,dx 230 | int 21h 231 | 232 | ;------------------------ display file size 233 | 234 | mov ebx,CStr("Bytes") 235 | mov eax, dwSize 236 | cdq 237 | cmp eax, 1024*8 238 | jb @F 239 | mov ebx,CStr("kB") 240 | mov ecx, 1024 241 | div ecx 242 | @@: 243 | invoke printf, CStr(<"file size: %lu %s",10>), eax, ebx 244 | 245 | ;------------------------ alloc buffer 246 | 247 | push dwSize 248 | pop cx 249 | pop bx 250 | mov ax,501h 251 | int 31h 252 | jc memerr 253 | push bx 254 | push cx 255 | pop eax 256 | mov pMem, eax 257 | 258 | ;------------------------ get start time 259 | call gettimer 260 | mov time1, eax 261 | 262 | ;------------------------ read file into buffer 263 | 264 | mov ebx, hFileSrc 265 | mov ecx, dwSize 266 | mov edx, pMem 267 | push ds 268 | push @flat 269 | pop ds 270 | mov ah,3Fh 271 | int 21h 272 | pop ds 273 | jc readerr 274 | cmp eax, dwSize 275 | jnz readerr 276 | 277 | ;------------------------ close file 278 | mov ebx, hFileSrc 279 | mov ah,3Eh 280 | int 21h 281 | 282 | ;------------------------ display read time 283 | call gettimer 284 | mov time2, eax 285 | sub eax, time1 286 | invoke printf, CStr(<"time for read: %lu ms",10>), eax 287 | 288 | ;------------------------ write file from buffer 289 | mov ebx, hFileDst 290 | mov ecx, dwSize 291 | mov edx, pMem 292 | push ds 293 | push @flat 294 | pop ds 295 | mov ah,40h 296 | int 21h 297 | pop ds 298 | jc writeerr1 299 | cmp eax, dwSize 300 | jnz writeerr2 301 | 302 | ;------------------------ close file 303 | mov ebx, hFileDst 304 | mov ah,3Eh 305 | int 21h 306 | 307 | ;------------------------ display write time 308 | call gettimer 309 | sub eax, time2 310 | invoke printf, CStr(<"time for write: %lu ms",10>), eax 311 | 312 | 313 | if ?TRAPI13 314 | mov ecx,fs:[dwReads] 315 | mov edx,fs:[dwRdSecs] 316 | invoke printf, CStr(<" int 13h reads: %lu, sectors: %lu",10>), ecx, edx 317 | 318 | mov ecx,fs:[dwWrites] 319 | mov edx,fs:[dwWrSecs] 320 | invoke printf, CStr(<" int 13h writes: %lu, sectors: %lu",10>), ecx, edx 321 | endif 322 | 323 | jmp @exit 324 | 325 | openerr1: 326 | movzx eax,ax 327 | invoke printf, CStr(<"source file open error %X",10>), eax 328 | jmp @exit 329 | openerr2: 330 | movzx eax,ax 331 | invoke printf, CStr(<"destination file open error %X",10>), eax 332 | jmp @exit 333 | readerr: 334 | ; movzx eax,ax 335 | invoke printf, CStr(<"read error %lX",10>), eax 336 | jmp @exit 337 | writeerr1: 338 | movzx eax,ax 339 | invoke printf, CStr(<"write error %X",10>), eax 340 | jmp @exit 341 | writeerr2: 342 | invoke printf, CStr(<"could write %lu bytes only",10>), eax 343 | jmp @exit 344 | memerr: 345 | invoke printf, CStr(<"out of memory",10>) 346 | jmp @exit 347 | usageerr: 348 | invoke printf, CStr(<"usage: copyfile src dst",10>) 349 | @exit: 350 | if ?TRAPI13 351 | .if (bInt13) 352 | mov dx,word ptr [dwOldInt13rm+0] 353 | mov cx,word ptr [dwOldInt13rm+2] 354 | mov bl,13h 355 | mov ax,0201h 356 | int 31h 357 | .endif 358 | endif 359 | ret 360 | align 4 361 | 362 | main endp 363 | 364 | mainCRTStartup proc c public 365 | 366 | invoke main 367 | mov ah,4Ch 368 | int 21h 369 | 370 | mainCRTStartup endp 371 | 372 | END 373 | 374 | -------------------------------------------------------------------------------- /HimemSX.asm: -------------------------------------------------------------------------------- 1 | 2 | ;--- HimemSX, XMM able to manage up to 1 TB memory. 3 | 4 | ;--- assembly time parameters 5 | 6 | DRIVER_VER equ 300h+54h 7 | VERSIONSTR equ <'3.54'> 8 | INTERFACE_VER equ 351h 9 | 10 | lf equ 10 11 | 12 | ifndef NUMHANDLES 13 | NUMHANDLES equ 48 ;std 48, default number of handles 14 | endif 15 | MAXHANDLES equ 128 ;std 128, max. number of handles 16 | ALLOWDISABLEA20 equ 1 ;std 1, 1=allow to disable A20 17 | BLOCKSIZE equ 2000h ;std 2000h, block size moved to/from ext. memory (max is 65535!) 18 | USEUNREAL equ 1 ;std 1, 1=use "unreal" mode for EMB copy 19 | PREF66LGDT equ 0 ;std 0, 1=use 66h prefix for LGDT 20 | ?LOG equ 0 ;std 0, 1=enable /LOG option 21 | ?TESTMEM equ 0 ;std 0, 1=enable /TESTMEM:ON|OFF option 22 | ifndef ?ALTSTRAT 23 | ?ALTSTRAT equ 0 ;std 0, 1=use alternate strategy for (re)alloc emb 24 | endif 25 | ?MERGE0HDL equ 1 ;std 1, 1=try to merge even if handle to free has size 0 26 | ?HINF_MSCOMP equ 1 ;std 1, 1=func. 0Eh (get handle info) MS Himem compatible 27 | ?PD110000 equ 0 ;std 0, 1=page dir at 110000h, 0=page dir at end of first i15 block 28 | ?ALLOCDX0 equ 1 ;std 1, 1=return DX=0 if alloc fails 29 | ifdef _DEBUG 30 | ?CATCHEXC equ 1 ;catch exceptions in protected-mode 31 | else 32 | ?CATCHEXC equ 0 33 | endif 34 | ?RESTDSESREGS equ 1 ;std 1, 1=restore DS/ES limits to 64kB after copy in sx pmode 35 | ?RESTCSREG equ 1 ;std ?, 1=restore CS seg after sx pmode - shouldn't be necessary, but apparently is... 36 | 37 | if ?RESTDSESREGS or ?CATCHEXC 38 | ;--- if DS+ES is to be restored to 64kB before leaving protected-mode, 39 | ;--- set page 0-3FFFFFh to RW since GDT is written then [accessed bit] and 40 | ;--- CR0 WP bit may be set! 41 | PF_PAGE0 equ 80h + 2 + 1 ;set 4MB page + RW + Present 42 | else 43 | PF_PAGE0 equ 80h + 1 ;set 4MB page + Present 44 | endif 45 | 46 | ;MAXFREEKB equ 0FBC0h 47 | MAXFREEKB equ 0FFFFh ;std FFFFh, xms v2.0 max ext. memory 48 | 49 | ;--- constants 50 | 51 | CPUID1_PSE36 equ 17 52 | XMS_START equ 1024+64 ; XMS starts at 1088k after HMA 53 | CMD_INIT equ 0 ; init command (used when installed) 54 | 55 | ;--- DOS device driver status codes used 56 | 57 | STATUS_OK equ 0100h ; driver is initialized and ok 58 | STATUS_BAD equ 8103h ; driver couldn't be installed 59 | 60 | VDISK_IDSTR equ "VDISK" 61 | VDISK_IDLEN equ 5 62 | VDISK_IDOFS equ 13h 63 | 64 | ;--- A20 switch methods (must match order in "methods" table) 65 | 66 | A20_KBC equ 0 67 | A20_PS2 equ 1 68 | A20_BIOS equ 2 69 | A20_ALWAYSON equ 3 70 | A20_FAST equ 4 71 | A20_PORT92 equ 5 72 | 73 | ;--- XMS error codes 74 | 75 | XMS_NOT_IMPLEMENTED equ 80h 76 | XMS_VDISK_DETECTED equ 81h 77 | XMS_A20_FAILURE equ 82h 78 | XMS_DRIVER_FAILURE equ 8eh 79 | XMS_DRIVER_FATAL equ 8fh 80 | XMS_HMA_NOT_THERE equ 90h 81 | XMS_HMA_IN_USE equ 91h 82 | XMS_HMAREQ_TOO_SMALL equ 92h 83 | XMS_HMA_NOT_USED equ 93h 84 | XMS_A20_STILL_ENABLED equ 94h 85 | XMS_ALL_MEM_ALLOCATED equ 0a0h 86 | XMS_NO_HANDLE_LEFT equ 0a1h 87 | XMS_INVALID_HANDLE equ 0a2h 88 | XMS_INVALID_SOURCE_HANDLE equ 0a3h 89 | XMS_INVALID_SOURCE_OFFSET equ 0a4h 90 | XMS_INVALID_DESTINATION_HANDLE equ 0a5h 91 | XMS_INVALID_DESTINATION_OFFSET equ 0a6h 92 | XMS_INVALID_LENGTH equ 0a7h 93 | XMS_INVALID_OVERLAP equ 0a8h 94 | XMS_PARITY_ERROR equ 0a9h 95 | XMS_BLOCK_NOT_LOCKED equ 0aah 96 | XMS_BLOCK_LOCKED equ 0abh 97 | XMS_LOCK_COUNT_OVERFLOW equ 0ach 98 | XMS_LOCK_FAILED equ 0adh 99 | XMS_ONLY_SMALLER_UMB equ 0b0h 100 | XMS_NO_UMB_AVAILABLE equ 0b1h 101 | XMS_UMB_SEGMENT_NR_INVALID equ 0b2h 102 | 103 | option proc:private 104 | option casemap:none 105 | 106 | @byte equ 107 | @word equ 108 | @dword equ 109 | 110 | @DbgOutS macro string 111 | if 0;def _DEBUG 112 | call printstring 113 | db string 114 | db 0 115 | endif 116 | endm 117 | 118 | @display macro value 119 | echo value 120 | endm 121 | 122 | ;--- structures 123 | 124 | ;--- cpu GDT descriptor 125 | 126 | desc struct 127 | limit dw ? ; segment limit 128 | base00_15 dw ? ; low word of base address 129 | base16_23 db ? ; high byte of base address 130 | db ? ; 93h = std ring 0 read/write segment 131 | attr db ? ; attributes, limit 16-19 132 | base24_31 db ? 133 | desc ends 134 | 135 | ;--- DOS device driver request header 136 | 137 | request_hdr struct 138 | req_size db ? ; number of bytes stored 139 | unit_id db ? ; unit ID code 140 | cmd db ? ; command code 141 | status dw ? ; status word 142 | rsvd db 8 dup (?) ; reserved 143 | request_hdr ends 144 | 145 | ;--- DOS device driver request for INIT 146 | 147 | init_strc struct 148 | init_hdr db size request_hdr dup (?) 149 | units db ? ; number of supported units 150 | end_addr dd ? ; end address of resident part 151 | cmd_line dd ? ; address of command line 152 | init_strc ends 153 | 154 | ;--- XMS_HANDLE is a documented structure, size 10 bytes 155 | 156 | XMS_HANDLE struct 157 | xh_flags db ? ; see below 158 | xh_locks db ? ; lock count 159 | xh_baseK dd ? ; base address in kbytes 160 | xh_sizeK dd ? ; size in kbytes 161 | XMS_HANDLE ends 162 | 163 | ;--- defined values for XMS_HANDLE.flags 164 | 165 | XMSF_FREE equ 1 ;describes a free block 166 | XMSF_USED equ 2 ;describes a used block 167 | XMSF_INPOOL equ 4 ;describes a free handle 168 | 169 | ;--- XMS handle table (documented structure, size 8 bytes) 170 | 171 | XMS_HANDLETABLE struct 172 | xht_sig DB ? ; identifier byte?, 1 for MS-DOS HIMEM, 0FDH for us 173 | xht_sizeof DB ? ; size of handle descriptor (xms_handle) 174 | xht_numhandles DW ? ; number of handles 175 | xht_pArray DD ? ; pointer to XMS handles array 176 | XMS_HANDLETABLE ends 177 | 178 | ;--- structure for XMS AH=0Bh (documented structure) 179 | 180 | xms_move struct 181 | len dd ? ; block length in bytes 182 | src_handle dw ? ; source handle 183 | src_offset dd ? ; offset into source 184 | dest_handle dw ? ; destination handle 185 | dest_offset dd ? ; offset into destination 186 | xms_move ends 187 | 188 | ;--- structure for XMS AH=0CBh (documented structure) 189 | 190 | sxms_move struct 191 | xms_move 192 | src_hi db ? ; super-extend high byte of source offset 193 | dest_hi db ? ; super-extend high byte of destination offset 194 | sxms_move ends 195 | 196 | ;--- int 15h, ax=E820h (documented) 197 | 198 | SMAP equ 534d4150h 199 | 200 | E820MAP struct 201 | baselow dd ? 202 | basehigh dd ? 203 | lenlow dd ? 204 | lenhigh dd ? 205 | type_ dd ? 206 | E820MAP ends 207 | 208 | IRETS struct 209 | wIP dw ? 210 | wCS dw ? 211 | bFlags db ? 212 | bFlags2 db ? 213 | IRETS ends 214 | 215 | ;--- segment definitions 216 | 217 | .SEQ ;place segments in the order defined here 218 | 219 | _RTEXT segment dword public 'CODE' ; resident code+data 220 | _RTEXT ends 221 | 222 | _TEXT segment word public 'CODE' ; nonresident code 223 | _TEXT ends 224 | 225 | _DATA segment word public 'DATA' ; nonresident data 226 | _DATA ends 227 | 228 | _STACK segment STACK 'STACK' ;1 kB stack 229 | db 1024 dup(?) 230 | _stacktop label byte 231 | _STACK ends 232 | 233 | ;--- use the TINY model 234 | 235 | DGROUP group _RTEXT,_TEXT,_DATA,_STACK 236 | 237 | .386P ; 386 instructions + privileged opcodes 238 | 239 | ;--- nonresident data 240 | 241 | _DATA segment 242 | 243 | ;--- variables 244 | 245 | request_ptr DD 0 ; pointer to request header 246 | xms_max DD 4095*1024; value /MAX= parameter 247 | xms_smax DD 1023*4096*1024; value /SUPERMAX= parameter 248 | dwMaxHigh dd -1 ; mask for upper memory address limit 249 | _xms_num_handles DW NUMHANDLES ;value /NUMHANDLES= parameter 250 | _method DB -1 ; value /METHOD: parameter 251 | _startup_verbose DB 00H ; value /VERBOSE parameter 252 | if ?LOG 253 | _xms_logging_enabled DB 00H ; value /LOG parameter 254 | endif 255 | hma_exists db 0 256 | 257 | ;--- constants 258 | 259 | szStartup DB 'HimemSX v', VERSIONSTR, ' [', @CatStr(!"%@Date!"), ']', lf 260 | db 'Portions (c) Till Gerken & tom ehlert.', lf, 0 261 | if ?TESTMEM 262 | szTESTMEMOFF DB '/TESTMEM:OFF', 0 263 | endif 264 | szVerbose DB '/V', 0 265 | if ?LOG 266 | szLOG DB '/LOG', 0 267 | endif 268 | szINTERFACE DB 'Interface : XMS v3.5 80686 1TB', lf, 0 269 | szNUMHANDLES DB '/NUMHANDLES=', 0 270 | szSelNumHandles DB 'No of XMS handles: %u', lf, 0 271 | szNumHandlesLim1 DB 'HimemSX: NUMHANDLES must be >= 8, corrected', lf, 0 272 | szNumHandlesLim2 DB 'HimemSX: NUMHANDLES must be <= ',@CatStr(!",%MAXHANDLES,!"),', corrected', lf, 0 273 | szX2MAX32 DB '/X2MAX32', 0 274 | szMETHOD DB '/METHOD:', 0 275 | szMAX DB '/MAX=', 0 276 | szSUPERMAX DB '/SUPERMAX=', 0 277 | szMaximum DB 'Maximum extended memory: %luK', lf, 0 278 | szHMAMIN DB '/HMAMIN=', 0 279 | szMinimum DB 'Minimum HMA that has to be requested: %uK', lf, 0 280 | szHMAMAX DB 'HimemSX: HMAMIN must be <= 63, corrected', lf, 0 281 | szPageDir DB 'Page Directory will be at %lX', lf, 0 282 | szIgnored DB 'Ignored commandline <%s>', lf, 0 283 | ;cant_disable_message db 'Can',27h,'t disable A20 - ignored',lf,'$' 284 | 285 | dHimem db 'HimemSX: $' 286 | 287 | ;-- method feedback text 288 | 289 | szKBC db "KBC",'$' 290 | szPS2 db "PS/2",'$' 291 | szFast db "Fast",'$' 292 | szBIOS db "BIOS",'$' 293 | szPort92 db "Port 92",'$' 294 | szA20 db " A20 method used",13,lf,'$' 295 | szAlwaysOn db 'Always on','$' 296 | MsgUnknownA20 db 'No Supported A20 method detected',13,lf,'$' 297 | 298 | old_dos db 'XMS needs at least DOS version 3.00.$' 299 | xms_twice db 'XMS is already installed.$' 300 | a20_error db 'Unable to switch A20 address line.$' 301 | vdisk_detected db 'VDISK has been detected.$' 302 | no_386 db 'At least a 80386 is required.$' 303 | ;xms_sizeerr db 'Unable to determine size of extended memory.$' 304 | xms_toosmall db 'Extended memory is too small or not available.$' 305 | error_msg db ' Driver won''t be installed.',7,13,10,'$' 306 | 307 | methods label byte 308 | db 3,"kbc" ;0 (A20_KBC) 309 | db 3,"ps2" ;1 (A20_PS2) 310 | db 4,"bios" ;2 311 | db 8,"alwayson" ;3 312 | db 4,"fast" ;4 313 | db 6,"port92" ;5 314 | db 0 315 | 316 | if ?TESTMEM 317 | ?TMSTR equ <" /TESTMEM:ON|OFF"> 318 | else 319 | ?TMSTR equ <" "> 320 | endif 321 | if ?LOG 322 | ?LGSTR equ <" /LOG"> 323 | else 324 | ?LGSTR equ <" "> 325 | endif 326 | 327 | szHello label byte 328 | db "Extended memory host for DOS (coordinates the usage of XMS and HMA).", lf 329 | db "HimemSX is a device driver to be loaded in CONFIG.SYS.", lf 330 | db "Place DEVICE=HIMEMSX.EXE [options] before any driver using XMS!", lf, lf 331 | db "Options: /MAX=### /METHOD:xxx /HMAMIN=n /NUMHANDLES=m /V /X2MAX32 /SUPERMAX=###", lf 332 | if ?TESTMEM or ?LOG 333 | db ?TMSTR,?LGSTR, lf 334 | endif 335 | db lf 336 | db " /MAX=### limit memory controlled by XMM to ###K.", lf 337 | db " The HMA is not affected by this value, it's always included.", lf 338 | db " /METHOD:xxx Specifies the method to be used for A20 handling.", lf 339 | db " Possible values for xxx:", lf 340 | db " ALWAYSON Assume that A20 line is permanently ON", lf 341 | db " BIOS Use BIOS to toggle the A20 line", lf 342 | db " FAST Use port 92h, bypass INT 15h test", lf 343 | db " PS2 Use port 92h, bypass PS/2 test", lf 344 | db " KBC Use the keyboard controller", lf 345 | db " PORT92 Use port 92h always", lf 346 | db " /HMAMIN=n Specifies minimum number of Kbs of HMA that a program", lf 347 | db " must request to gain access to the HMA (default: 0Kb).", lf 348 | db " /NUMHANDLES=m Set number of XMS handles (default: 48, min: 8, max: ",@CatStr(!",%MAXHANDLES,!"),").", lf 349 | if ?TESTMEM 350 | db " /TESTMEM:ON|OFF Performs or skips an extended memory test (def: OFF).", lf 351 | endif 352 | db " /SUPERMAX=### limit super-extended memory controlled by XMM to ###K.", lf 353 | db " /V Gives extra information.", lf 354 | db " /X2MAX32 Limit XMS 2.0 free/avail. memory report to 32M-1K.", lf 355 | if ?LOG 356 | db " /LOG Logs the driver activity to the screen.", lf 357 | endif 358 | db 0 359 | 360 | _DATA ends 361 | 362 | ;****************************************************************************** 363 | ; resident code and data 364 | ;****************************************************************************** 365 | 366 | _RTEXT segment 367 | 368 | assume cs:DGROUP, ds:DGROUP 369 | 370 | ;****************************************************************************** 371 | ; device driver header 372 | 373 | dd -1 ; +0 last driver in list 374 | dw 8000h ; +4 driver flags 375 | dw offset strategy ; +6 pointer to strategy routine 376 | dw offset init_interrupt ; +8 pointer to interrupt handler 377 | db 'XMSXXXX0' ; device driver name 378 | 379 | ;****************************************************************************** 380 | ; global data 381 | 382 | ;--- put some variables before gdt_start (so it is QWORD aligned) 383 | 384 | a20_locks dw 0 ; internal A20 lock count 385 | _x2max32 dw MAXFREEKB ; maximum XMS 2.0 free/available 32M 386 | _hma_min dw 0 ; minimal space in HMA 387 | 388 | 389 | gdt_start label near 390 | 391 | gdt32 dw gdt_size-1,gdt_start,0 392 | hma_used db 0 ; set if HMA is used 393 | dummyretf: 394 | retf 395 | 396 | data32dsc desc <-1,0,0,92h,0cfh,0> ; 08 32-bit read/write data, 4G 397 | data16dsc desc <-1,0,0,92h, 0h,0> ; 10 16-bit read/write data, 64k 398 | if ?CATCHEXC 399 | code16dsc desc <-1,0,0,9Ah, 0h,0> ; 18 16-bit read/exec code, 64k 400 | code16sel equ 18h 401 | videodsc desc <-1,8000h,0Bh,92h,0h,0>; 20 selector for vga segment B800h 402 | videosel equ 20h 403 | endif 404 | gdt_size equ $ - offset gdt_start 405 | data32sel equ 08h 406 | data16sel equ 10h 407 | 408 | xms_highest_addr dd 10FFFFh 409 | xms_handle_table XMS_HANDLETABLE <0fdh, size XMS_HANDLE, 0, 0> 410 | 411 | if ?CATCHEXC 412 | 413 | gate struct 414 | ofs dw ? 415 | sel dw ? 416 | attrib dw ? 417 | ofs32 dw ? 418 | gate ends 419 | stdidt dw 3FFh,0,0 420 | idt32 dw 19*sizeof gate-1,idt_start,0 421 | idt_start label gate 422 | gate 423 | gate 424 | gate 425 | gate 426 | gate 427 | gate 428 | gate 429 | gate 430 | gate 431 | gate 432 | gate 433 | gate 434 | gate 435 | gate 436 | gate 437 | gate 438 | gate 439 | gate 440 | gate 441 | EXCNO = 0 442 | exc00 label near 443 | rept 19 444 | push EXCNO 445 | jmp excxx 446 | EXCNO = EXCNO+1 447 | endm 448 | excxx: 449 | pop ax 450 | shld cx,ax,12 451 | and al,0Fh 452 | add al,'0' 453 | and cl,0Fh 454 | add cl,'0' 455 | cmp al,'9' 456 | jbe @F 457 | add al,7 458 | @@: 459 | mov ah,17h 460 | mov ch,17h 461 | shl eax,16 462 | mov ax,cx 463 | push videosel 464 | pop ds 465 | mov dword ptr ds:[0],17581745h 466 | mov dword ptr ds:[4],173D1743h 467 | mov dword ptr ds:[8],eax 468 | jmp $ 469 | endif 470 | align 4 471 | 472 | ;****************************************************************************** 473 | ;--- A20 get status 474 | ; Out: ZF=0 - A20 enabled 475 | ; ZF=1 - A20 disabled 476 | 477 | test_a20 proc 478 | if 0 479 | push ax 480 | push ds 481 | push es 482 | xor ax,ax 483 | mov ds,ax 484 | dec ax 485 | mov es,ax ; es->FFFF seg 486 | assume es:DGROUP; es is NOT DGROUP, but this makes MASM happy 487 | pushf ; save original flags (IF is important) 488 | cli ; ensure interrupts are off while testing 489 | mov ax,es:[10h] ; read word at FFFF:10h, the 1M limit 490 | not ax ; ~1M word 491 | push @word ds:[0] ; save word we're changing (INT 0 offset) 492 | mov ds:[0],ax ; save ~1M word to 0:0 (and FFFF:10h if A20 disabled) 493 | ; mov ax,ds:[0] ; read back, may be unnecessary (forced memory access?) 494 | cmp ax,es:[10h] ; compare 0:0 ~1M word to 1M word, only equal if A20 is disabled 495 | pop @word ds:[0] ; restore INT 0 offset 496 | lahf 497 | popf 498 | sahf 499 | pop es 500 | pop ds 501 | pop ax 502 | else 503 | 504 | ;--- this might be better: no need to disable interrupts and no memory 505 | ;--- is modified. 4 dwords are compared (0000:0010 and FFFF:0020) 506 | 507 | push ds 508 | push es 509 | push cx 510 | push si 511 | push di 512 | mov cx,-1 513 | mov es,cx 514 | mov si,10h 515 | inc cx 516 | mov ds,cx 517 | mov di,20h 518 | mov cl,4 519 | repz cmpsd 520 | pop di 521 | pop si 522 | pop cx 523 | pop es 524 | pop ds 525 | endif 526 | ret 527 | test_a20 endp 528 | 529 | ;****************************************************************************** 530 | ; enables the A20 address line 531 | ; currently dummy/always on, code replaced as A20 tests indicate 532 | ; all registers AND flags preserved! 533 | 534 | enable_a20 proc 535 | push ax 536 | mov ah,2 537 | jmp disable_enable_a20 538 | enable_a20 endp 539 | 540 | disable_a20 proc 541 | push ax 542 | mov ah,0 543 | disable_a20 endp ;fall through 544 | 545 | disable_enable_a20 proc ;patch area 546 | 547 | ;--- the default is the ALWAYSON "method", which is just a dummy 548 | 549 | pop ax 550 | ret 551 | 552 | ; since this is replaceable, we need to bulk up the space allocated for it 553 | ; for larger replacement routines 554 | 555 | DB 42-2 DUP (?) 556 | 557 | size_disable_enable_a20 equ $ - disable_enable_a20 558 | 559 | disable_enable_a20 endp 560 | 561 | ;****************************************************************************** 562 | ; Interrupt handlers 563 | ;****************************************************************************** 564 | ;****************************************************************************** 565 | ; new INT15h handler 566 | ; 567 | ; this externally preserves A20 state on function 87h 568 | ; 569 | 570 | int15_handler proc 571 | cmp ah,87h 572 | je do_move 573 | cmp ah,88h ; is it a ext. mem size req.? 574 | je ext_mem_size 575 | cmp ax,0E820h ; memory map? 576 | je memmap 577 | db 0EAh 578 | old_int15 dd 0 ; old INT 15h vector 579 | 580 | ext_mem_size: 581 | xor ax,ax ; no memory available 582 | exit_set_reset_carry: 583 | push bp 584 | mov bp,sp 585 | rcr [bp+2].IRETS.bFlags,1 586 | rol [bp+2].IRETS.bFlags,1 587 | pop bp 588 | exit_iret: 589 | iret 590 | 591 | do_move: 592 | pushf 593 | call test_a20 594 | jz isdisabled 595 | call cs:[old_int15] 596 | call enable_a20 ; preserves flags 597 | jmp exit_set_reset_carry 598 | isdisabled: 599 | call cs:[old_int15] 600 | call disable_a20 ; preserves flags 601 | jmp exit_set_reset_carry 602 | memmap: 603 | pushf 604 | call cs:[old_int15] 605 | jc exit_set_reset_carry 606 | cmp eax,SMAP 607 | jnz exit_iret 608 | cmp es:[di].E820MAP.basehigh,0 609 | jz exit_set_reset_carry 610 | cmp es:[di].E820MAP.type_,1 ;"available" memory? 611 | clc 612 | jnz exit_set_reset_carry 613 | mov byte ptr es:[di].E820MAP.type_,2 ;change to "reserved" 614 | jmp exit_set_reset_carry 615 | 616 | int15_handler endp 617 | 618 | ;****************************************************************************** 619 | ; new INT2Fh handler. Catches Func. 4300h+4310h 620 | 621 | int2f_handler proc 622 | pushf 623 | cmp ah,43h 624 | je @@maybe_my2f 625 | @@jmp_old2f: 626 | popf 627 | db 0EAh 628 | old_int2f dd 0 ; old INT 2fh vector 629 | 630 | 631 | @@maybe_my2f: 632 | cmp al,00h ; is it "Installation Check"? 633 | je @@get_driver_installed 634 | cmp al,10h ; is it "Get Driver Address"? 635 | je @@get_xms_address 636 | cmp al,09h ; is it "get handle table"? 637 | je @@get_xms_handle_table 638 | cmp al,08h ; is it "get A20 handler number"? 639 | jne @@jmp_old2f 640 | mov al,ah ;al==43h if function supported 641 | machine_type label byte 642 | mov bx,0002 ;bh=switch time; 0=medium, 1=fast, 2=slow 643 | popf ;bl=machine type; 01=std AT (KBC), 02=PS/2 (port 92) 644 | iret 645 | @@get_driver_installed: 646 | mov al,80h ; yes, we are installed ;) 647 | popf 648 | iret 649 | @@get_xms_address: 650 | mov bx,offset xms_dispatcher 651 | @@shared2f: 652 | push cs 653 | pop es 654 | popf 655 | iret 656 | @@get_xms_handle_table: 657 | mov al,ah ;al==43h if function supported 658 | mov bx,offset xms_handle_table 659 | jmp @@shared2f 660 | 661 | int2f_handler endp 662 | 663 | ;****************************************************************************** 664 | ; XMS functions 665 | ;****************************************************************************** 666 | ; returns XMS version number 667 | ; In: AH=0 668 | ; Out: AX=XMS version number 669 | ; BX=internal revision number 670 | ; DX=1 if HMA exists, 0 if not 671 | 672 | xms_get_version proc 673 | mov ax,INTERFACE_VER 674 | mov bx,DRIVER_VER 675 | mov dx,1 ; HMA is always available 676 | ret 677 | xms_get_version endp 678 | 679 | ;****************************************************************************** 680 | ; requests HMA 681 | ; In: AH=1 682 | ; DX=space needed in HMA (0ffffh if application tries to request HMA) 683 | ; Out: AX=1 if successful, BL=0 (MS Himem compatible) 684 | ; AX=0 if not successful 685 | ; BL=80h -> function not implemented (implemented here ;) ) 686 | ; BL=81h -> VDISK is detected 687 | ; BL=90h -> HMA does not exist 688 | ; BL=91h -> HMA already in use 689 | ; BL=92h -> DX less than HMA_MIN 690 | 691 | xms_request_hma proc 692 | xor ax,ax 693 | cmp [hma_used],al ; is HMA already used? 694 | mov bl,XMS_HMA_IN_USE 695 | jnz @@exit 696 | cmp dx,[_hma_min] ; is request big enough? 697 | mov bl,XMS_HMAREQ_TOO_SMALL 698 | jb @@exit 699 | inc ax 700 | mov [hma_used],al ; assign HMA to caller 701 | mov bl,0 702 | @@exit: 703 | ret 704 | xms_request_hma endp 705 | 706 | ;****************************************************************************** 707 | ; releases HMA 708 | ; In: AH=2 709 | ; Out: AX=1 if successful, BL=0 (MS Himem compatible) 710 | ; AX=0 if not successful 711 | ; BL=80h -> function not implemented 712 | ; BL=81h -> VDISK is detected 713 | ; BL=90h -> HMA doesn't exist 714 | ; BL=93h -> HMA wasn't allocated 715 | 716 | xms_release_hma proc 717 | xor ax,ax 718 | cmp [hma_used],al ; is HMA used? 719 | mov bl,XMS_HMA_NOT_USED 720 | jz @@exit 721 | mov [hma_used],al ; now release it 722 | inc ax 723 | mov bl,0 724 | @@exit: 725 | ret 726 | xms_release_hma endp 727 | 728 | ;****************************************************************************** 729 | ; global A20 address line enable 730 | ; In: AH=3 731 | ; Out: AX=1 if successful 732 | ; AX=0 if not successful 733 | ; BL=80h -> function is not implemented 734 | ; BL=81h -> VDISK is detected 735 | ; BL=82h -> A20 failure 736 | 737 | xms_global_enable_a20 proc 738 | call enable_a20 ; enable A20 739 | call test_a20 ; is it really enabled? 740 | jz error_A20_failure 741 | xms_global_enable_a20 endp ; fall throu 742 | 743 | xms_success: 744 | mov ax,1 745 | mov bl,0 746 | ret 747 | 748 | ;****************************************************************************** 749 | ; global A20 address line disable 750 | ; In: AH=4 751 | ; Out: AX=1 if successful (A20 is disabled) 752 | ; AX=0 if not successful 753 | ; BL=80h -> function is not implemented 754 | ; BL=81h -> VDISK is detected 755 | ; BL=82h -> A20 failure 756 | ; BL=94h -> A20 still enabled 757 | 758 | xms_global_disable_a20 proc 759 | 760 | IF ALLOWDISABLEA20 761 | if 1 762 | cmp [a20_locks],0 763 | jnz error_A20_still_enabled 764 | endif 765 | call disable_a20 ; disable A20 766 | call test_a20 ; is it really disabled? 767 | jz xms_success 768 | endif 769 | 770 | error_A20_still_enabled:: 771 | xor ax,ax 772 | mov bl,XMS_A20_STILL_ENABLED 773 | ret 774 | 775 | xms_global_disable_a20 endp 776 | 777 | error_A20_failure: 778 | xor ax,ax 779 | mov bl,XMS_A20_FAILURE 780 | ret 781 | 782 | ;****************************************************************************** 783 | ; enables A20 locally 784 | ; In: AH=5 785 | ; Out: AX=1 if A20 is enabled, 0 otherwise 786 | ; BL=80h -> function not implemented 787 | ; BL=81h -> VDISK is detected 788 | ; BL=82h -> A20 failure 789 | 790 | xms_local_enable_a20 proc 791 | 792 | cmp [a20_locks],1 793 | inc [a20_locks] ; increase lock counter 794 | jc xms_global_enable_a20 795 | jmp xms_success 796 | 797 | xms_local_enable_a20 endp 798 | 799 | ;****************************************************************************** 800 | ; disables A20 locally 801 | ; In: AH=6 802 | ; Out: AX=1 if A20 is disabled, 0 otherwise 803 | ; BL=80h -> function not implemented 804 | ; BL=81h -> VDISK is detected 805 | ; BL=82h -> A20 failure 806 | ; BL=94h -> A20 still enabled 807 | 808 | xms_local_disable_a20 proc 809 | 810 | cmp [a20_locks],0 811 | jz error_A20_failure 812 | dec [a20_locks] ; decrease lock counter 813 | jnz error_A20_still_enabled 814 | jmp xms_global_disable_a20 815 | 816 | xms_local_disable_a20 endp 817 | 818 | ;****************************************************************************** 819 | ; returns the state of A20 820 | ; In: AH=7 821 | ; Out: AX=1 if A20 is physically enabled, AX=0 if not 822 | ; BL=00h -> function was successful 823 | ; BL=80h -> function is not implemented 824 | ; BL=81h -> VDISK is detected 825 | 826 | xms_query_a20 proc 827 | xor ax,ax ; suppose A20 is disabled 828 | call test_a20 829 | setnz al 830 | mov bl,0 831 | ret 832 | xms_query_a20 endp 833 | 834 | 835 | ;****************************************************************************** 836 | ; alloc a XMS memory handle 837 | ; In: DS=CS 838 | ; Out: CY=1 - no free handle 839 | ; CY=0 - free handle found 840 | ; BX - offset of free handle 841 | 842 | xms_alloc_handle proc 843 | mov cx,[xms_handle_table.xht_numhandles] ; check all handles 844 | mov bx, @word [xms_handle_table.xht_pArray] 845 | @@nexthandle: 846 | cmp [bx].XMS_HANDLE.xh_flags,XMSF_INPOOL 847 | jz @@found_handle ; found a blank handle 848 | add bx,sizeof XMS_HANDLE ; skip to next handle 849 | loop @@nexthandle 850 | stc ; no free block found, error 851 | @@found_handle: 852 | ret 853 | xms_alloc_handle endp 854 | 855 | ;****************************************************************************** 856 | ; xms_check_handle 857 | ; In: DS=CS 858 | ; SI - handle to check 859 | ; 860 | ; Out: CY=1 - no valid handle 861 | ; BL=0a2h - XMS_INVALID_HANDLE 862 | ; AX=0 - usual error return 863 | ; 864 | ; CY=0 - no error 865 | ; 866 | ; registers destroyed - AX 867 | ; 868 | ;-- called by 869 | ;-- xms_free_emb 870 | ;-- xms_move_emb 871 | ;-- xms_lock_emb 872 | ;-- xms_unlock_emb 873 | ;-- xms_ext_get_handle_info 874 | ;-- xms_ext_realloc_emb 875 | 876 | xms_check_handle_ex: 877 | mov si,dx 878 | 879 | xms_check_handle proc 880 | push dx 881 | 882 | mov ax,si 883 | sub ax, @word [xms_handle_table.xht_pArray] 884 | jb @@no_valid_handle 885 | xor dx,dx 886 | 887 | push bx 888 | mov bx,sizeof XMS_HANDLE 889 | div bx 890 | pop bx 891 | 892 | or dx,dx 893 | jnz @@no_valid_handle 894 | 895 | cmp ax,[xms_handle_table.xht_numhandles] ; less then last handle ?? 896 | jae @@no_valid_handle 897 | 898 | cmp [si].XMS_HANDLE.xh_flags,XMSF_USED ; is it in use ?? 899 | jne @@no_valid_handle 900 | pop dx 901 | xor ax,ax 902 | ret 903 | @@no_valid_handle: 904 | pop dx 905 | pop ax ;skip return address 906 | xor ax,ax 907 | mov bl,XMS_INVALID_HANDLE 908 | stc 909 | ret 910 | 911 | xms_check_handle endp 912 | 913 | ;****************************************************************************** 914 | ; query free super-extended memory 915 | ; In: AH=C8h 916 | ; Out: EAX=size of largest free super-extended block in kbytes 917 | ; EDX=total amount of free super-extended block in kbytes 918 | ; BL=0 if ok 919 | ; BL=080h -> function not implemented 920 | ; BL=081h -> VDISK is detected 921 | ; BL=0a0h -> all super-extended memory is allocated 922 | 923 | xms_sext_query_free_mem proc 924 | 925 | xor eax,eax ; contains largest free block 926 | xor edx,edx ; contains total free block 927 | 928 | push bx 929 | pushf 930 | cli 931 | mov cx,[xms_handle_table.xht_numhandles] 932 | mov bx, @word [xms_handle_table.xht_pArray] 933 | nextitem: 934 | test [bx].XMS_HANDLE.xh_flags,XMSF_FREE ; check if flagged free or in use 935 | je @F 936 | 937 | ;--- filter blocks below 4 GB 938 | ;--- 00000000-003fffffh 939 | test word ptr [bx].XMS_HANDLE.xh_baseK+2, 0ffc0h 940 | jz @F 941 | 942 | mov esi, [bx].XMS_HANDLE.xh_sizeK 943 | add edx, esi 944 | cmp esi, eax ; check if larger than largest 945 | jbe @F 946 | mov eax,esi ; larger, update 947 | @@: 948 | add bx,sizeof XMS_HANDLE 949 | loop nextitem 950 | popf ; restore IF 951 | pop bx 952 | mov bl,0 953 | and edx,edx 954 | jnz @@freeblockexists 955 | mov bl,XMS_ALL_MEM_ALLOCATED 956 | @@freeblockexists: 957 | ret ; success 958 | 959 | xms_sext_query_free_mem endp 960 | 961 | ;****************************************************************************** 962 | ; query free extended memory 963 | ; In: AH=88h 964 | ; Out: EAX=size of largest free XMS block in kbytes 965 | ; ECX=highest ending address of any memory block 966 | ; EDX=total amount of free XMS in kbytes 967 | ; BL=0 if ok 968 | ; BL=080h -> function not implemented 969 | ; BL=081h -> VDISK is detected 970 | ; BL=0a0h -> all XMS is allocated 971 | 972 | xms_ext_query_free_mem proc 973 | 974 | xor eax,eax ; contains largest free block 975 | xor edx,edx ; contains total free XMS 976 | 977 | push bx 978 | pushf 979 | cli 980 | mov cx,[xms_handle_table.xht_numhandles] 981 | mov bx, @word [xms_handle_table.xht_pArray] 982 | nextitem: 983 | test [bx].XMS_HANDLE.xh_flags,XMSF_FREE ; check if flagged free or in use 984 | je @F 985 | 986 | ;--- filter blocks beyond 4 GB 987 | ;--- 00000000-003fffffh 988 | test word ptr [bx].XMS_HANDLE.xh_baseK+2, 0ffc0h 989 | jnz @F 990 | 991 | mov esi, [bx].XMS_HANDLE.xh_sizeK 992 | add edx, esi 993 | cmp esi, eax ; check if larger than largest 994 | jbe @F 995 | mov eax,esi ; larger, update 996 | @@: 997 | add bx,sizeof XMS_HANDLE 998 | loop nextitem 999 | popf ; restore IF 1000 | pop bx 1001 | mov bl,0 1002 | and edx,edx 1003 | jnz @@freeblockexists 1004 | mov bl,XMS_ALL_MEM_ALLOCATED 1005 | @@freeblockexists: 1006 | mov ecx,[xms_highest_addr] ; highest address to ecx return value 1007 | ret ; success 1008 | 1009 | xms_ext_query_free_mem endp 1010 | 1011 | ;****************************************************************************** 1012 | ; returns free XMS 1013 | ; In: AH=8 1014 | ; Out: AX=size of largest free XMS block in kbytes 1015 | ; DX=total amount of free XMS in kbytes 1016 | ; BL=0 if ok 1017 | ; BL=0a0h -> all XMS is allocated 1018 | 1019 | xms_query_free_mem proc 1020 | 1021 | ; protect high parts 1022 | push eax 1023 | pop ax 1024 | push ecx 1025 | push edx 1026 | pop dx 1027 | 1028 | call xms_ext_query_free_mem 1029 | 1030 | ; returns: 1031 | ; EAX=size of largest free XMS block in kbytes 1032 | ; ECX=highest ending address of any memory block (not used) 1033 | ; EDX=total amount of free XMS in kbytes 1034 | 1035 | movzx ecx, [_x2max32] 1036 | cmp edx,ecx ; dx = min(edx,0ffff | 7fff) 1037 | jb @@edx_not_larger 1038 | mov dx,cx 1039 | @@edx_not_larger: 1040 | cmp eax,ecx ; ax = min(eax,0ffff | 7fff) 1041 | jb @@eax_not_larger 1042 | mov ax,cx 1043 | @@eax_not_larger: 1044 | 1045 | ; restore high parts 1046 | push dx 1047 | pop edx 1048 | 1049 | pop ecx 1050 | 1051 | push ax 1052 | pop eax 1053 | 1054 | ret 1055 | xms_query_free_mem endp 1056 | 1057 | ;****************************************************************************** 1058 | ; allocates an XMS block 1059 | ; In: AH=9 1060 | ; DX=amount of XMS being requested in kbytes 1061 | ; Out: AX=1 if successful 1062 | ; DX=handle 1063 | ; AX=0 if not successful 1064 | ; BL=080h -> function not implemented 1065 | ; BL=081h -> VDISK is detected 1066 | ; BL=0a0h -> all XMS is allocated 1067 | ; BL=0a1h -> no free handles left 1068 | 1069 | xms_alloc_emb proc 1070 | @DbgOutS <"xms_alloc_emb",13,10> 1071 | push edx 1072 | movzx edx,dx ; extend alloc request to 32-bits 1073 | jmp @@xms_alloc2 1074 | 1075 | ; 32-bit entry for function 89h, just uses full edx value 1076 | 1077 | xms_ext_alloc_emb:: ;<--- entry, ah=11h 1078 | xms_sext_alloc_emb::;<--- entry, ah=15h 1079 | @DbgOutS <"xms_ext_alloc_emb",13,10> 1080 | push edx 1081 | 1082 | @@xms_alloc2: 1083 | push cx 1084 | pushf 1085 | cli 1086 | if 0; v3.35 check for size 0 after scan! 1087 | and edx,edx ; a request for 0 kB might still work 1088 | jz @@nullhandle 1089 | endif 1090 | mov cx,[xms_handle_table.xht_numhandles] ; check all handles 1091 | mov di, @word [xms_handle_table.xht_pArray] 1092 | @@nexthandle: 1093 | cmp [di].XMS_HANDLE.xh_flags,XMSF_FREE 1094 | jnz @@skipitem 1095 | 1096 | ;--- filter blocks 1097 | cmp ah,15h ;15h is xms_sext_alloc_emb # 1098 | jnz @F 1099 | test word ptr [di].XMS_HANDLE.xh_baseK+2,0FFC0h 1100 | jnz sext_mem 1101 | jmp @@skipitem 1102 | @@: 1103 | test word ptr [di].XMS_HANDLE.xh_baseK+2,0FFC0h 1104 | jnz @@skipitem 1105 | sext_mem: 1106 | cmp edx,[di].XMS_HANDLE.xh_sizeK ; check if it's large enough 1107 | jbe @@found_block 1108 | @@skipitem: 1109 | add di,sizeof XMS_HANDLE ; skip to next handle 1110 | loop @@nexthandle 1111 | if 1; v3.35 check for size 0 1112 | and edx,edx ; a request for 0 kB might still work 1113 | jz @@nullhandle 1114 | endif 1115 | mov bl,XMS_ALL_MEM_ALLOCATED 1116 | @@alloc_failed: 1117 | popf 1118 | pop cx 1119 | pop edx 1120 | if ?ALLOCDX0 1121 | xor dx,dx ;return DX=0 if alloc fails 1122 | endif 1123 | xor ax,ax 1124 | ret 1125 | @@nullhandle: 1126 | push bx 1127 | call xms_alloc_handle ; get a free handle in BX 1128 | mov di,bx 1129 | pop bx 1130 | mov bl,XMS_NO_HANDLE_LEFT 1131 | jc @@alloc_failed 1132 | xor ax,ax ; set ZF to skip code below 1133 | 1134 | @@found_block: 1135 | mov @word [di].XMS_HANDLE.xh_flags,XMSF_USED ;clear locks field, too 1136 | jz @@perfect_fit2 ; if it fits perfectly, go on 1137 | push bx 1138 | call xms_alloc_handle ; get a free handle in BX 1139 | jc @@perfect_fit ; no more handles, use all mem left 1140 | ife ?ALTSTRAT 1141 | mov esi,[di].XMS_HANDLE.xh_sizeK 1142 | mov [di].XMS_HANDLE.xh_sizeK,edx 1143 | sub esi,edx ; calculate resting memory 1144 | add edx,[di].XMS_HANDLE.xh_baseK; calc new base address of free block 1145 | mov @word [bx].XMS_HANDLE.xh_flags,XMSF_FREE 1146 | mov [bx].XMS_HANDLE.xh_baseK,edx; set new base of free block 1147 | mov [bx].XMS_HANDLE.xh_sizeK,esi; set remaining size of free block 1148 | else 1149 | ;--- alternate strategie: return the new allocated handle, 1150 | ;--- the found handle stays free 1151 | mov [di].XMS_HANDLE.xh_flags,XMSF_FREE 1152 | mov esi,[di].XMS_HANDLE.xh_baseK 1153 | add [di].XMS_HANDLE.xh_baseK,edx 1154 | sub [di].XMS_HANDLE.xh_sizeK,edx 1155 | mov [bx].XMS_HANDLE.xh_baseK,esi 1156 | mov [bx].XMS_HANDLE.xh_sizeK,edx 1157 | mov @word [bx].XMS_HANDLE.xh_flags,XMSF_USED ;clear locks field, too 1158 | mov di,bx 1159 | endif 1160 | @@perfect_fit: 1161 | pop bx 1162 | @@perfect_fit2: 1163 | popf 1164 | pop cx 1165 | pop edx 1166 | mov dx,di ; return handle in DX 1167 | mov bl,0 1168 | mov ax,1 1169 | ret 1170 | xms_alloc_emb endp 1171 | 1172 | ;****************************************************************************** 1173 | ; frees an XMS block 1174 | ; In: AH=0ah 1175 | ; DX=handle to allocated block that should be freed 1176 | ; Out: AX=1 if successful 1177 | ; AX=0 if not successful 1178 | ; BL=080h -> function not implemented 1179 | ; BL=081h -> VDISK is detected 1180 | ; BL=0a2h -> handle is invalid 1181 | ; BL=0abh -> handle is locked 1182 | 1183 | xms_free_emb proc 1184 | 1185 | call xms_check_handle_ex ; check if dx holds a "used" handle 1186 | mov bl,XMS_BLOCK_LOCKED 1187 | cmp [si].XMS_HANDLE.xh_locks,0 ; is the block locked? 1188 | jnz @@exit 1189 | push eax 1190 | push bx 1191 | push cx 1192 | push edx 1193 | pushf 1194 | cli 1195 | ;--- see if there are blocks to merge 1196 | mov eax,[si].XMS_HANDLE.xh_baseK ; get base address 1197 | mov edx,[si].XMS_HANDLE.xh_sizeK 1198 | mov edi, eax ; base in edi 1199 | add eax, edx ; end-address in eax 1200 | ife ?MERGE0HDL 1201 | mov cl, XMSF_FREE 1202 | and edx, edx 1203 | jnz @F 1204 | mov cl, XMSF_INPOOL 1205 | @@: 1206 | mov [si].XMS_HANDLE.xh_flags,cl 1207 | jz @@done 1208 | endif 1209 | 1210 | ;--- now scan the handle array for successor/predecessor 1211 | 1212 | mov cx,[xms_handle_table.xht_numhandles] 1213 | mov bx,@word [xms_handle_table.xht_pArray] 1214 | @@nextitem: 1215 | cmp [bx].XMS_HANDLE.xh_flags,XMSF_FREE 1216 | jnz @@skipitem 1217 | mov edx,[bx].XMS_HANDLE.xh_baseK 1218 | cmp eax, edx ; is successor free? 1219 | je @F 1220 | add edx,[bx].XMS_HANDLE.xh_sizeK 1221 | cmp edi, edx ; is predecessor free? 1222 | jne @@skipitem 1223 | @@: 1224 | ;--- predecessor/successor in BX 1225 | cmp bx,si 1226 | jbe @F 1227 | xchg bx,si ;merge into the "lower" handle and free the "higher" handle 1228 | @@: 1229 | xor edx, edx 1230 | xchg edx, [si].XMS_HANDLE.xh_sizeK 1231 | add [bx].XMS_HANDLE.xh_sizeK, edx ;new size is sum of both handle sizes 1232 | xor edx, edx 1233 | xchg edx, [si].XMS_HANDLE.xh_baseK 1234 | cmp edx, [bx].XMS_HANDLE.xh_baseK 1235 | ja @F 1236 | mov [bx].XMS_HANDLE.xh_baseK, edx ;new base is min(hdl1.base,hdl2.base) 1237 | @@: 1238 | mov [si].XMS_HANDLE.xh_flags,XMSF_INPOOL 1239 | mov si,bx 1240 | @@skipitem: 1241 | add bx,sizeof XMS_HANDLE 1242 | loop @@nextitem 1243 | if ?MERGE0HDL 1244 | mov cl, XMSF_FREE 1245 | cmp [si].XMS_HANDLE.xh_sizeK,0 1246 | jnz @F 1247 | mov cl, XMSF_INPOOL 1248 | @@: 1249 | mov [si].XMS_HANDLE.xh_flags,cl 1250 | endif 1251 | @@done: 1252 | popf 1253 | pop edx 1254 | pop cx 1255 | pop bx 1256 | pop eax 1257 | inc ax 1258 | mov bl,0 1259 | @@exit: 1260 | ret 1261 | 1262 | xms_free_emb endp 1263 | 1264 | ;****************************************************************************** 1265 | ; calculates the move address 1266 | ; In: SI - handle (0 if EDX should be interpreted as seg:ofs value) 1267 | ; BX.EDX - offset 1268 | ; ECX - length 1269 | ; Out: EAX - move address in kB 1270 | ; Modifies: EDX, SI 1271 | 1272 | xms_get_move_addr proc 1273 | or si,si ; translate address in EDX? 1274 | jnz @@is_emb 1275 | 1276 | ; its segment:offset in EDX 1277 | 1278 | ; eax = 16*(edx high) + dx 1279 | movzx eax,dx ; save offset 1280 | mov dh,0 1281 | shr edx,12 ; convert segment to absolute address 1282 | add eax,edx ; add offset 1283 | 1284 | mov edx,eax ; check that eax(address) + ecx (length) is <= 10fff0 1285 | add edx,ecx 1286 | jc @@wrong_size ; negative length might wrap 1287 | cmp edx,10fff0h 1288 | ja @@wrong_size 1289 | clc 1290 | ret 1291 | 1292 | @@is_emb: ; it's a handle:offset pair 1293 | call xms_check_handle ;check if si holds a "used" handle 1294 | 1295 | push bx 1296 | mov eax,ecx ; contains length 1297 | add eax,edx ; assert length + offset < size 1298 | adc bx,0 1299 | add eax,1024-1 ; round up to kB 1300 | adc bx,0 1301 | shrd eax,ebx,10 ; convert to kB units 1302 | pop bx 1303 | cmp eax,[si].XMS_HANDLE.xh_sizeK ; compare with max offset 1304 | ja @@wrong_size 1305 | 1306 | mov eax,[si].XMS_HANDLE.xh_baseK ; get block base address 1307 | mov esi,eax ; store in source index 1308 | shl eax,10 ; convert from kb to linear 1309 | shr esi,22 1310 | add eax,edx ; add offset into block 1311 | adc si,bx 1312 | ret 1313 | 1314 | @@wrong_size: 1315 | mov bl,XMS_INVALID_LENGTH 1316 | xor ax,ax 1317 | stc 1318 | ret 1319 | xms_get_move_addr endp 1320 | 1321 | ;****************************************************************************** 1322 | ; moves an EMB 1323 | ; In: AH=0bh or 16h 1324 | ; ES:SI=pointer to XMS move structure (DS is DGROUP) 1325 | ; Out: AX=1 if successful 1326 | ; AX=0 if not successful 1327 | ; BL=080h -> function not implemented 1328 | ; BL=081h -> VDISK is detected 1329 | ; BL=082h -> A20 failure 1330 | ; BL=0a3h -> source handle is invalid 1331 | ; BL=0a4h -> source offset is invalid 1332 | ; BL=0a5h -> destination handle is invalid 1333 | ; BL=0a6h -> destination offset is invalid 1334 | ; BL=0a7h -> length is invalid 1335 | ; BL=0a8h -> move has invalid overlap 1336 | ; BL=0a9h -> parity error 1337 | 1338 | 1339 | xms_move_emb proc 1340 | 1341 | @DbgOutS <"xms_move_emb enter",13,10> 1342 | IF ALLOWDISABLEA20 1343 | call test_a20 ; get A20 state 1344 | jnz @@was_enabled 1345 | call enable_a20 ; now enable it! - if it was disabled 1346 | push offset disable_a20 ; and make sure it is disabled on exit 1347 | @@was_enabled: 1348 | endif 1349 | cmp ah,16h ; super-extended move 1350 | mov ax,0 ; default to error 1351 | push ecx 1352 | push edx 1353 | push eax 1354 | push bp 1355 | push bx 1356 | 1357 | lahf 1358 | 1359 | mov ecx,es:[si].xms_move.len ; get length 1360 | test cl,1 ; is it even? 1361 | jnz @@move_invalid_length 1362 | 1363 | push si 1364 | push ax 1365 | xor bx,bx 1366 | test ah,40h 1367 | jz @F 1368 | mov bl,es:[si].sxms_move.dest_hi 1369 | @@: 1370 | mov edx,es:[si].xms_move.dest_offset 1371 | mov si,es:[si].xms_move.dest_handle 1372 | call xms_get_move_addr ; get move address 1373 | mov dx,si ;save lines 32-39 in DX, since BL must be preserved 1374 | pop di 1375 | pop si 1376 | jc @@copy_dest_is_wrong 1377 | test di,4000h 1378 | mov edi,eax ; store in destination index 1379 | push dx 1380 | jz @F 1381 | mov bl,es:[si].sxms_move.src_hi 1382 | @@: 1383 | mov edx,es:[si].xms_move.src_offset 1384 | mov si,es:[si].xms_move.src_handle 1385 | call xms_get_move_addr ; get move address 1386 | pop dx 1387 | jc @@copy_source_is_wrong 1388 | xchg eax,esi 1389 | mov bh,al 1390 | mov bl,dl 1391 | 1392 | ;************************************************** 1393 | ; setup finished with 1394 | ; BH.ESI = source A00-A39 1395 | ; BL.EDI = destination A00-A39 1396 | ; ECX = number of words to move 1397 | ;************************************************** 1398 | 1399 | or ecx,ecx ; nothing to do ?? 1400 | jz @@xms_exit_copy 1401 | 1402 | mov bp,offset rmcopysx ; switch to pm with paging 1403 | and bx,bx 1404 | jnz @F 1405 | mov bp,offset rmcopy ; both blocks < 4GB, no PSE-36 paging needed 1406 | @@: 1407 | 1408 | ; overlap test. start of destination block (BL.EDI) 1409 | ; must either be <= start of source block (BH.ESI) 1410 | ; or >= start of source block + block length (BH.ESI+ECX) 1411 | 1412 | ; 1. check if BL.EDI <= BH.ESI 1413 | cmp bl,bh 1414 | jb @@move_ok_to_start 1415 | ja @F 1416 | cmp edi,esi 1417 | jbe @@move_ok_to_start 1418 | @@: 1419 | ; calculate source + block length: DL.EAX = BH.ESI + ECX 1420 | mov dl,bh 1421 | mov eax, esi 1422 | add eax, ecx 1423 | adc dl,0 1424 | ; 2. check if BL.EDI >= DL.EAX 1425 | cmp bl,dl 1426 | ja @@move_ok_to_start 1427 | jb @@move_invalid_overlap 1428 | cmp edi,eax 1429 | jb @@move_invalid_overlap 1430 | 1431 | @@move_ok_to_start: 1432 | SMSW AX ; don't use priviledged "mov eax,cr0"! 1433 | test al,1 ; are we already in PM? 1434 | jz @F 1435 | ; cmp bx,0 1436 | ; jnz @@copy_invalid_handle 1437 | mov bp,offset pmcopy ; yes, use INT 15h, ah=87h 1438 | push ss ; set ES for int 15h, ah=87h call 1439 | pop es 1440 | @@: 1441 | mov edx,ecx 1442 | 1443 | @@copy_loop: 1444 | mov ecx,edx 1445 | cmp ecx,BLOCKSIZE 1446 | ; jle @F ; 27.10.2020 don't do signed comparisons! 1447 | jbe @F 1448 | mov ecx,BLOCKSIZE 1449 | @@: 1450 | sub edx,ecx 1451 | push edx 1452 | call bp 1453 | pop edx 1454 | jc @@move_a20_failure 1455 | and edx, edx 1456 | jnz @@copy_loop 1457 | @@xms_exit_copy: 1458 | pop bx 1459 | pop bp 1460 | pop eax 1461 | pop edx 1462 | pop ecx 1463 | inc ax ; success 1464 | ret 1465 | 1466 | @@move_invalid_overlap: 1467 | mov bl,XMS_INVALID_OVERLAP 1468 | jmp @@xms_exit_copy_failure 1469 | 1470 | @@move_invalid_length: 1471 | mov bl,XMS_INVALID_LENGTH 1472 | jmp @@xms_exit_copy_failure 1473 | 1474 | @@copy_source_is_wrong: 1475 | cmp bl,XMS_INVALID_LENGTH 1476 | je @@xms_exit_copy_failure 1477 | mov bl,XMS_INVALID_SOURCE_HANDLE 1478 | jmp @@xms_exit_copy_failure 1479 | 1480 | @@copy_dest_is_wrong: 1481 | cmp bl,XMS_INVALID_LENGTH 1482 | je @@xms_exit_copy_failure 1483 | mov bl,XMS_INVALID_DESTINATION_HANDLE 1484 | jmp @@xms_exit_copy_failure 1485 | @@copy_invalid_handle: 1486 | mov bl,XMS_INVALID_HANDLE 1487 | jmp @@xms_exit_copy_failure 1488 | 1489 | @@move_a20_failure: 1490 | mov bl,XMS_A20_FAILURE 1491 | 1492 | ; common error exit routine 1493 | @@xms_exit_copy_failure: 1494 | pop ax 1495 | pop bp 1496 | mov bh,ah ; restore BH only, preserve BL 1497 | pop eax 1498 | pop edx 1499 | pop ecx 1500 | ret 1501 | 1502 | ;------------------------------------------------------------ 1503 | ; "real-mode" copy proc 1504 | ; ESI = src linear adress 1505 | ; EDI = dst linear adress 1506 | ; ECX = length (hiword cleared) 1507 | 1508 | ; 2 strategies are implemented. The first does the move in protected-mode, 1509 | ; the latter activates "unreal" mode and does the move there. 1510 | ; After the last transfer "unreal" mode is exited. Interrupts occuring 1511 | ; during the "interrupt window" will run in "unreal" mode as well, but 1512 | ; this shouldn't hurt. 1513 | 1514 | rmcopy: 1515 | 1516 | ife USEUNREAL 1517 | 1518 | pushf 1519 | cli ; no interrupts when doing protected mode 1520 | if PREF66LGDT 1521 | db 66h ; load full 32bit base 1522 | endif 1523 | lgdt fword ptr cs:[gdt32]; load GDTR (use CS prefix here) 1524 | mov eax,cr0 1525 | inc ax ; set PE bit 1526 | mov cr0,eax 1527 | jmp @F ; setting CS to a protected-mode selector is not required 1528 | @@: 1529 | mov dx,data32sel 1530 | mov ds,dx 1531 | mov es,dx 1532 | 1533 | shr cx,2 ; get number of DWORDS to move 1534 | rep movs @dword [edi],[esi] 1535 | adc cx,cx 1536 | rep movs @word [edi],[esi] ; move a trailing WORD 1537 | 1538 | mov dx,data16sel ; restore selector attributes to 64 kB 1539 | mov ds,dx 1540 | mov es,dx 1541 | 1542 | dec ax ; clear PE bit 1543 | mov cr0,eax 1544 | popf 1545 | clc 1546 | ret 1547 | 1548 | else 1549 | if 0 1550 | pushf 1551 | cli ; no interrupts during the block move 1552 | pushf 1553 | mov dx,data32sel ; activate "unreal" mode. This has to be done 1554 | call set_ureal ; every time since the mode might have been 1555 | xor dx,dx ; exited by an interrupt routine. 1556 | mov ds,dx 1557 | mov es,dx 1558 | shr cx,2 ; get number of DWORDS to move 1559 | rep movs @dword [edi],[esi] 1560 | adc cx,cx 1561 | rep movs @word [edi],[esi] ; move a trailing WORD 1562 | popf 1563 | jnz @F 1564 | call reset_ureal 1565 | @@: 1566 | popf 1567 | ret 1568 | 1569 | reset_ureal: 1570 | mov dx,data16sel 1571 | set_ureal: 1572 | if PREF66LGDT 1573 | db 66h ; load full 32bit base 1574 | endif 1575 | lgdt fword ptr cs:[gdt32]; load GDTR (use CS prefix here) 1576 | mov eax,cr0 1577 | inc ax ; set PE bit 1578 | mov cr0,eax 1579 | ;--- the 80386 needs a "flush" after switching to PM 1580 | ;--- before a segment register can be set! 1581 | jmp @F 1582 | @@: 1583 | dec ax ; clear PE bit 1584 | mov ds,dx 1585 | mov es,dx 1586 | mov cr0,eax 1587 | ret 1588 | else 1589 | ;--- set int 0dh, then just start to copy. 1590 | ;--- if int 0dh is called, an exception occured, since IRQs are disabled. 1591 | ;--- then set unreal mode inside int 0dh code. 1592 | pushf 1593 | push cs 1594 | push offset myint0d 1595 | xor dx,dx 1596 | pop eax 1597 | shr ecx,2 ; get number of DWORDS to move 1598 | mov ds,dx 1599 | mov es,dx 1600 | cli 1601 | xchg eax,ds:[13*4] 1602 | rep movs @dword [edi],[esi] 1603 | adc cx,cx 1604 | rep movs @word [edi],[esi] ; move a trailing WORD 1605 | mov ds:[13*4],eax ; restore int 0dh 1606 | popf 1607 | ret 1608 | myint0d: 1609 | push ds 1610 | push es 1611 | push eax 1612 | lgdt fword ptr cs:[gdt32]; load GDTR (use CS prefix here) 1613 | mov eax,cr0 1614 | inc ax ; set PE bit 1615 | mov cr0,eax 1616 | ; jmp @F 1617 | ;@@: 1618 | mov dx,data32sel 1619 | dec ax ; clear PE bit 1620 | mov ds,dx 1621 | mov es,dx 1622 | mov cr0,eax 1623 | pop eax 1624 | pop es 1625 | pop ds 1626 | iret 1627 | endif 1628 | endif 1629 | 1630 | 1631 | ;------------------------------------------------------------------------ 1632 | ; cpu is in v86-mode, use int15, ah=87 to copy things around 1633 | 1634 | ; ESI = src linear adress 1635 | ; EDI = dst linear adress 1636 | ; ECX = length (hiword cleared) 1637 | 1638 | ; int 15h, ah=87h expects: 1639 | ; CX = number of WORDS to copy 1640 | ; ES:SI -> GDT 1641 | ; GDT: entry 0 = 0 1642 | ; entry 1 = 0 1643 | ; entry 2 = src 1644 | ; entry 3 = dst 1645 | ; entry 4 = used internally 1646 | ; entry 5 = used internally 1647 | 1648 | pmcopy: 1649 | sub sp,2*8 ;room for entries 4+5 1650 | 1651 | shld eax,edi,16 ;push dst descriptor 1652 | mov dl,al 1653 | mov dh,093h 1654 | mov al,0 1655 | push ax 1656 | push dx 1657 | push di 1658 | push cx 1659 | 1660 | shld eax,esi,16 ;push src descriptor 1661 | mov dl,al 1662 | mov al,0 1663 | push ax 1664 | push dx 1665 | push si 1666 | push cx 1667 | 1668 | xor eax,eax ;push entries 0+1 1669 | push eax 1670 | push eax 1671 | push eax 1672 | push eax 1673 | 1674 | ;--- update BH.ESI and BL.EDI 1675 | mov ax,bx 1676 | add edi,ecx ; buff += copied length 1677 | adc al,0 1678 | add esi,ecx 1679 | adc ah,0 1680 | 1681 | push si 1682 | lea si,[esp+2] 1683 | push ax 1684 | shr cx,1 ; convert to words 1685 | 1686 | ;--- is super-extended memory involved? 1687 | and bx,bx 1688 | jz @F 1689 | ;--- then the extended Int15h, ah=87h API must be set 1690 | ;--- CX = F00Fh 1691 | ;--- HiWord(ECX) = size in words 1692 | ;--- Hiword(EAX) = F00Fh 1693 | ;--- DX = src bits 32-47 1694 | ;--- BX = dst bits 32-47 1695 | ;--- ES:SI -> GDT similar as in standard Int 15, ah=87h 1696 | shl ecx,16 1697 | mov cx,0F00Fh 1698 | mov ax,cx 1699 | shl eax,16 1700 | movzx dx,bh 1701 | movzx bx,bl 1702 | @@: 1703 | clc 1704 | mov ah,87h 1705 | int 15h 1706 | pop bx 1707 | pop si 1708 | lea sp,[esp+6*8];don't modify flags! 1709 | ret 1710 | 1711 | ;--- copy in pm with 32-bit paging and 4 MB pages 1712 | ;--- EDI: bits 0-31 of destination 1713 | ;--- ESI: bits 0-31 of source 1714 | ;--- BL: bits 32-39 of destination 1715 | ;--- BH: bits 32-39 of source 1716 | ;--- ECX: bytes to copy 1717 | 1718 | if ?PD110000 1719 | PageDir equ 110000h 1720 | endif 1721 | DSTBASE equ 400000h*1 1722 | SRCBASE equ 400000h*3 1723 | 1724 | rmcopysx:: 1725 | pushf 1726 | push cx 1727 | push esi 1728 | push edi 1729 | 1730 | cli ; no interrupts when doing protected mode 1731 | 1732 | lgdt fword ptr cs:[gdt32]; load GDTR (use CS prefix here) 1733 | if ?CATCHEXC 1734 | lidt fword ptr cs:[idt32] 1735 | endif 1736 | .586p 1737 | mov eax,cr4 1738 | or al,10h ;enable PSE 1739 | mov cr4,eax 1740 | .386p 1741 | mov eax,cr0 1742 | mov dx,data32sel 1743 | inc ax ;set PE bit 1744 | mov cr0,eax 1745 | mov ds,dx 1746 | mov es,dx 1747 | 1748 | if ?PD110000 1749 | mov edx,PageDir 1750 | bts eax,31 1751 | mov dword ptr [edx+0],PF_PAGE0 1752 | mov cr3,edx 1753 | mov cr0,eax 1754 | else 1755 | mov edx,0 1756 | PageDir equ dword ptr $-4 1757 | mov cr3,edx 1758 | mov dword ptr [edx+0],PF_PAGE0 1759 | endif 1760 | 1761 | ;--- 4 MB page directory entries: 1762 | ;--- bit 7: 1=4MB page 1763 | ;--- bit 12: PAT 1764 | ;--- bit 13-20: addressbits 32-39 1765 | ;--- bit 21: 0 1766 | ;--- bit 22-31: addressbits 22-31 1767 | 1768 | ;--- .3.........2.........1.........0 1769 | ;--- 10987654321098765432109876543210 1770 | ;--- xxxxxxxxxx?????????????????????? bits 22-31 in edi 1771 | ;--- ?????????????xxxxxxxxxx????????? shld eax,edi,19 1772 | ;--- ?????????????xxxxxxxxxx?xxxxxxxx mov al,bl 1773 | ;--- ?????????????xxxxxxxxxx0xxxxxxxx and ah,0feh 1774 | ;--- xxxxxxxxxx0xxxxxxxx0000000000000 shl eax,13 1775 | shld eax,edi,19 1776 | mov al,bl 1777 | and ah,0feh 1778 | shl eax,13 1779 | 1780 | and edi,3fffffh 1781 | mov al,83h ;dst 4MB page, writable, present 1782 | add edi,DSTBASE 1783 | mov [edx+4],eax 1784 | add eax,1 shl 22 1785 | jnc @F ;4 GB boundary crossed? 1786 | add eax,1 shl 13 1787 | @@: 1788 | mov [edx+8],eax 1789 | 1790 | shld eax,esi,19 1791 | mov al,bh 1792 | and ah,0feh 1793 | shl eax,13 1794 | 1795 | and esi,3fffffh 1796 | mov al,81h ;src 4MB page, present 1797 | add esi,SRCBASE 1798 | mov [edx+12],eax 1799 | add eax,1 shl 22 1800 | jnc @F ;4 GB boundary crossed? 1801 | add eax,1 shl 13 1802 | @@: 1803 | mov [edx+16],eax 1804 | 1805 | ife ?PD110000 1806 | mov eax,cr0 1807 | bts eax,31 1808 | mov cr0,eax 1809 | endif 1810 | 1811 | shr cx,2 ; get number of DWORDS to move 1812 | rep movs @dword [edi],[esi] 1813 | adc cx,cx 1814 | rep movs @word [edi],[esi] ; move a trailing WORD 1815 | 1816 | if ?RESTDSESREGS 1817 | mov dx,data16sel ; restore selector attributes to 64 kB 1818 | mov ds,dx 1819 | mov es,dx 1820 | endif 1821 | 1822 | mov eax,cr0 1823 | and eax,7ffffffeh 1824 | mov cr0,eax 1825 | if ?RESTCSREG 1826 | db 0eah ;jmp far16 opcode 1827 | dw offset @F 1828 | RmCS dw 0 1829 | @@: 1830 | endif 1831 | if ?CATCHEXC 1832 | lidt fword ptr cs:[stdidt] 1833 | endif 1834 | 1835 | pop edi 1836 | pop esi 1837 | pop cx 1838 | add edi,ecx 1839 | adc bl,0 1840 | add esi,ecx 1841 | adc bh,0 1842 | popf 1843 | clc 1844 | ret 1845 | 1846 | xms_move_emb endp 1847 | 1848 | ;****************************************************************************** 1849 | ; locks an EMB 1850 | ; In: AH=0ch 1851 | ; DX=XMS handle to be locked 1852 | ; Out: AX=1 if block is locked 1853 | ; DX:BX=32-bit linear address of block 1854 | ; AX=0 if not successful 1855 | ; BL=080h -> function not implemented 1856 | ; BL=081h -> VDISK is detected 1857 | ; BL=0a2h -> handle is invalid 1858 | ; BL=0ach -> lock count overflow 1859 | ; BL=0adh -> lock fails 1860 | 1861 | xms_lock_emb proc 1862 | 1863 | @DbgOutS <"xms_lock_emb enter",13,10> 1864 | call xms_check_handle_ex ; check if dx holds "used" handle 1865 | 1866 | test [si].XMS_HANDLE.xh_baseK+2,0FFC0h ;beyond 4GB? 1867 | mov bl,XMS_LOCK_FAILED 1868 | jnz _ret 1869 | 1870 | inc [si].XMS_HANDLE.xh_locks ; increase lock counter 1871 | jz @@lock_error 1872 | mov esi,[si].XMS_HANDLE.xh_baseK 1873 | shl esi,10 ; calculate linear address 1874 | push esi 1875 | pop bx 1876 | pop dx 1877 | inc ax 1878 | _ret: 1879 | ret 1880 | @@lock_error:: 1881 | dec [si].XMS_HANDLE.xh_locks 1882 | mov bl,XMS_LOCK_COUNT_OVERFLOW 1883 | ret 1884 | xms_lock_emb endp 1885 | 1886 | ;****************************************************************************** 1887 | ; locks a (super-extended) MB 1888 | ; In: AH=0cch 1889 | ; DX=XMS handle to be locked 1890 | ; Out: AX=1 if block is locked 1891 | ; EDX:EBX=64-bit linear address of block 1892 | ; AX=0 if not successful 1893 | ; BL=080h -> function not implemented 1894 | ; BL=081h -> VDISK is detected 1895 | ; BL=0a2h -> handle is invalid 1896 | ; BL=0ach -> lock count overflow 1897 | ; BL=0adh -> lock fails 1898 | 1899 | xms_sext_lock_emb proc 1900 | 1901 | @DbgOutS <"xms_sext_lock_emb enter",13,10> 1902 | call xms_check_handle_ex ; check if dx holds "used" handle 1903 | inc [si].XMS_HANDLE.xh_locks; increase lock counter 1904 | jz @@lock_error 1905 | mov ebx,[si].XMS_HANDLE.xh_baseK 1906 | mov edx,ebx 1907 | shl ebx,10 ; calculate linear address 0-31 1908 | shr edx,22 ; calculate linear address 32-41 1909 | inc ax 1910 | ret 1911 | xms_sext_lock_emb endp 1912 | 1913 | ;****************************************************************************** 1914 | ; unlocks an EMB 1915 | ; In: AH=0dh 1916 | ; DX=XMS handle to unlock 1917 | ; Out: AX=1 if block is unlocked 1918 | ; AX=0 if not successful 1919 | ; BL=080h -> function not implemented 1920 | ; BL=081h -> VDISK is detected 1921 | ; BL=0a2h -> handle is invalid 1922 | ; BL=0aah -> block is not locked 1923 | 1924 | xms_unlock_emb proc 1925 | 1926 | @DbgOutS <"xms_unlock_emb enter",13,10> 1927 | call xms_check_handle_ex ; check if dx holds "used" handle 1928 | cmp [si].XMS_HANDLE.xh_locks,al; check if block is locked 1929 | jz @F 1930 | dec [si].XMS_HANDLE.xh_locks ; decrease lock counter 1931 | inc ax 1932 | mov bl,0 1933 | ret 1934 | @@: 1935 | mov bl,XMS_BLOCK_NOT_LOCKED 1936 | ret 1937 | xms_unlock_emb endp 1938 | 1939 | ;****************************************************************************** 1940 | ; get XMS handle information 1941 | ; In: AH=8eh 1942 | ; DX=XMS handle 1943 | ; Out: AX=1 if successful 1944 | ; BH=block's lock count 1945 | ; CX=number of free XMS handles 1946 | ; EDX=block's length in kbytes 1947 | 1948 | xms_ext_get_handle_info proc 1949 | 1950 | @DbgOutS <"xms_ext_get_handle_info enter",13,10> 1951 | call xms_check_handle_ex; check handle validity (dx== "used" handle?) 1952 | xor cx,cx ; reset free handle counter 1953 | mov dx,[xms_handle_table.xht_numhandles] 1954 | mov di,@word [xms_handle_table.xht_pArray] ;use DI here, not BX 1955 | @@nextitem: 1956 | cmp [di].XMS_HANDLE.xh_flags,XMSF_INPOOL 1957 | setz al 1958 | add cx,ax 1959 | add di,sizeof XMS_HANDLE 1960 | dec dx 1961 | jnz @@nextitem 1962 | mov bh,[si].XMS_HANDLE.xh_locks ; store lock count 1963 | mov edx,[si].XMS_HANDLE.xh_sizeK ; store block size 1964 | ; mov bl,0 ;set BL on exit? 1965 | mov al,1 1966 | ret 1967 | xms_ext_get_handle_info endp 1968 | 1969 | ;******************************************************************** 1970 | ; returns XMS handle information 1971 | ; In: AH=0eh 1972 | ; DX=XMS handle 1973 | ; Out: AX=1 if successful 1974 | ; BH=block's lock count 1975 | ; BL=number of free XMS handles 1976 | ; DX=block's length in kbytes 1977 | ; AX=0 if not successful 1978 | ; BL=080h -> function not implemented 1979 | ; BL=081h -> VDISK is detected 1980 | ; BL=0a2h -> handle is invalid 1981 | 1982 | xms_get_handle_info proc 1983 | 1984 | push cx 1985 | push edx 1986 | pop dx 1987 | @DbgOutS <"xms_get_handle_info enter",13,10> 1988 | 1989 | call xms_ext_get_handle_info 1990 | or ax,ax 1991 | jz @@get_handle_info_err 1992 | 1993 | cmp ch,0 ; bl = min(cx,0xff) 1994 | jz @F 1995 | mov cl,0ffh 1996 | @@: 1997 | mov bl,cl 1998 | 1999 | ;--- MS Himem: if size > 0xffff, return error BL=A2h 2000 | if ?HINF_MSCOMP 2001 | cmp edx,010000h ; dx must be <= 0xffff 2002 | jc @F 2003 | mov bl,XMS_INVALID_HANDLE 2004 | dec ax 2005 | jmp @@get_handle_info_err 2006 | else 2007 | cmp edx,010000h ; dx = min(edx,0xffff); 2008 | jb @F 2009 | mov dx,0ffffh 2010 | endif 2011 | @@: 2012 | 2013 | @@get_handle_info_err: 2014 | 2015 | push dx 2016 | pop edx 2017 | pop cx 2018 | ret 2019 | 2020 | xms_get_handle_info endp 2021 | 2022 | ;****************************************************************************** 2023 | ; reallocates an EMB. shrinking and growing supported 2024 | ; In: AH=8fh 2025 | ; EBX=new size for the EMB in kbytes 2026 | ; DX=unlocked XMS handle 2027 | ; Out: AX=1 if successful 2028 | ; AX=0 if not successful 2029 | ; BL=080h -> function not implemented 2030 | ; BL=081h -> VDISK is detected 2031 | ; BL=0a0h -> all XMS is allocated 2032 | ; BL=0a1h -> all handles are in use 2033 | ; BL=0a2h -> invalid handle 2034 | ; BL=0abh -> block is locked 2035 | 2036 | xms_ext_realloc_emb proc 2037 | 2038 | @DbgOutS <"xms_ext_realloc_emb enter",13,10> 2039 | call xms_check_handle_ex ; dx == "used" handle? 2040 | push edx 2041 | 2042 | ; fail if block is locked 2043 | cmp [si].XMS_HANDLE.xh_locks,0 2044 | jne @@ext_xms_locked 2045 | 2046 | mov edx, ebx 2047 | if 1;v3.35 2048 | mov cx,[xms_handle_table.xht_numhandles] 2049 | mov di,@word [xms_handle_table.xht_pArray] 2050 | mov eax,[si].XMS_HANDLE.xh_sizeK 2051 | add eax,[si].XMS_HANDLE.xh_baseK 2052 | nextitem: 2053 | test [di].XMS_HANDLE.xh_flags,XMSF_FREE ;scan "free embs" only 2054 | jz skipitem 2055 | cmp eax,[di].XMS_HANDLE.xh_baseK ;successor? 2056 | jnz skipitem 2057 | mov eax,[si].XMS_HANDLE.xh_sizeK 2058 | add eax,[di].XMS_HANDLE.xh_sizeK ;get the total size 2059 | cmp edx,eax ;new size > total size? 2060 | ja @@ext_growing ;then the handle can't grow, have to copy... 2061 | sub edx,[si].XMS_HANDLE.xh_sizeK ;get the size which is additionally needed (might be < 0!) 2062 | mov [si].XMS_HANDLE.xh_sizeK, ebx 2063 | add [di].XMS_HANDLE.xh_baseK, edx 2064 | sub [di].XMS_HANDLE.xh_sizeK, edx 2065 | jnz @@ext_grow_success ;remaining size > 0? 2066 | mov [di].XMS_HANDLE.xh_flags, XMSF_INPOOL ;no, so free the handle 2067 | mov [di].XMS_HANDLE.xh_baseK, 0 2068 | jmp @@ext_grow_success 2069 | skipitem: 2070 | add di,sizeof XMS_HANDLE 2071 | loop nextitem 2072 | endif 2073 | cmp ebx,[si].XMS_HANDLE.xh_sizeK 2074 | jbe @@ext_shrink_it 2075 | 2076 | @@ext_growing: 2077 | ; growing, try to allocate a new block 2078 | ; new size in kB in EDX 2079 | 2080 | mov ah,11h ;11h is xms_ext_alloc_emb # 2081 | test word ptr [si].XMS_HANDLE.xh_baseK+2, 0FFC0h 2082 | jz @F 2083 | mov ah,15h ;15h is for super-extended memory 2084 | @@: 2085 | call xms_ext_alloc_emb ;get a new handle in DX, size EDX 2086 | and ax,ax 2087 | jz @@ext_failed 2088 | 2089 | ; got new block, copy info from old block to new block 2090 | 2091 | pop si ; get old handle 2092 | push si 2093 | 2094 | ; transfer old handle data to new location. 2095 | ; v3.54: since the block may be > 4G and max amount to copy is 4G-2, 2096 | ; the transfer is done with chunks of max. 4G - 1K. 2097 | 2098 | MAXCHUNK equ 4096 * 1024 - 1 2099 | 2100 | xor edi,edi 2101 | push di ; src+dst hibytes 2102 | push edi ; dst.offset 2103 | push dx ; dst.handle 2104 | push edi ; src.offset 2105 | push si ; src.handle 2106 | mov ecx,[si].XMS_HANDLE.xh_sizeK 2107 | nextchk: 2108 | mov edi, ecx 2109 | cmp ecx, MAXCHUNK ;remaining more than max.? 2110 | jb @F 2111 | mov edi, MAXCHUNK ;load edi with 4G-1K 2112 | @@: 2113 | shl edi, 10 ; K to byte 2114 | push edi ; length 2115 | mov si, sp 2116 | push ss 2117 | pop es ; es:si -> xms_move 2118 | mov ah, 16h ; always use sext move 2119 | call xms_move_emb 2120 | pop edi ; get size of current chunk in edi 2121 | push cs ; xms_move_emb eats critical ds value 2122 | pop ds 2123 | mov si, sp 2124 | add ss:[si-4].sxms_move.src_offset, edi ; adjust src and dest offsets 2125 | adc ss:[si-4].sxms_move.src_hi, 0 ; including bits 32-39 2126 | add ss:[si-4].sxms_move.dest_offset, edi 2127 | adc ss:[si-4].sxms_move.dest_hi, 0 2128 | shr edi, 10 2129 | sub ecx, edi 2130 | jnz nextchk 2131 | add sp, sizeof sxms_move - 4 2132 | 2133 | pop si ; restore handle 2134 | push si 2135 | 2136 | ; swap handle data so handle pointers remain valid 2137 | ; handle data is 10 bytes long 2138 | 2139 | mov di,dx 2140 | pushf 2141 | cli ;exchange must be atomic 2142 | mov edx,[si+0] 2143 | xchg edx,[di+0] 2144 | mov [si+0],edx 2145 | mov edx,[si+4] 2146 | mov ax,[si+8] 2147 | xchg edx,[di+4] 2148 | xchg ax,[di+8] 2149 | mov [si+4],edx 2150 | mov [si+8],ax 2151 | popf 2152 | mov dx,di 2153 | 2154 | ; free newly allocated handle in DX with old handle data in it 2155 | 2156 | call xms_free_emb 2157 | jmp @@ext_grow_success 2158 | 2159 | @@ext_no_xms_handles_left: 2160 | pop bx 2161 | popf 2162 | mov bl,XMS_NO_HANDLE_LEFT 2163 | jmp @@ext_failed 2164 | 2165 | @@ext_xms_locked: 2166 | mov bl,XMS_BLOCK_LOCKED 2167 | 2168 | @@ext_failed: 2169 | pop edx 2170 | xor ax,ax 2171 | ret 2172 | 2173 | @@ext_shrink_it: 2174 | mov edi,[si].XMS_HANDLE.xh_sizeK ; get old size 2175 | sub edi,edx ; calculate what's left over 2176 | jz @@ext_dont_need_handle ; jump if we don't need another handle 2177 | if 0; v3.36: don't modify si handle data until the new handle has been allocated 2178 | mov [si].XMS_HANDLE.xh_sizeK, edx 2179 | add edx,[si].XMS_HANDLE.xh_baseK ; calculate new base address 2180 | endif 2181 | pushf 2182 | cli 2183 | push bx 2184 | call xms_alloc_handle ; alloc a handle in BX, size EDI 2185 | jc @@ext_no_xms_handles_left ; return if there's an error 2186 | if 1; v3.36: don't modify si handle data until the new handle has been allocated 2187 | mov [si].XMS_HANDLE.xh_sizeK, edx 2188 | add edx,[si].XMS_HANDLE.xh_baseK ; calculate new base address 2189 | endif 2190 | mov [bx].XMS_HANDLE.xh_baseK, edx 2191 | mov [bx].XMS_HANDLE.xh_sizeK, edi 2192 | if 1;v3.35 2193 | ;--- if this branch is active, there's surely NO free successor 2194 | ;--- so we don't need to merge. 2195 | mov [bx].XMS_HANDLE.xh_flags,XMSF_FREE 2196 | pop bx 2197 | popf 2198 | else 2199 | mov @word [bx].XMS_HANDLE.xh_flags,XMSF_USED 2200 | mov dx,bx ; and FREE it again - 2201 | pop bx 2202 | popf 2203 | call xms_free_emb ; to merge it with free block list 2204 | endif 2205 | 2206 | @@ext_dont_need_handle: 2207 | @@ext_grow_success: 2208 | pop edx 2209 | mov ax,1 2210 | mov bl,0 2211 | ret 2212 | xms_ext_realloc_emb endp 2213 | 2214 | ;****************************************************************************** 2215 | ; reallocates an EMB. shrinking and growing supported 2216 | ; In: AH=0fh 2217 | ; BX=new size for the EMB in kbytes 2218 | ; DX=unlocked XMS handle 2219 | ; 2220 | 2221 | xms_realloc_emb proc 2222 | 2223 | @DbgOutS <"xms_realloc_emb enter",13,10> 2224 | push ebx ; protect high part of ebx 2225 | pop bx 2226 | movzx ebx,bx ; clear top 16 bit 2227 | call xms_ext_realloc_emb 2228 | push bx ; recover top 16 bit of ebx 2229 | pop ebx 2230 | ret 2231 | 2232 | xms_realloc_emb endp 2233 | 2234 | ;****************************************************************************** 2235 | ; requests an UMB block 2236 | ; In: AH=10h 2237 | ; DX=size of requested memory block in paragraphs 2238 | ; Out: AX=1 if successful 2239 | ; BX=segment number of UMB 2240 | ; DX=actual size of the allocated block in paragraphs 2241 | ; AX=0 if not successful 2242 | ; DX=size of largest available UMB in paragraphs 2243 | ; BL=080h -> function not implemented 2244 | ; BL=0b0h -> only a smaller UMB are available 2245 | ; BL=0b1h -> no UMBs are available 2246 | 2247 | 2248 | ;****************************************************************************** 2249 | ; releases an UMB block 2250 | ; In: AH=11h 2251 | ; DX=segment of UMB 2252 | ; Out: AX=1 if successful 2253 | ; AX=0 if not successful 2254 | ; BL=080h -> function not implemented 2255 | ; BL=0b2h -> UMB segment number is invalid 2256 | 2257 | 2258 | ;****************************************************************************** 2259 | ; reallocates an UMB 2260 | ; In: AH=12h 2261 | ; BX=new size for UMB in paragraphs 2262 | ; DX=segment of UMB to reallocate 2263 | ; Out: AX=1 if successful 2264 | ; AX=0 if not successful 2265 | ; BL=080h -> function not implemented 2266 | ; BL=0b0h -> no UMB large enough to satisfy request 2267 | ; DX=size of largest UMB in paragraphs 2268 | ; BL=0b2h -> UMB segment is invalid 2269 | 2270 | ;xms_realloc_umb: 2271 | ;xms_request_umb: 2272 | ;xms_release_umb: 2273 | ; xor ax,ax 2274 | ; mov bl,XMS_NOT_IMPLEMENTED 2275 | ; ret;return_failure 2276 | 2277 | ;****************************************************************************** 2278 | ; XMS dispatcher 2279 | ;****************************************************************************** 2280 | ; XMS dispatcher 2281 | ; In: AH - function number 2282 | ; Out: AX=0 -> function not supported 2283 | ; else see appr. routine 2284 | 2285 | align 2 2286 | 2287 | xms_table label word 2288 | dw xms_get_version ;0 2289 | dw xms_request_hma ;1 2290 | dw xms_release_hma ;2 2291 | dw xms_global_enable_a20 ;3 2292 | dw xms_global_disable_a20 ;4 2293 | dw xms_local_enable_a20 ;5 2294 | dw xms_local_disable_a20 ;6 2295 | dw xms_query_a20 ;7 2296 | dw xms_query_free_mem ;8 2297 | dw xms_alloc_emb ;9 2298 | dw xms_free_emb ;0A 2299 | dw xms_move_emb ;0B 2300 | dw xms_lock_emb ;0C 2301 | dw xms_unlock_emb ;0D 2302 | dw xms_get_handle_info ;0E 2303 | dw xms_realloc_emb ;0F 2304 | 2305 | dw xms_ext_query_free_mem ; 88 10 2306 | dw xms_ext_alloc_emb ; 89 11 2307 | dw xms_ext_get_handle_info ; 8e 12 2308 | dw xms_ext_realloc_emb ; 8f 13 2309 | 2310 | dw xms_sext_query_free_mem ; C8 14 2311 | dw xms_sext_alloc_emb ; C9 15 2312 | dw xms_move_emb ; CB 16 2313 | dw xms_sext_lock_emb ; CC 17 2314 | 2315 | xms_dispatcher proc 2316 | jmp short @F 2317 | nop 2318 | nop ; 3 nops, guarantee hookability 2319 | nop 2320 | @@: 2321 | 2322 | if ?LOG 2323 | dispatcher_log_entry label byte 2324 | call log_entry ; this might get patched 2325 | endif 2326 | 2327 | pushf 2328 | cmp ah,0fh ; 00-0F? 2329 | jbe @@ok1 2330 | mov al,ah 2331 | shr al,1 2332 | cmp al,88h/2 ; 88-89? 2333 | jz @@ok2 2334 | cmp al,8Eh/2 ; 8E-8F? 2335 | jz @@ok3 2336 | cmp al,0C8h/2 ; C8-C9? 2337 | jz @@ok4 2338 | cmp ah,0CBh ; CB? 2339 | jz @@ok5 2340 | cmp ah,0CCh ; CC? 2341 | jz @@ok5 2342 | xor ax,ax ; everything else fails 2343 | mov bl,XMS_NOT_IMPLEMENTED 2344 | jmp @@dispatcher_end 2345 | @@ok5: 2346 | dec ah ; CB-CC -> CA-CB 2347 | @@ok4: 2348 | sub ah,0C8h-14h ; C8-CB -> 14-17 2349 | jmp @@ok1 2350 | @@ok3: 2351 | sub ah,4 ; 8E-8F -> 8A-8B 2352 | @@ok2: 2353 | sub ah, 88h-10h ; 88-8B -> 10-13 2354 | ; 2355 | ;real dispatcher 2356 | ; 2357 | ; save ds,es,esi,edi 2358 | ; set es = USERds 2359 | ; set ds = DGROUP 2360 | 2361 | @@ok1: 2362 | cld 2363 | push ds ; protect registers 2364 | push es 2365 | push esi ; might get used 32 bit wide 2366 | push edi ; 2367 | 2368 | push ds ; set up segment registers for internal use 2369 | pop es 2370 | push cs 2371 | pop ds 2372 | 2373 | movzx di,ah ; is nowhere used as input 2374 | shl di,1 2375 | call [xms_table+di] ; call the handler here 2376 | pop edi ; restore saved registers 2377 | pop esi 2378 | 2379 | pop es 2380 | pop ds 2381 | ; @DbgOutS <"xms exit",13,10> 2382 | 2383 | @@dispatcher_end: 2384 | popf 2385 | if ?LOG 2386 | dispatcher_log_exit label byte 2387 | call log_exit ; this might get patched 2388 | endif 2389 | retf 2390 | 2391 | xms_dispatcher endp 2392 | 2393 | ?PRINTSTR = 0 2394 | ifdef _DEBUG 2395 | ?PRINTSTR = 1 2396 | endif 2397 | ?PRINTSTR = ?PRINTSTR + ?LOG 2398 | 2399 | if ?PRINTSTR 2400 | ;******************************************* 2401 | ; printing routines 2402 | ; 2403 | ; usage: 2404 | ; call printstring 2405 | ; db 'hello world' 2406 | ; 2407 | ; printdh,printdx - what the name implies 2408 | ; 2409 | ;******************************************* 2410 | printstring proc 2411 | 2412 | pusha 2413 | mov bp,sp 2414 | ifdef _DEBUG 2415 | pushf 2416 | endif 2417 | mov si,[bp+16] 2418 | cld 2419 | @@nextitem: 2420 | lods @byte cs:[si] 2421 | cmp al, 0 ; end of string? 2422 | je @@done 2423 | mov ah,0Eh 2424 | mov bx,0007h 2425 | int 10h 2426 | jmp @@nextitem 2427 | @@done: 2428 | mov [bp+16],si 2429 | ifdef _DEBUG 2430 | popf 2431 | endif 2432 | popa 2433 | ret 2434 | 2435 | printstring endp 2436 | 2437 | endif 2438 | 2439 | if 0 2440 | 2441 | printdx proc 2442 | call printdh 2443 | ror dx,8 2444 | 2445 | printdh: 2446 | call printnibble 2447 | printnibble: 2448 | 2449 | ror dh,4 2450 | mov al,dh 2451 | and al,0Fh 2452 | cmp al,10 2453 | sbb al,69H 2454 | das 2455 | int 29h 2456 | ret 2457 | printdx endp 2458 | 2459 | endif 2460 | 2461 | if ?LOG 2462 | ;*** returns NZ, if we shall log NOW 2463 | ;*** this will LOG stuff to screen only, if 2464 | ;*** SCROLL_LOCK is locked 2465 | 2466 | lognow proc 2467 | push ds 2468 | push 40h 2469 | pop ds 2470 | test @byte ds:[17h],10h 2471 | pop ds 2472 | ret 2473 | lognow endp 2474 | 2475 | log_entry proc 2476 | pushf 2477 | call lognow 2478 | jz @F 2479 | call printstring 2480 | db 'XMS enter:',0 2481 | @@: 2482 | popf 2483 | ret 2484 | log_entry endp 2485 | 2486 | log_exit proc 2487 | pushf 2488 | call lognow 2489 | jz @F 2490 | call printstring 2491 | db ' XMS leave',13,10,0 2492 | @@: 2493 | popf 2494 | ret 2495 | log_exit endp 2496 | 2497 | endif 2498 | 2499 | ;****************************************************************************** 2500 | ; mark for the trace log mode driver end. above has to be the resident part, 2501 | 2502 | align 4 2503 | 2504 | trace_driver_end: 2505 | 2506 | _RTEXT ends 2507 | 2508 | _TEXT segment 2509 | 2510 | startoftext label byte 2511 | 2512 | ;****************************************************************************** 2513 | ; checks if VDISK is already installed 2514 | ; note: HMA check is skipped because of speed and some other (weird) reasons. 2515 | ; In: nothing 2516 | ; Out: ZF=0 -> VDISK is installed 2517 | ; ZF=1 -> VDISK not installed 2518 | ; 2519 | ; tom:it's absolute unclear, if [13] or [12] should be checked. 2520 | ; HIMEM verifies [13], so we do that as well. 2521 | ; goto HELL, dear VDISK 2522 | ; verify only 4 bytes, should do as well 2523 | ; 2524 | 2525 | 2526 | _install_check_vdisk proc 2527 | push bx 2528 | push ds 2529 | 2530 | xor bx,bx ; get interrupt vector 19h 2531 | mov ds,bx 2532 | lds bx,ds:[19h*4] 2533 | 2534 | cmp @dword [bx],053494456h; 'VDIS' 2535 | 2536 | pop ds 2537 | pop bx 2538 | ret 2539 | _install_check_vdisk endp 2540 | 2541 | ; checks if CPU is a 386 2542 | ; In: nothing 2543 | ; Out: CY=0 - processor is a 386 or higher 2544 | ; CY=1 - processor lower than 386 2545 | 2546 | check_cpu proc 2547 | pushf 2548 | mov ah,70h 2549 | push ax 2550 | popf 2551 | pushf 2552 | pop ax 2553 | popf 2554 | and ah,0F0h 2555 | cmp ah,70h ;Z=386 ok 2556 | ret 2557 | check_cpu endp 2558 | 2559 | if 0 2560 | ; checks if A20 can be enabled and disabled 2561 | ; Out: CF=0 - A20 switching works 2562 | ; CF=1 - A20 failure 2563 | 2564 | check_a20 proc 2565 | call enable_a20 2566 | call test_a20 ; TEST_A20 should return ZF=0 2567 | jz a20failed 2568 | IF ALLOWDISABLEA20 2569 | call disable_a20 2570 | call test_a20 ; TEST_A20 should return ZF=1 2571 | jz a20_ok 2572 | ; we can't disable A20. 2573 | ; so what ? 2574 | ; these guys are crazy anyway, 2575 | ; and we (nearly) ignore that 2576 | 2577 | mov dx,offset cant_disable_message 2578 | call dispmsg 2579 | a20_ok: 2580 | ENDIF 2581 | clc 2582 | ret 2583 | a20failed: 2584 | stc 2585 | ret 2586 | check_a20 endp 2587 | 2588 | endif 2589 | 2590 | ;--- there are 3 A20 switch procs: 2591 | ;--- 1. KBC (port 64h/60h) 2592 | ;--- 2. fast, ps2, port92 (port 92h) 2593 | ;--- 3. BIOS (int 15h, ax=240xh) 2594 | 2595 | ; try turning A20 on or off from current to see if it works 2596 | ; KBC HIMEM method 2597 | ; entry: ah == 0 A20 turn off, ah == 2 turn on, ax on stack 2598 | ; 2599 | disable_enable_a20_KBC proc 2600 | push cx 2601 | pushf 2602 | cli ; shut off interrupts while we twiddle 2603 | 2604 | call Sync8042 ; check keyboard controller ready 2605 | mov al,0D1h ; Send D1h 2606 | out 64h,al 2607 | call Sync8042 2608 | mov al,0ddh ; or df=dd+2 2609 | or al,ah ; disable/enable A20 command (DDh/DFh) 2610 | out 60h,al 2611 | call Sync8042 2612 | 2613 | ; wait up to 20 microseconds for A20 line to settle 2614 | mov al,0FFh ; pulse output port NULL 2615 | out 64h,al 2616 | call Sync8042 2617 | popf 2618 | pop cx 2619 | pop ax 2620 | ret 2621 | 2622 | Sync8042: 2623 | xor cx,cx 2624 | @@InSync: 2625 | in al,64h 2626 | and al,2 2627 | loopnz @@InSync 2628 | ret 2629 | size_disable_enable_a20_KBC equ $ - disable_enable_a20_KBC 2630 | 2631 | disable_enable_a20_KBC endp 2632 | 2633 | ; the so-called 'fast' A20 method replacement code 2634 | ; entry: ah == 0 A20 turn off, ah == 2 turn on, ax on stack 2635 | ; 2636 | disable_enable_a20_fast proc 2637 | pushf 2638 | in al,92h 2639 | or ah,ah 2640 | jne @@deaf_on ; turning on A20 2641 | test al,2 2642 | je @@deaf_done ; already flagged off, don't do it again, might upset something 2643 | and al,NOT 2 ; set A20 bit off 2644 | jmp @@deaf_out 2645 | 2646 | ; ah == 2 2647 | @@deaf_on: 2648 | test al,ah 2649 | jne @@deaf_done ; already flagged on 2650 | or al,ah ; set A20 bit on 2651 | 2652 | @@deaf_out: 2653 | out 92h,al 2654 | 2655 | ; wait until it gets on or off, possibly superfluous, code opinion differs 2656 | push cx 2657 | xor cx,cx 2658 | @@deaf_wait: 2659 | in al,92h 2660 | and al,2 2661 | cmp al,ah 2662 | loopne @@deaf_wait 2663 | pop cx 2664 | 2665 | @@deaf_done: 2666 | popf 2667 | pop ax 2668 | ret 2669 | 2670 | size_disable_enable_a20_fast equ $ - disable_enable_a20_fast 2671 | 2672 | disable_enable_a20_fast endp 2673 | 2674 | ; BIOS A20 method 2675 | ; entry: ah == 0 A20 turn off, ah == 2 turn on, ax on stack 2676 | ; don't check for errors, assume BIOS works more than once on same call, 2677 | ; if it doesn't, not much we can do about it anyway 2678 | ; 2679 | disable_enable_a20_BIOS: 2680 | pushf 2681 | sub sp,10 ; give buggy BIOS some stack to chew on without causing problems 2682 | ; one word might suffice, but let's be really safe 2683 | cli 2684 | shr ah,1 ; ah to 0 or 1 2685 | mov al,24h 2686 | xchg ah,al ; ax == 2400h to turn off, 2401h to turn on 2687 | int 15h 2688 | 2689 | add sp,10 ; restore potentially gnawed-on stack 2690 | popf 2691 | pop ax 2692 | ret 2693 | size_disable_enable_a20_BIOS equ $ - disable_enable_a20_BIOS 2694 | 2695 | ;--- copy the enable/disable code to disable_enable_a20 2696 | 2697 | flag_kbc: 2698 | mov @byte [machine_type+1],1 ;set machine to 1 (AT), which is KBC 2699 | mov dx,offset szKBC 2700 | mov si,offset disable_enable_a20_KBC 2701 | mov cx,size_disable_enable_a20_KBC 2702 | jmp xxx_success 2703 | flag_bios: 2704 | mov dx,offset szBIOS 2705 | mov si,offset disable_enable_a20_BIOS 2706 | mov cx,size_disable_enable_a20_BIOS 2707 | jmp xxx_success 2708 | flag_port92: 2709 | mov dx,offset szPort92 2710 | jmp fast_success 2711 | flag_ps2: 2712 | mov dx,offset szPS2 2713 | jmp fast_success 2714 | flag_fast: 2715 | mov dx,offset szFast 2716 | fast_success: 2717 | mov si, offset disable_enable_a20_fast 2718 | mov cx, size_disable_enable_a20_fast 2719 | xxx_success: 2720 | push si 2721 | push cx 2722 | 2723 | mov ah,9 2724 | int 21h 2725 | mov dx,offset szA20 2726 | mov ah,9 2727 | int 21h 2728 | 2729 | pop cx 2730 | pop si 2731 | 2732 | push di 2733 | push es 2734 | push cs 2735 | pop es 2736 | mov di, offset disable_enable_a20 2737 | rep movsb 2738 | pop es 2739 | pop di 2740 | clc ; flag success 2741 | ret 2742 | 2743 | ; check if BIOS flags port 92h fast method supported 2744 | 2745 | detect_fast proc 2746 | stc 2747 | mov ax,2403h 2748 | int 15h 2749 | jc @@fail_test 2750 | or ah,ah 2751 | jne @@fail_test 2752 | test bl,2 ;PS/2 supported? 2753 | je @@fail_test 2754 | 2755 | mov si,offset disable_enable_a20_fast 2756 | call detect_and_handle_test 2757 | ret 2758 | @@fail_test: 2759 | stc ; flag failure 2760 | ret 2761 | detect_fast endp 2762 | 2763 | ; check if BIOS flags PS/2 present, to try port 92h fast method used by PS/2's 2764 | ; shares enable/disable code with fast 2765 | 2766 | detect_PS2 proc 2767 | 2768 | mov ah,0c0h ; get system description vector 2769 | stc 2770 | int 15h 2771 | jc @@fail_test ; not a PS/2 2772 | 2773 | ; test feature information byte 1, micro channel implemented bit 2774 | test @byte es:[bx+5],2 2775 | jz @@fail_test ; not micro channel 2776 | 2777 | mov si,offset disable_enable_a20_fast 2778 | call detect_and_handle_test 2779 | ret 2780 | 2781 | @@fail_test: 2782 | stc ; flag failure 2783 | ret 2784 | 2785 | detect_PS2 endp 2786 | 2787 | ; check if port 92h fast method supported without BIOS or PS/2 test 2788 | ; shares enable/disable code with fast and PS/2 2789 | 2790 | detect_port92 proc 2791 | 2792 | mov si,offset disable_enable_a20_fast 2793 | call detect_and_handle_test 2794 | ret 2795 | 2796 | detect_port92 endp 2797 | 2798 | 2799 | detect_BIOS proc 2800 | stc ; preset carry flag 2801 | mov ax,2402h ; get gate status 2802 | int 15h 2803 | jc @@fail_test 2804 | or ah,ah 2805 | jne @@fail_test 2806 | mov cl,al ; save status 2807 | 2808 | mov si,offset disable_enable_a20_BIOS 2809 | call detect_and_handle_test 2810 | ret 2811 | @@fail_test: 2812 | stc ; flag failure 2813 | ret 2814 | 2815 | detect_BIOS endp 2816 | 2817 | 2818 | detect_KBC proc 2819 | 2820 | mov si,offset disable_enable_a20_KBC 2821 | call detect_and_handle_test 2822 | ret 2823 | 2824 | detect_KBC endp 2825 | 2826 | ; upon entry si->disable/enable routine for a20 method being tested 2827 | ; return carry set if failed, reset if success 2828 | ; 2829 | detect_and_handle_test proc 2830 | call test_a20 2831 | setnz cl 2832 | jz @@dah_2 ; A20 disabled on entry 2833 | 2834 | push offset @@dah_2 2835 | push ax 2836 | mov ah,0 2837 | jmp si ; try to disable A20 2838 | 2839 | @@dah_2: 2840 | call test_a20 2841 | jnz @@dah_fail ; A20 not disabled 2842 | 2843 | ; try to enable A20 (always disabled at this point) 2844 | push offset @@dah_3 2845 | push ax 2846 | mov ah,2 2847 | jmp si 2848 | 2849 | @@dah_3: 2850 | call test_a20 2851 | jz @@dah_fail ; A20 not enabled 2852 | or cl,cl 2853 | jne @@dah_success ; A20 was enabled on entry, done 2854 | push offset @@dah_4 ; disable to entry state 2855 | push ax 2856 | mov ah,0 2857 | jmp si 2858 | 2859 | @@dah_4: 2860 | call test_a20 2861 | jnz @@dah_fail ; A20 not disabled 2862 | @@dah_success: 2863 | clc 2864 | ret 2865 | @@dah_fail: 2866 | stc 2867 | ret 2868 | 2869 | detect_and_handle_test endp 2870 | 2871 | ;--- end of A20 code 2872 | 2873 | ; reserve size of routine checks 2874 | 2875 | if size_disable_enable_a20_fast gt size_disable_enable_a20 2876 | .err 2877 | endif 2878 | if size_disable_enable_a20_BIOS gt size_disable_enable_a20 2879 | .err 2880 | endif 2881 | if size_disable_enable_a20_KBC gt size_disable_enable_a20 2882 | .err 2883 | endif 2884 | 2885 | ;--- set the a20 enable/disable code in the resident part 2886 | ;--- out: NC if ok, C on errors 2887 | 2888 | seta20method proc 2889 | 2890 | ; process forced methods 2891 | 2892 | mov al,[_method] 2893 | cmp al,A20_ALWAYSON 2894 | je @@is_alwayson 2895 | cmp al,A20_BIOS 2896 | je @@is_bios 2897 | cmp al,A20_FAST 2898 | je @@is_fast 2899 | cmp al,A20_PS2 2900 | je @@is_ps2 2901 | cmp al,A20_KBC 2902 | je @@is_kbc 2903 | cmp al,A20_PORT92 2904 | je @@is_port92 2905 | 2906 | ; check if the A20 line is on, if so assume it's always on 2907 | call test_a20 2908 | jz @@check_A20_method ; not on, try other methods 2909 | 2910 | ; use A20 always on code (dummy enable/disable A20 routine) 2911 | @@is_alwayson: 2912 | mov dx,offset szAlwaysOn 2913 | mov ah,9 2914 | int 21h 2915 | mov dx,offset szA20 2916 | mov ah,9 2917 | int 21h 2918 | jmp @@got_type 2919 | 2920 | @@check_A20_method: 2921 | 2922 | call detect_fast; see if port 92h (2403h BIOS call) handler supported 2923 | jnc @@is_fast 2924 | 2925 | call detect_PS2 ; see if port 92h (PS/2 signature) handler supported 2926 | jnc @@is_ps2 2927 | 2928 | call detect_KBC ; see if KBC handler supported 2929 | jnc @@is_kbc 2930 | 2931 | ; try BIOS here, demoted from first in line because unreliable BIOS 2932 | ; versions of A20 control exist 2933 | 2934 | call detect_BIOS ; see if BIOS A20 handler supported 2935 | jnc @@is_bios 2936 | 2937 | ; see if fast port 92h handler supported without BIOS or PS/2 signature 2938 | ; leave this test until last because messing with port 92h is 2939 | ; reported to crash some machines which don't support that method 2940 | call detect_port92 2941 | jnc @@is_port92 2942 | 2943 | ; out of options to try, return error 2944 | 2945 | mov dx,offset MsgUnknownA20 2946 | call dispmsg 2947 | stc 2948 | ret 2949 | 2950 | @@is_bios: 2951 | call flag_bios 2952 | jmp @@got_type 2953 | @@is_fast: 2954 | call flag_fast 2955 | jmp @@got_type 2956 | @@is_ps2: 2957 | call flag_ps2 2958 | jmp @@got_type 2959 | @@is_kbc: 2960 | call flag_kbc 2961 | jmp @@got_type 2962 | @@is_port92: 2963 | call flag_port92 2964 | @@got_type: 2965 | clc 2966 | ret 2967 | 2968 | seta20method endp 2969 | 2970 | ;****************************************************************************** 2971 | ; strategy routine. is called by DOS to initialize the driver once. 2972 | ; only thing to be done here is to store the address of the device driver 2973 | ; request block. 2974 | ; In: ES:BX - address of request header 2975 | ; Out: nothing 2976 | 2977 | strategy proc far 2978 | mov @word cs:[request_ptr+0],bx ; store offset addr 2979 | mov @word cs:[request_ptr+2],es ; store segment addr 2980 | ret ; far return here! 2981 | strategy endp 2982 | 2983 | printf proto c :ptr, :vararg 2984 | 2985 | print_char proc 2986 | pop cx 2987 | pop dx 2988 | push cx 2989 | cmp dl,10 2990 | jnz @@isnotlf 2991 | mov dl,13 2992 | mov ah,2 2993 | int 21h 2994 | mov dl,10 2995 | @@isnotlf: 2996 | mov ah,2 2997 | int 21h 2998 | ret 2999 | print_char endp 3000 | 3001 | ;--- get the A20 method ("/METHOD:xxx") 3002 | 3003 | GetA20Method proc stdcall uses si di pszMethod:ptr 3004 | 3005 | mov si,pszMethod 3006 | mov di,offset methods 3007 | xor bx,bx 3008 | push ds 3009 | pop es 3010 | cld 3011 | @@nextitem: 3012 | mov cl,[di] 3013 | mov ch,0 3014 | jcxz @@notfound 3015 | inc di 3016 | pusha 3017 | @@nextchar: 3018 | lodsb 3019 | or al,20h 3020 | scasb 3021 | loopz @@nextchar 3022 | popa 3023 | jz @@found 3024 | add di,cx 3025 | inc bx 3026 | jmp @@nextitem 3027 | @@notfound: 3028 | or bx,-1 3029 | jmp @@done 3030 | @@found: 3031 | mov di,si 3032 | add si,cx 3033 | @@nextchar2: 3034 | lodsb 3035 | stosb 3036 | and al,al 3037 | jnz @@nextchar2 3038 | @@done: 3039 | mov ax,bx 3040 | ret 3041 | 3042 | GetA20Method endp 3043 | 3044 | ;--- convert long to string 3045 | ;--- SS may be != DS here! 3046 | ;--- assume psz is a string defined on stack! 3047 | 3048 | ltob proc stdcall uses edi num:dword, psz:ptr, base:word 3049 | 3050 | mov ch,0 3051 | movzx edi,base 3052 | mov eax,num 3053 | cmp di,-10 3054 | jne @@ispositive 3055 | mov di,10 3056 | and eax,eax 3057 | jns @@ispositive 3058 | neg eax 3059 | mov ch,'-' 3060 | @@ispositive: 3061 | mov bx,psz 3062 | add bx,10 3063 | mov @byte ss:[bx],0 3064 | dec bx 3065 | @@nextdigit: 3066 | xor edx, edx 3067 | div edi 3068 | add dl,'0' 3069 | cmp dl,'9' 3070 | jbe @@isdigit 3071 | add dl,7+20h 3072 | @@isdigit: 3073 | mov ss:[bx],dl 3074 | dec bx 3075 | and eax, eax 3076 | jne @@nextdigit 3077 | cmp ch,0 3078 | je @@nosign 3079 | mov ss:[bx],ch 3080 | dec bx 3081 | @@nosign: 3082 | inc bx 3083 | mov ax,bx 3084 | ret 3085 | 3086 | ltob endp 3087 | 3088 | ;--- assume SS!=DS 3089 | 3090 | printf proc c uses si di fmt:ptr, args:vararg 3091 | 3092 | local flag:byte 3093 | local longarg:byte 3094 | local size_:word 3095 | local fill:word 3096 | local szTmp[12]:byte 3097 | 3098 | lea di,args 3099 | nextfcharX: 3100 | mov si,fmt 3101 | nextfchar: 3102 | lodsb 3103 | or al,al 3104 | je done 3105 | cmp al,'%' 3106 | je isfspec 3107 | push ax 3108 | call print_char 3109 | jmp nextfchar 3110 | done: 3111 | xor ax,ax 3112 | ret 3113 | 3114 | isfspec: 3115 | push nextfcharX 3116 | xor dx,dx 3117 | mov longarg,dl 3118 | mov bl,1 3119 | mov cl,' ' 3120 | cmp @byte [si],'-' 3121 | jne @F 3122 | dec bx 3123 | inc si 3124 | @@: 3125 | mov flag,bl 3126 | cmp @byte [si],'0' 3127 | jne @F 3128 | mov cl,'0' 3129 | inc si 3130 | @@: 3131 | mov fill,cx 3132 | mov bx,dx 3133 | .while byte ptr [si] >= '0' && byte ptr [si] <= '9' 3134 | lodsb 3135 | sub al,'0' 3136 | cbw 3137 | imul bx,bx,10 3138 | add bx,ax 3139 | .endw 3140 | mov size_,bx 3141 | cmp @byte [si],'l' 3142 | jne @F 3143 | mov longarg,1 3144 | inc si 3145 | @@: 3146 | lodsb 3147 | mov fmt,si 3148 | cmp al,'x' 3149 | je print_x 3150 | cmp al,'X' 3151 | je print_x 3152 | cmp al,'c' 3153 | je print_c 3154 | cmp al,'d' 3155 | je print_d 3156 | cmp al,'i' 3157 | je print_i 3158 | cmp al,'s' 3159 | je print_s 3160 | cmp al,'u' 3161 | je print_u 3162 | and al,al 3163 | jnz @F 3164 | pop ax 3165 | jmp done 3166 | print_c: 3167 | mov ax,ss:[di] 3168 | add di,2 3169 | @@: 3170 | push ax 3171 | call print_char 3172 | retn 3173 | print_x: 3174 | mov bx,16 3175 | jmp print_number 3176 | print_d: 3177 | print_i: 3178 | mov bx,-10 3179 | jmp print_number 3180 | print_u: 3181 | mov bx,10 3182 | print_number: 3183 | cmp longarg,0 3184 | je @F 3185 | mov eax,ss:[di] 3186 | add di,4 3187 | jmp print_long 3188 | @@: 3189 | movzx eax,@word ss:[di] 3190 | add di,2 3191 | cmp bx,0 3192 | jge @F 3193 | movsx eax,ax 3194 | @@: 3195 | print_long: 3196 | lea cx,szTmp 3197 | invoke ltob, eax, cx, bx 3198 | mov si,ax 3199 | push ds 3200 | push ss 3201 | pop ds 3202 | call print_string 3203 | pop ds 3204 | retn 3205 | 3206 | print_s: 3207 | mov si,ss:[di] 3208 | add di,2 3209 | 3210 | print_string: 3211 | mov ax,si 3212 | mov bx,size_ 3213 | .while byte ptr [si] 3214 | inc si 3215 | .endw 3216 | sub si,ax 3217 | xchg ax,si 3218 | sub bx,ax 3219 | .if flag == 1 3220 | .while sword ptr bx > 0 3221 | push fill 3222 | call print_char 3223 | dec bx 3224 | .endw 3225 | .endif 3226 | 3227 | .while byte ptr [si] 3228 | lodsb 3229 | push ax 3230 | call print_char 3231 | .endw 3232 | 3233 | .while sword ptr bx > 0 3234 | push fill 3235 | call print_char 3236 | dec bx 3237 | .endw 3238 | retn 3239 | 3240 | printf endp 3241 | 3242 | ;--- skip "white space" characters 3243 | 3244 | _skipWhite proc 3245 | nextitem: 3246 | cmp @byte [si],' ' 3247 | je @F 3248 | cmp @byte [si],9 3249 | jne done 3250 | @@: 3251 | inc si 3252 | jmp nextitem 3253 | done: 3254 | ret 3255 | _skipWhite endp 3256 | 3257 | ;--- int _cdecl _memicmp(char * psz1, char * psz2, int len); 3258 | ;--- must preserve BX! 3259 | 3260 | _memicmp proc c uses si di psz1:word, psz2:word, len:word 3261 | 3262 | mov cx,len 3263 | mov si,psz2 3264 | mov di,psz1 3265 | cld 3266 | @@nextitem: 3267 | lodsb 3268 | mov ah,[di] 3269 | inc di 3270 | or al,20h 3271 | or ah,20h 3272 | sub al,ah 3273 | loopz @@nextitem 3274 | cbw 3275 | ret 3276 | _memicmp endp 3277 | 3278 | ;--- toupper() returns uppercase character 3279 | 3280 | _toupper proc 3281 | cmp al,'a' 3282 | jb @F 3283 | cmp al,'z' 3284 | ja @F 3285 | sub al,20h 3286 | @@: 3287 | ret 3288 | 3289 | _toupper endp 3290 | 3291 | ;--- convert a string into a DWORD, returned in EAX 3292 | ;--- also accept suffix G, M, K and adjust value then 3293 | 3294 | GetValue proc stdcall uses esi di commandline:word, base:word, usesuffix:word 3295 | 3296 | xor esi, esi ;result 3297 | mov bx,commandline 3298 | nextchar: 3299 | mov al,@byte [bx] 3300 | cmp al,'0' 3301 | jb @F 3302 | cmp al,'9' 3303 | ja @F 3304 | sub al,'0' 3305 | jmp @@I318 3306 | @@: 3307 | call _toupper 3308 | cmp al,'A' 3309 | jl @@FB316 3310 | sub al,55 ;'A' -> 10 3311 | @@I318: 3312 | movzx ecx, @word [base] 3313 | cmp cl,al 3314 | jle @@FB316 3315 | xchg eax,esi 3316 | mul ecx 3317 | xchg eax,esi 3318 | movzx eax,al 3319 | add esi,eax 3320 | inc bx 3321 | jmp nextchar 3322 | 3323 | @@FB316: 3324 | cmp @byte [usesuffix],0 3325 | je @@I322 3326 | mov al,[bx] 3327 | call _toupper 3328 | cmp al,'M' 3329 | je is_mega 3330 | cmp al,'G' ;'G' 3331 | je is_giga 3332 | cmp al,'K' 3333 | je is_kilo ;'K' 3334 | jmp @@I322 3335 | is_giga: 3336 | shl esi,10 3337 | is_mega: 3338 | shl esi,10 3339 | is_kilo: 3340 | mov @byte [bx],' ' 3341 | @@I322: 3342 | push esi 3343 | mov si,bx 3344 | mov di,commandline 3345 | push ds 3346 | pop es 3347 | @@nextchar: 3348 | lodsb 3349 | stosb 3350 | and al,al 3351 | jnz @@nextchar 3352 | pop eax 3353 | ret 3354 | 3355 | GetValue endp 3356 | 3357 | ;--- char * _stdcall FindCommand(char * pszSearchString) 3358 | ;--- parses the command line for a specific command. 3359 | ;--- If found, the command is removed and 3360 | ;--- the address behind that command is returned. Else, 0 is returned 3361 | ;--- SI -> commandline 3362 | 3363 | FindCommand proc stdcall uses di si pszCmd:ptr 3364 | 3365 | mov di,pszCmd 3366 | mov bx,di 3367 | .while byte ptr [di] 3368 | inc di 3369 | .endw 3370 | sub di,bx 3371 | xchg bx,di ;string len to BX 3372 | nextitem: 3373 | cmp @byte [si],0 3374 | je notfound 3375 | invoke _memicmp, si, di, bx 3376 | or ax,ax 3377 | je itemfound 3378 | inc si 3379 | jmp nextitem 3380 | itemfound: ;item has been found, now remove it from cmdline 3381 | push si 3382 | mov di,si 3383 | add si,bx 3384 | push ds 3385 | pop es 3386 | @@: 3387 | lodsb 3388 | stosb 3389 | and al,al 3390 | jnz @B 3391 | pop ax 3392 | jmp done 3393 | notfound: 3394 | xor ax,ax 3395 | done: 3396 | ret 3397 | 3398 | FindCommand endp 3399 | 3400 | ;--- ParseCmdLine() 3401 | ;--- si -> cmdline 3402 | 3403 | ParseCmdLine proc 3404 | 3405 | @DbgOutS <"ParseCmdLine enter",13,10> 3406 | 3407 | invoke printf, offset szStartup 3408 | 3409 | if ?TESTMEM 3410 | ;--- /TESTMEM:OFF option 3411 | invoke FindCommand, offset szTESTMEMOFF 3412 | or ax,ax 3413 | jne @F 3414 | ;--- do something useful here! 3415 | @@: 3416 | endif 3417 | 3418 | ;--- /V option 3419 | invoke FindCommand, offset szVerbose 3420 | or ax,ax 3421 | je @F 3422 | mov _startup_verbose,1 3423 | @@: 3424 | if ?LOG 3425 | ;--- /LOG option 3426 | invoke FindCommand, offset szLOG 3427 | or ax,ax 3428 | je @F 3429 | mov _xms_logging_enabled,1 3430 | @@: 3431 | endif 3432 | cmp _startup_verbose,0 3433 | je @F 3434 | invoke printf,offset szINTERFACE 3435 | @@: 3436 | 3437 | ;--- /NUMHANDLES=xx option 3438 | ;--- ensure value xx is within limits 3439 | invoke FindCommand, offset szNUMHANDLES 3440 | or ax,ax 3441 | je nonumhandles 3442 | invoke GetValue, ax, 10, 0 3443 | mov _xms_num_handles,ax 3444 | cmp _startup_verbose,0 3445 | je @F 3446 | invoke printf,offset szSelNumHandles,ax 3447 | @@: 3448 | cmp _xms_num_handles,8 3449 | jae @F 3450 | invoke printf,offset szNumHandlesLim1 3451 | mov _xms_num_handles,8 3452 | @@: 3453 | cmp _xms_num_handles,MAXHANDLES 3454 | jbe @F 3455 | invoke printf,offset szNumHandlesLim2 3456 | mov _xms_num_handles,MAXHANDLES 3457 | @@: 3458 | nonumhandles: 3459 | 3460 | ;--- /X2MAX32 and options 3461 | invoke FindCommand, offset szX2MAX32 3462 | or ax,ax 3463 | je @F 3464 | mov _x2max32,32767 ;7fffH 3465 | @@: 3466 | 3467 | ;--- /METHOD:xx option 3468 | invoke FindCommand, offset szMETHOD 3469 | or ax,ax 3470 | je nomethod 3471 | invoke GetA20Method, ax 3472 | mov _method,al 3473 | nomethod: 3474 | 3475 | ;--- /MAX=xx option 3476 | invoke FindCommand, offset szMAX 3477 | or ax,ax 3478 | je nomax 3479 | invoke GetValue, ax, 10, 1 3480 | mov [xms_max],eax 3481 | cmp _startup_verbose,0 3482 | je nomax 3483 | invoke printf,offset szMaximum, eax 3484 | nomax: 3485 | 3486 | ;--- /SUPERMAX=xx option 3487 | invoke FindCommand, offset szSUPERMAX 3488 | or ax,ax 3489 | je nosmax 3490 | invoke GetValue, ax, 10, 1 3491 | mov [xms_smax],eax 3492 | nosmax: 3493 | 3494 | ;--- /HMAMIN=xx option 3495 | invoke FindCommand, offset szHMAMIN 3496 | or ax,ax 3497 | je nohmamin 3498 | invoke GetValue, ax, 10, 1 3499 | mov _hma_min,ax 3500 | cmp _startup_verbose,0 3501 | je @F 3502 | invoke printf,offset szMinimum,ax 3503 | @@: 3504 | cmp _hma_min,63 3505 | jbe @F 3506 | invoke printf,offset szHMAMAX 3507 | mov _hma_min,63 3508 | @@: 3509 | shl _hma_min,10 3510 | nohmamin: 3511 | 3512 | call _skipWhite 3513 | cmp @byte [si],0 3514 | je @F 3515 | invoke printf,offset szIgnored, si 3516 | @@: 3517 | 3518 | @DbgOutS <"ParseCmdLine exit",13,10> 3519 | xor ax,ax 3520 | ret 3521 | 3522 | ParseCmdLine endp 3523 | 3524 | ;****************************************************************************** 3525 | ; initializes the driver. called only once! 3526 | ; may modify DI 3527 | ; In: DS:DI - pointer to init structure 3528 | ; Out: DS = DGROUP 3529 | 3530 | DoCommandline proc 3531 | 3532 | mov ax,ss 3533 | mov dx,sp 3534 | 3535 | push cs 3536 | pop ss 3537 | mov sp,offset _stacktop 3538 | pusha 3539 | push es 3540 | lds si,[di].init_strc.cmd_line 3541 | @@: 3542 | lodsb 3543 | cmp al,20h 3544 | ja @B 3545 | dec si 3546 | 3547 | sub sp,128 3548 | mov di,sp 3549 | push ss 3550 | pop es 3551 | mov cx,128-1 3552 | @@nextitem2: 3553 | lodsb 3554 | cmp al,13 3555 | jz @@done 3556 | cmp al,10 3557 | jz @@done 3558 | stosb 3559 | and al,al 3560 | loopnz @@nextitem2 3561 | @@done: 3562 | mov al,0 3563 | stosb 3564 | push ss 3565 | pop ds ;DS=DGROUP 3566 | 3567 | mov si,sp 3568 | call ParseCmdLine 3569 | add sp,128 3570 | pop es 3571 | popa 3572 | ;--- restore original stack, DOS requires it 3573 | mov ss,ax 3574 | mov sp,dx 3575 | ret 3576 | 3577 | DoCommandline endp 3578 | 3579 | dispmsg proc 3580 | push cs 3581 | pop ds 3582 | push dx 3583 | mov dx,offset dHimem 3584 | mov ah,9 3585 | int 21h 3586 | pop dx 3587 | mov ah,9 3588 | int 21h 3589 | ret 3590 | dispmsg endp 3591 | 3592 | ;--- set entry in handle array 3593 | ;--- ds:si -> handle array 3594 | ;--- edx = block addr in kB 3595 | ;--- ecx = block size in kB 3596 | ;--- updates ds:si to point to next (free) handle 3597 | ;--- updates global vars xms_max, xms_smax, xms_highest_addr 3598 | 3599 | seti15handle proc 3600 | 3601 | cmp edx, 1024 ;does the block start at 0x100000? 3602 | jnz @F 3603 | if ?PD110000 3604 | add edx, 64+4 ;then exclude the first 64 kB for HMA + 4 kB for page directory 3605 | sub ecx, 64+4 3606 | jc exit 3607 | else 3608 | add edx, 64 3609 | sub ecx, 64+4 3610 | jc exit 3611 | lea eax, [edx+ecx] ;place the page directory at the end of the first block 3612 | shl eax, 10 3613 | mov [PageDir],eax 3614 | endif 3615 | or hma_exists,1 3616 | @@: 3617 | cmp edx, 400000h ;beyond 4GB? 3618 | jc @F 3619 | sub xms_smax, ecx ;/SUPERMAX option set? 3620 | jnc beyond4g 3621 | add ecx, xms_smax ;limit to maximum 3622 | mov xms_smax,0 3623 | jecxz exit 3624 | jmp beyond4g 3625 | @@: 3626 | sub xms_max, ecx ;/MAX option set? 3627 | jnc @F 3628 | add ecx, xms_max ;limit to maximum 3629 | mov xms_max,0 3630 | jecxz exit 3631 | @@: 3632 | lea eax,[edx+ecx] 3633 | shl eax,10 3634 | dec eax 3635 | cmp eax, xms_highest_addr 3636 | jb @F 3637 | mov xms_highest_addr, eax 3638 | @@: 3639 | beyond4g: 3640 | mov [si].XMS_HANDLE.xh_flags, XMSF_FREE 3641 | mov [si].XMS_HANDLE.xh_locks, 0 3642 | mov [si].XMS_HANDLE.xh_baseK, edx 3643 | mov [si].XMS_HANDLE.xh_sizeK, ecx 3644 | add si, sizeof XMS_HANDLE 3645 | exit: 3646 | ret 3647 | seti15handle endp 3648 | 3649 | ;-- look for extended memory, int 15h, ax/ah 0e820h 3650 | ;-- in: ds:si->handle array 3651 | ;-- modifies eax, ebx, ecx, edx, si, di 3652 | ;-- out: ds:si->next free handle 3653 | 3654 | geti15mem proc 3655 | 3656 | local mmap:E820MAP 3657 | 3658 | @DbgOutS <"get15mem: get extended memory with int 15, E820",13,10> 3659 | 3660 | ; try 0e820h only! 3661 | 3662 | xor ebx,ebx 3663 | push ss 3664 | pop es 3665 | 3666 | e820_nextitem: ; ebx offset is updated with each successive int 15h 3667 | 3668 | mov edx,SMAP 3669 | mov ecx, sizeof E820MAP 3670 | lea di, mmap 3671 | xor eax,eax 3672 | mov mmap.baselow,eax ; insurance against buggy BIOS 3673 | mov mmap.basehigh,eax 3674 | mov mmap.lenlow,eax 3675 | mov mmap.lenhigh,eax 3676 | mov mmap.type_,eax 3677 | mov ax,0e820h 3678 | clc 3679 | int 15h 3680 | setc dl ; keep carry flag status 3681 | cmp eax,SMAP 3682 | jne e820_bad ; failure 3683 | cmp dl,1 3684 | je e820_done ; CF doesn't have to signal fail, can just mean done 3685 | 3686 | cmp ecx,sizeof E820MAP ; didn't return all the info needed, assume done 3687 | jb e820_done 3688 | 3689 | cmp mmap.type_,1 ; memory available to OS 3690 | jne e820_itemdone 3691 | 3692 | mov edx, mmap.baselow 3693 | mov eax, mmap.basehigh 3694 | cmp eax,0 ; memory beyond 4 GB? 3695 | jnz @F 3696 | cmp edx, 100000h ; has to live in extended memory 3697 | jb e820_itemdone 3698 | @@: 3699 | test eax, dwMaxHigh ;block start beyond 4GB/1TB? 3700 | jnz e820_itemdone 3701 | shrd edx, eax, 10 3702 | mov ecx, mmap.lenlow 3703 | mov eax, mmap.lenhigh 3704 | test eax, dwMaxHigh ;block size > 4GB/1TB? 3705 | jnz e820_itemdone 3706 | shrd ecx, eax, 10 3707 | mov eax, edx 3708 | add eax, ecx ;block crossing 4TB (XMS v3 limit)? 3709 | jc @F 3710 | dec eax 3711 | test eax, 0C0000000h;block crossing 1TB barrier (PSE-36 limit)? 3712 | jz e820_itemok 3713 | @@: 3714 | mov ecx, 0C0000000h ;truncate the block to ensure it fits in the first TB 3715 | sub ecx, edx 3716 | e820_itemok: 3717 | call seti15handle ;set xms block, sizeK in ECX, baseK in EDX 3718 | e820_itemdone: 3719 | cmp ebx,0 ;was this the last entry? 3720 | jnz e820_nextitem 3721 | e820_bad: 3722 | e820_done: 3723 | @@exit: 3724 | ret 3725 | geti15mem endp 3726 | 3727 | 3728 | hascpuid proc 3729 | push di 3730 | mov di,sp 3731 | and sp,0fffch ;make sure we don't get an exc 11 (if AM set in CR0) 3732 | pushfd ; save EFlags 3733 | cli 3734 | pushd 240000h ; set AC bit in eflags, reset IF 3735 | popfd ; pop extended flags 3736 | pushfd ; push extended flags 3737 | pop ax 3738 | pop ax ; get HiWord(EFlags) into AX 3739 | popfd ; restore EFlags 3740 | mov sp,di 3741 | pop di 3742 | test al,04 ;AC bit set? 3743 | je @F 3744 | test al,20h ;CPUID bit set? 3745 | jz @F 3746 | clc 3747 | ret 3748 | @@: 3749 | stc 3750 | ret 3751 | hascpuid endp 3752 | 3753 | ;--- driver init. this proc should be last 3754 | ;--- since it initializes the handle table 3755 | ;--- ds:di = request_ptr 3756 | 3757 | initialize proc 3758 | 3759 | pushf 3760 | pushad 3761 | cld 3762 | @DbgOutS <"initialize enter",13,10> 3763 | 3764 | mov ax,3000h ; get DOS version number 3765 | int 21h 3766 | cmp al,3h ; we need at least 3.00 3767 | jnc @@dosok 3768 | mov dx,offset old_dos 3769 | @@error_exit: ; error msgs old_dos, xms_twice, a20_error, vdisk_detected, xms_tosmall 3770 | call dispmsg 3771 | mov dx,offset error_msg 3772 | mov ah,9 3773 | int 21h 3774 | popad 3775 | popf 3776 | ret 3777 | @@dosok: 3778 | mov ax,4300h ; check if XMS is already 3779 | int 2fh ; installed 3780 | cmp al,80h 3781 | mov dx,offset xms_twice 3782 | je @@error_exit 3783 | 3784 | call DoCommandline ; parse commandline 3785 | 3786 | ;--- now DS=DGROUP 3787 | 3788 | call hascpuid 3789 | jc @F 3790 | xor eax,eax 3791 | inc eax 3792 | .586 3793 | cpuid 3794 | .386 3795 | bt edx,CPUID1_PSE36 3796 | jnc @F 3797 | mov byte ptr dwMaxHigh,0; allow memory up to 000000ff.ffffffffh 3798 | @@: 3799 | 3800 | @DbgOutS <"initialize: processing selected A20 method",13,10> 3801 | 3802 | call seta20method ; modifies SI! 3803 | mov dx,offset a20_error 3804 | jc @@error_exit 3805 | 3806 | call _install_check_vdisk ; is VDISK installed? 3807 | mov dx,offset vdisk_detected 3808 | jz @@error_exit 3809 | 3810 | mov ax,cs ; setup descriptors 3811 | ; mov [code_seg],ax ; eliminate relocation entry 3812 | if ?RESTCSREG 3813 | mov RmCS,ax 3814 | endif 3815 | movzx eax,ax 3816 | shl eax,4 3817 | ; or @dword [code16dsc+2],eax 3818 | add @dword [gdt32+2],eax 3819 | if ?CATCHEXC 3820 | add @dword [idt32+2],eax 3821 | mov code16dsc.base00_15,ax 3822 | shr eax,16 3823 | mov code16dsc.base16_23,al 3824 | endif 3825 | 3826 | mov ax,352Fh ; getvect --> es:bx 3827 | int 21h 3828 | mov @word [old_int2f+0],bx 3829 | mov @word [old_int2f+2],es 3830 | mov ax,3515h ; getvect --> es:bx 3831 | int 21h 3832 | mov @word [old_int15+0],bx 3833 | mov @word [old_int15+2],es 3834 | 3835 | ; ***************** handle LOG mode 3836 | 3837 | mov si, offset trace_driver_end 3838 | ifdef _DEBUG 3839 | jmp @F 3840 | else 3841 | if ?LOG 3842 | cmp [_xms_logging_enabled],0 3843 | jne @F 3844 | endif 3845 | endif 3846 | 3847 | if ?LOG 3848 | mov di, offset dispatcher_log_entry 3849 | lea si, [di+3] 3850 | mov cx, offset dispatcher_log_exit - (offset dispatcher_log_entry + 2) 3851 | mov [dispatcher_log_exit],0cbh ; patch call to RETF 3852 | push ds 3853 | pop es 3854 | rep movsb 3855 | mov si, di 3856 | add si, 3 3857 | and si, not 3 3858 | endif 3859 | @@: 3860 | mov @word xms_handle_table.xht_pArray+0, si 3861 | mov @word xms_handle_table.xht_pArray+2, ds 3862 | 3863 | @DbgOutS <"initialize: init handle array",13,10> 3864 | 3865 | call geti15mem ; look for extended memory via int 15h, ax=e820h 3866 | cmp hma_exists,1 3867 | mov dx,offset xms_toosmall 3868 | jnz @@error_exit 3869 | 3870 | cmp _startup_verbose,0 3871 | je @F 3872 | invoke printf,offset szPageDir,PageDir 3873 | @@: 3874 | 3875 | ; we clear the handle table, as this may overwrite part of the code above 3876 | ; but must not erase itself 3877 | 3878 | IF ($ - startoftext) le MAXHANDLES * sizeof XMS_HANDLE 3879 | 3880 | .err 3881 | 3882 | ENDIF 3883 | 3884 | mov ax, _xms_num_handles 3885 | mov xms_handle_table.xht_numhandles,ax 3886 | mov cx,sizeof XMS_HANDLE 3887 | mul cx 3888 | add ax,@word xms_handle_table.xht_pArray+0 3889 | mov bx,ax 3890 | xor eax,eax 3891 | @@: 3892 | mov [si].XMS_HANDLE.xh_flags,XMSF_INPOOL ; handle not used 3893 | mov [si].XMS_HANDLE.xh_locks,al ; clear locks 3894 | mov [si].XMS_HANDLE.xh_baseK,eax 3895 | mov [si].XMS_HANDLE.xh_sizeK,eax 3896 | add si,sizeof XMS_HANDLE 3897 | cmp si,bx 3898 | jb @B 3899 | 3900 | @DbgOutS <"initialize: set int vectors 15h and 2Fh",13,10> 3901 | 3902 | mov ax,252Fh ; install own INT2Fh 3903 | mov dx,offset int2f_handler 3904 | int 21h 3905 | mov ax,2515h ; install own INT15h 3906 | mov dx,offset int15_handler 3907 | int 21h 3908 | 3909 | ; driver init done 3910 | 3911 | les di,[request_ptr] 3912 | mov @word es:[di+0].init_strc.end_addr,si 3913 | mov @word es:[di+2].init_strc.end_addr,cs ; set end address 3914 | mov es:[di].request_hdr.status,STATUS_OK ; we're alright 3915 | 3916 | @@exit: 3917 | @DbgOutS <"initialize exit",13,10> 3918 | popad 3919 | popf 3920 | ret 3921 | initialize endp 3922 | 3923 | ;****************************************************************************** 3924 | ; init_interrupt routine. called by DOS right after the strategy routine to 3925 | ; process the incoming job. also used to initialize the driver. 3926 | 3927 | init_interrupt proc far 3928 | 3929 | push di 3930 | push ds 3931 | @DbgOutS <"init interrupt enter",13,10> 3932 | 3933 | lds di,cs:[request_ptr] ; load address of request header 3934 | 3935 | cmp [di].request_hdr.cmd,CMD_INIT; do we have to initialize? 3936 | jne @@done 3937 | mov @word [di].init_strc.end_addr+0,0 ; init to error 3938 | mov @word [di].init_strc.end_addr+2,cs 3939 | mov [di].request_hdr.status, STATUS_BAD 3940 | call check_cpu ; do we have at least a 386? 3941 | jz @F 3942 | mov dx,offset no_386 3943 | call dispmsg 3944 | jmp @@done 3945 | @@: 3946 | push es 3947 | call initialize 3948 | pop es 3949 | mov @word cs:[6], offset dummyretf; new strategy offset 3950 | mov @word cs:[8], offset dummyretf; new interrupt offset 3951 | @@done: 3952 | ; lds si,[request_ptr] ; return this to DOS (why?) 3953 | 3954 | @DbgOutS <"init interrupt exit",13,10> 3955 | pop ds 3956 | pop di 3957 | ret 3958 | init_interrupt endp 3959 | 3960 | ;********************************************* 3961 | ; startpoint when executing as EXE 3962 | ;********************************************* 3963 | 3964 | startexe proc 3965 | 3966 | push cs 3967 | pop ds 3968 | invoke printf, offset szStartup 3969 | invoke printf, offset szHello 3970 | mov ah,04ch 3971 | int 21h 3972 | startexe endp 3973 | 3974 | _TEXT ends 3975 | 3976 | end startexe 3977 | --------------------------------------------------------------------------------