For one of my projects, I'd like to be able to detect upper memory blocks and, if there's one of sufficient size available, relocate the main code into it and execute from there, freeing up the whole TPA for data. Problem is, there isn't a particularly good introduction to upper-memory programming available out there. Ralf Brown's Interrupt List has some documentation on the structures involved, and I've found one tutorial by a virus hobbyist showing how to find blocks, but that involves a lot of poking around system data structures, and when I try it it winds up off in never-never-land - it seems to have gotten entirely the wrong start address for the beginning of the UMB chain, as none of the entries have the marker that indicates end-of-list set correctly.

(Here's the code I'm using at the moment. It prints one segment pointer and quits on Windows XP, without printing Z for end-of-list or M for no, and prints a few kilobytes of garbage on DOSBox. Haven't got a chance to try it on my actual DOS machine yet.)

	; umb.asm - traverses the UMB chain
org 0x100

start:
mov ax, 0x3306 ; get true DOS version
int 0x21
inc al ; will wrap from 0xFF -> 0x00 if DOS version is less than 5.0
jz baddos
mov ah, 0x52 ; get pointer to the List of Lists
int 0x21

; ES:BX now contains the pointer
lds si, ; get pointer to disk buffer
mov ax, ; get pointer to first MCB
inc ax ; will wrap from 0xFFFF -> 0x0000 if no UMBs
jz noumb
dec ax

xor si, si
; for now, simply traverse the list until the end
lp: mov ds, ax

; DEBUG - prints a doggerel-hex (0123456789:;<=>?) version of the segment pointer
push ax
mov cx, ax
mov ah, 0x02

mov dl, ch
shr dl, 1
shr dl, 1
shr dl, 1
shr dl, 1
add dl, '0'
int 0x21
shl cx, 1
shl cx, 1
shl cx, 1
shl cx, 1

mov dl, ch
shr dl, 1
shr dl, 1
shr dl, 1
shr dl, 1
add dl, '0'
int 0x21
shl cx, 1
shl cx, 1
shl cx, 1
shl cx, 1

mov dl, ch
shr dl, 1
shr dl, 1
shr dl, 1
shr dl, 1
add dl, '0'
int 0x21
shl cx, 1
shl cx, 1
shl cx, 1
shl cx, 1

mov dl, ch
shr dl, 1
shr dl, 1
shr dl, 1
shr dl, 1
add dl, '0'
int 0x21
shl cx, 1
shl cx, 1
shl cx, 1
shl cx, 1

pop ax

; /DEBUG

mov dl,
mov ah, 0x02
int 0x21
mov dl,
cmp dl, 'Z'
jnz el
mov ax, ds
;inc ax
mov bx,
add ax, bx
jmp short lp
el: mov ah, 0x4c
int 0x21

baddos:
noumb:
mov dx, error
mov ah, 0x09
int 0x21

mov ah, 0x4c
int 0x21

error: db 'ERROR',0xd,'$'


Does anyone have a good tutorial on how to detect UMBs and allocate them? This would be handy, but I don't want to do something crash-prone just to save a few kilobytes.
Posted on 2012-05-30 19:22:44 by commodorejohn
Okay, well, silly me, I went and did some further research and it turns out the standard memory-allocation calls can be set to use upper memory when available. This code works on XP; DOSBox doesn't want to play along, but it doesn't spew gibberish. (Still haven't gotten to try it on my 386 box yet.)

	; umb2.asm - attempts to use normal DOS memory allocation to get a 16KB UMB
org 0x100

start:
mov ax, 0x3306 ; get true DOS version
int 0x21
inc al ; will wrap from 0xFF -> 0x00 if DOS version is less than 5.0
jz baddos

mov ax, 0x5803 ; set UMB link state
mov bx, 0x0001 ; add UMBs to DOS memory chain
int 0x21
jc nolink

mov ax, 0x5801 ; set memory-allocation strategy
mov bx, 0x0041 ; high memory only, best fit
int 0x21
jc badstrategy

mov ah, 0x48 ; allocate memory
mov bx, 0x0400 ; 16KB?
int 0x21
jc nomemory

push ax
mov ah, 0x09
mov dx, success1
int 0x21
pop bx
mov es, bx
call printhex
mov ah, 0x09
mov dx, success2
int 0x21

; set things back to the way they were
mov ah, 0x49
int 0x21

mov ax, 0x5801 ; set memory-allocation strategy
mov bx, 0x0000 ; high memory (only?), best fit
int 0x21

mov ax, 0x5803 ; set UMB link state
mov bx, 0x0000 ; add UMBs to DOS memory chain
int 0x21

mov ah, 0x4c
int 0x21

baddos:
mov ah, 0x09
mov dx, error1
int 0x21
mov ah, 0x4c
int 0x21

nolink:
mov ah, 0x09
mov dx, error2
int 0x21
mov ah, 0x4c
int 0x21

badstrategy:
mov ah, 0x09
mov dx, error3
int 0x21

; set things back to the way they were
mov ax, 0x5803 ; set UMB link state
mov bx, 0x0000 ; add UMBs to DOS memory chain
int 0x21

mov ah, 0x4c
int 0x21

nomemory:
push bx
mov ah, 0x09
mov dx, error4
int 0x21
pop bx
call printhex
mov ah, 0x09
mov dx, error5
int 0x21

; set things back to the way they were
mov ax, 0x5801 ; set memory-allocation strategy
mov bx, 0x0000 ; high memory (only?), best fit
int 0x21

mov ax, 0x5803 ; set UMB link state
mov bx, 0x0000 ; add UMBs to DOS memory chain
int 0x21

mov ah, 0x4c
int 0x21

printhex: ; takes BX = 16-bit hexadecimal number
mov cx, bx
mov bx, hextable
mov ah, 0x02

mov al, ch
shr al, 1
shr al, 1
shr al, 1
shr al, 1
xlatb
mov dl, al
int 0x21
shl cx, 1
shl cx, 1
shl cx, 1
shl cx, 1

mov al, ch
shr al, 1
shr al, 1
shr al, 1
shr al, 1
xlatb
mov dl, al
int 0x21
shl cx, 1
shl cx, 1
shl cx, 1
shl cx, 1

mov al, ch
shr al, 1
shr al, 1
shr al, 1
shr al, 1
xlatb
mov dl, al
int 0x21
shl cx, 1
shl cx, 1
shl cx, 1
shl cx, 1

mov al, ch
shr al, 1
shr al, 1
shr al, 1
shr al, 1
xlatb
mov dl, al
int 0x21
shl cx, 1
shl cx, 1
shl cx, 1
shl cx, 1

ret

success1: db 'Successfully allocated 0x4000 bytes at segment 0x$'
success2: db '.',0xd,'$'
error1: db 'DOS 4.0 and lower do not support UMB!',0xd,'$'
error2: db 'Could not link UMBs into DOS memory chain.',0xd,'$'
error3: db 'Could not set memory-allocation strategy.',0xd,'$'
error4: db 'Could not allocate memory. Largest block available is 0x$'
error5: db '0 bytes.',0xd,'$'
hextable: db '0123456789ABCDEF'
Posted on 2012-05-30 20:09:51 by commodorejohn
Wow, upper memory blocks, yes, I was using them twenty years ago and I have published an ASM source, perhaps it might help you: http://vitsoft.info/tsrup.htm
Posted on 2012-06-01 17:43:32 by vit$oft