1
0
mirror of synced 2026-04-30 13:42:03 +00:00
Files
MicroCoreLabs.Projects/XTMax/Drivers/USEUMB/USE!UMBS.ASM
Matthieu Bucchianeri 0fbbc0070c Add support for DOS Upper Memory Blocks (UMBs). (#38)
* Import unmodified USE!UMBS source.

From https://forum.vcfed.org/index.php?threads/loading-dos-high-on-a-xt.32320/

* Customize the UMB Driver prompts.

* Refactor memory management. Allow activation of EMS/UMB on demand.

* Update the XTEMM and XTUMBS drivers to map their ranges.

* Add instructions for all the drivers.
2025-03-07 22:00:26 -08:00

471 lines
11 KiB
NASM

; 1 tab = 4 spaces
;
; This is a rewrite of Marco van Zwetselaar's USE!UMBS.SYS v2.0 from 1991.
;
; Changes in this version (v2.2);
; * UMBs are now initialized to avoid parity errors.
; * Minor size optimizations.
;
; Changes in the previous version (v2.1);
; * This file assembles in NASM.
; * Optimizations to reduce memory usage.
; * Command line parameters are now used in CONFIG.SYS to specify address ranges for UMBs.
;
; Example for a single 128 KB UMB starting at segment D000h:
;
; DEVICE=USE!UMBS.SYS D000-F000
;
; Please report bugs to me at krille_n_@hotmail.com
;
; Thanks!
;
; Krister Nordvall
;
[cpu 8086]
;--------------------------------------------------------------------
; Skips the immediately following 2 byte instruction by using it
; as an immediate value to a dummy instruction.
; Destroys the contents of %1.
;
; SKIP2B
; Parameters:
; %1: Any 16 bit general purpose register or F for flags.
; Returns:
; Nothing
; Corrupts registers:
; %1
;--------------------------------------------------------------------
%macro SKIP2B 1
%ifidni %1, f
db 03Dh ; Opcode byte for CMP AX, <immed>
;db 0A9h ; Alt. version TEST AX, <immed>
%elifidni %1, ax
db 0B8h ; Opcode byte for MOV AX, <immed>
%elifidni %1, cx
db 0B9h ; Opcode byte for MOV CX, <immed>
%elifidni %1, dx
db 0BAh ; Opcode byte for MOV DX, <immed>
%elifidni %1, bx
db 0BBh ; Opcode byte for MOV BX, <immed>
%elifidni %1, sp
db 0BCh ; Opcode byte for MOV SP, <immed>
%elifidni %1, bp
db 0BDh ; Opcode byte for MOV BP, <immed>
%elifidni %1, si
db 0BEh ; Opcode byte for MOV SI, <immed>
%elifidni %1, di
db 0BFh ; Opcode byte for MOV DI, <immed>
%else
%error "Invalid parameter passed to SKIP2B"
%endif
%endmacro
TAB equ 9
CR equ 13
SPACE equ 32
bRH_Command equ 2
wRH_Status equ 3
fpRH_BreakAddress equ 14
fpRH_CommandLine equ 18
org 0
fpNextDeviceDriver dd -1
wDeviceAttributeWord dw 0E000h
wOffsetStrategyRoutine dw StrategyRoutine
wOffsetInterruptRoutine dw InterruptRoutine
sDeviceName db 'ZwetsUMB'
fpRequestHeader dd 0
Interrupt2FhHandler:
cmp ah, 43h
je SHORT RequestIsForMe
db 0EAh ; Far jump opcode
fpOldInterrupt2FhHandler:
dd 0 ; Pointer filled in during initialization
RequestIsForMe:
cmp al, 10h
jne SHORT ReturnHandlerExists
mov bx, UMBController
push cs
pop es
iret
ReturnHandlerExists:
mov al, 80h
iret
StrategyRoutine:
mov [cs:fpRequestHeader], bx
mov [cs:fpRequestHeader+2], es
retf
InterruptRoutine:
push es
push bx
push ax
les bx, [cs:fpRequestHeader]
mov al, [es:bx+bRH_Command]
test al, al
jz SHORT Install
cmp al, 10h
jbe SHORT .NotImplemented
mov ax, 8003h ; "Unknown command"
SKIP2B f
.NotImplemented:
xor ax, ax
Return:
or ax, 100h ; Set Done bit
mov [es:bx+wRH_Status], ax
pop ax
pop bx
pop es
retf
UMBController:
; The first 5 bytes of Himem handlers are special and must not be changed.
jmp SHORT .CheckIfUMBorXMSrequest
nop
nop
nop
.CheckIfUMBorXMSrequest:
cmp ah, 10h
je SHORT UMBrequest
JMPtoOurXMShandler:
jmp SHORT XMSrequest ; This jump is patched out during init if an existing XMS handler is detected
fpOldXMShandler equ JMPtoOurXMShandler+1
db 0
db 0
db 0
UMBrequest:
push si
push cx
push bx
SKIP2B si
.wOffsetFreeUMBs:
dw 0 ; Offset filled in during init
xor cx, cx
xchg dx, cx ; DX = 0, CX = Requested block size
.Search:
cs
lodsw ; Load start address of range
test ax, ax ; Are we at the end of the list?
jz SHORT .NoBlockFound ; Jump if so
mov bx, ax ; Segment is returned in BX
inc ax ; Block already given away?
cs
lodsw ; Load length of block
jz SHORT .Search ; If yes, get next block
cmp dx, ax ; Is it larger than the largest found so far?
ja SHORT .CheckSize ; If not, check size to see if it's large enough
mov dx, ax ; It is so save it as the largest so far
.CheckSize:
cmp cx, ax ; Large enough to fulfill request?
ja SHORT .Search ; Jump if not
mov WORD [cs:si-4], 0FFFFh ; Mark as given away
mov dx, 1 ; Return Success in AX and size of block in DX
xchg dx, ax
pop cx ; Remove BX from stack
jmp SHORT .Return
.NoBlockFound:
pop bx ; Restore BX
mov bl, 0B0h ; Return "Smaller UMB available" in BL
test dx, dx ; Size of largest block found = 0?
jnz SHORT .Return ; If not, assumption was correct
inc bx ; Yes, return "No UMBs available" instead
.Return:
pop cx
pop si
retf
XMSrequest:
test ah, ah ; Get XMS version?
jnz SHORT .NotGetVersion
xor bx, bx ; Internal revision number
cwd ; HMA not present
jmp SHORT .Return
.NotGetVersion:
cmp ah, 8 ; Query extended memory?
jne SHORT .NotQueryExtendedMem
cwd ; 0 KB extended memory
.NotQueryExtendedMem:
mov bl, 80h ; Error: Not implemented
.Return:
xor ax, ax ; Always return failure
retf
Install:
push cx
push dx
push ds
push si
push cs
pop ds
mov ah, 9
mov dx, s$Hello1
int 21h
mov dx, s$Author
int 21h
; Is there an XMS handler already?
mov ax, 4300h
int 2Fh
cmp al, 80h
jne SHORT .OKtoInstall ; No, it's OK to install (with our handler)
; Yes, patch out the short unconditional jump to our XMS handler
mov BYTE [JMPtoOurXMShandler], 0EAh ; 'jmp far ...'
; Then get the address to the old handler and store it
mov ax, 4310h
int 2Fh
mov [fpOldXMShandler], bx
mov [fpOldXMShandler+2], es
; To reduce memory usage we also move the breakpoint to exclude our XMS handler
mov WORD [.wOffsetBreakpoint], XMSrequest
; Tell the user we found an existing handler
mov ah, 9
mov dx, s$XMMfound
int 21h
; Check if it services UMBs by trying to allocate far too much
mov ah, 10h
cwd
dec dx
call FAR [fpOldXMShandler]
; Assume we are not needed
mov dx, s$NotInstalled
xor ax, ax
cmp bl, 80h ; Error: Not implemented?
je SHORT .OKtoInstall
jmp .StoreBreakPointer
.OKtoInstall:
; Get the breakpoint (Install or XMSrequest) to BX and store it as the start of the FreeUMBs list
mov bx, [.wOffsetBreakpoint]
mov [UMBrequest.wOffsetFreeUMBs], bx
; Time to interpret the command line in CONFIG.SYS
lds si, [fpRequestHeader]
lds si, [si+fpRH_CommandLine]
push cs ; For restoring DS later
mov cx, 4 ; Hex digit count in CH = 0, Shift count in CL = 4
xor ah, ah ; WORD parameter count in AH
; First scan until we find a whitespace (tab or space) to find the start of parameters
.SearchForWhiteSpace:
lodsb
cmp al, CR
je .EndOfLineEncountered
cmp al, SPACE
je SHORT .SearchForParameter
cmp al, TAB
jne SHORT .SearchForWhiteSpace
; We've found the first whitespace which means we've reached the end of the path+filename
.SearchForParameter:
lodsb
cmp al, CR
je SHORT .EndOfLineEncountered
cmp al, SPACE
je SHORT .SearchForParameter
cmp al, TAB
je SHORT .SearchForParameter
; We have the first byte of a parameter
dec si
.NextHexDigit:
lodsb
inc ch
cmp al, '9'
jbe SHORT .NotAtoF
and al, ~32
cmp al, 'F'
ja SHORT .ParameterError
cmp al, 'A'
jb SHORT .ParameterError
sub al, 7
.NotAtoF:
sub al, '0'
jb SHORT .ParameterError
shl dx, cl
or dl, al
and ch, 3
jnz SHORT .NextHexDigit
; We have a complete WORD in DX
inc ah ; Increment WORD count
sahf ; WORD count Odd or Even?
jc SHORT .FirstWordOfParameter
sub dx, [cs:bx-2] ; Convert end of range to length
jbe SHORT .ParameterError ; End of range below or equal to start of range
; Program the range
push ax
push bx
push cx
mov ax, [cs:bx-2]
mov bx, dx
mov dx, 26dh ; UMB base register
out dx, ax
mov dx, 26fh ; UMB pages count register
mov ax, bx
mov cl, 7
shr ax, cl ; bytes to 2K pages
out dx, al
shl ax, cl ; 2K pages (back) to bytes
mov dx, ax
pop cx
pop bx
pop ax
test dx, dx
jz SHORT .ParameterError ; Range was smaller than 2KB
.FirstWordOfParameter:
mov [cs:bx], dx
inc bx
inc bx
lodsb
sahf
jc SHORT .CheckIfDash
cmp al, CR
je SHORT .EndOfLineEncountered
cmp al, SPACE
je SHORT .SearchForParameter
cmp al, TAB
je SHORT .SearchForParameter
SKIP2B ax ; Fall through to .ParameterError
.CheckIfDash:
cmp al, '-'
je SHORT .NextHexDigit
.ParameterError:
pop ds ; DS = CS
mov dx, s$ParamError
xor ax, ax
jmp SHORT .StoreBreakPointer
.EndOfLineEncountered:
test ah, ah
jz SHORT .ParameterError ; No parameters at all
pop ds ; DS = CS
mov WORD [bx], 0 ; End of Free UMBs block marker
inc bx
inc bx
mov [.wOffsetBreakpoint], bx
; We need to initialize the UMBs by writing to every address to avoid parity errors later when reading
mov si, [UMBrequest.wOffsetFreeUMBs]
push di
.InitNextBlock:
lodsw ; Segment address of UMB to AX
test ax, ax
jz SHORT .AllBlocksInitialized
xchg bx, ax ; Save address in BX
lodsw ; Block size in paragraphs to AX
xor dx, dx
xchg dx, ax ; DX = Paragraph count, AX = Zero
xor di, di ; Point ES:DI to start of UMB
.InitNextSegment:
mov es, bx
cmp dh, 10h ; Block larger than or equal to a full segment?
jb SHORT .LessThan64KB
mov cx, 8000h ; We need to write (at least) a full 64 KB segment
add bh, 10h ; Add 64 KB to the segment address for the next iteration (if any)
sub dh, 10h ; Subtract 64 KB from the paragraph count
rep stosw
jmp SHORT .InitNextSegment
.LessThan64KB:
mov cl, 3
shl dx, cl ; Paragraphs to WORDs
mov cx, dx ; WORD count to CX
rep stosw
jmp SHORT .InitNextBlock
.AllBlocksInitialized:
pop di
; Get handler for Interrupt 2Fh and store it
mov ax, 352Fh
int 21h
mov [fpOldInterrupt2FhHandler], bx
mov [fpOldInterrupt2FhHandler+2], es
; Then hook it
mov ax, 252Fh
mov dx, Interrupt2FhHandler
int 21h
; Tell the user we are installed
mov dx, s$Installed
SKIP2B ax
.wOffsetBreakpoint:
dw Install ; Default breakpoint offset is Install
.StoreBreakPointer:
les bx, [fpRequestHeader]
mov [es:bx+fpRH_BreakAddress], ax
mov [es:bx+fpRH_BreakAddress+2], cs
mov ah, 9
int 21h ; Print Installed/NotInstalled/ParamError
xor ax, ax ; No errors
pop si
pop ds
pop dx
pop cx
jmp Return
s$Hello1 db "XTUMBS: UMB Manager for XTMax v2.2",0Dh,0Ah,'$'
s$Author db " Based on rewritten USE!UMBS by Krister Nordvall",0Dh,0Ah
db " Based on original work by Marco van Zwetselaar",0Dh,0Ah,'$'
s$XMMfound db "Found XMS Manager.",0Dh,0Ah,'$'
s$Installed db "UMB Manager Installed.",0Dh,0Ah,0Ah,'$'
s$NotInstalled db "Not Installed: UMB is already managed!",0Dh,0Ah,0Ah,'$'
s$ParamError db "Not Installed: Invalid Command Line!",0Dh,0Ah,0Ah,'$'