Source file: /~heha/hs/dos/dosmisc.zip/SRC/UMBADD$.ASM

; HIMEM-Erweiterung auf UMBs für MS-DOS 5;
; Original (c) Peter Siering, erschienen in c't 8/91
; Erweiterte Version von as, 15-AUG-91. Aufruf mit:

; DEVICE=UMBADD.SYS /I=aaaa-bbbb /I=cccc-dddd....

; Beispiel: DEVICE=UMBADD.SYS /I=BC00-C000 /I=C000-C800

	  .MODEL SMALL
	  .CODE

INITREQUEST STRUC   ; Parameterblock für INIT
  irLength        db ?    ; Gesamtumfang des Parameterblocks
  irUnit          db ?    ; nicht verwendet
  irFunction      db ?    ; Funktion $00, d.h. Init
  irStatus        dw ?    ; Ergebniscode der Initialisierung
  irReserved      db 8 dup (?) ; "reserviert", nicht verwendet
  irUnits         db ?    ; Zahl der vom Treiber bedienten Geräte
  irEndAddress    dd ?    ; höchste verfügbare/belegte Speicheradresse
  irParamAddress  dd ?    ; Adresse der Kommandozeile (device=...)
  irDriveNumber   db ?    ; erste nicht belegte Laufwerks-Kennz.
  irMessageFlag   db ?    ; Flag für Fehlermeldungen
INITREQUEST ENDS

; Gerätetreiberkopf
DNext     dd -1           ; Adresse des nächsten Treibers: FFFF:FFFF
DAttr     dw 0E000h       ; Attribute
DStrat    dw OFFSET Strat ; Offset der "Strategie"-Routine
DIntr     dw OFFSET Intr  ; Offset der "Interrupt"-Routine
DName     db 'UMBADDXX'   ; Name (oder Units)

ParamAddr    dd ?            ; Zwischenspeicher für INIT-Parameterblock

; Strategie-Routine, speichert lediglich ES:BX (Parameterblock-Adresse)
Strat     PROC FAR
	  mov Word Ptr [ParamAddr],bx
	  mov Word Ptr [ParamAddr+2],es
	IDEAL
	push	ds
	push	es
	push	dx
	xor	dx,dx
	mov	ds,dx
	les	dx,[9*4]
	mov	[cs:OldInt9Ofs],dx
	mov	[cs:OldInt9Seg],es
	mov	[word 9*4],offset NewInt9
	mov	[9*4+2],cs
	pop	dx
	pop	es
	pop	ds
	MASM
	  retf
Strat     ENDP

; Interrupt-Routine, definiert nur die INIT-Funktion
Intr      PROC FAR
	  push ax         ; mehr als diese drei Register
	  push bx         ; werden momentan nicht gebraucht
	  push es
	  pushf
	  les  bx,[ParamAddr] ; ES:BX = Parameterblock
	  mov  es:[bx+irStatus],8103h ; pessimistischer Ansatz
	  mov  al,es:[bx+irFunction] ; Funktionsnummer
	  or   al,al      ; Init?
	  jnz  OtherCmd   ; Code "Befehl unbekannt" gesetzt
	  call InitFunc   ; INIT - liefert Status in AX
	  les  bx,[ParamAddr]
	  mov  es:[bx+irStatus],ax
OtherCmd: popf
	  pop  es
	  pop  bx
	  pop  ax
	  retf
Intr      ENDP

UMBEntry STRUC
Start     dw ?      ; Startadresse (Paragraphs)
Len       dw ?      ; Länge Paragraphs
UMBEntry ENDS

UMBList  dw 22 dup(0) ; maximal 10 Einträge

; wird von IntFunc in die Kette eingesetzt und von DOS
; zum Belegen der UMBs aufgerufen.
; Aufruf: DX = gewünschte Größe (Bytes), Flags auf dem Stack
; Zurück: DX = Größe gefundener Block, BX = Segment, AX = 1
;         bzw. AX = 0 und BL = $B1 für "überhaupt kein Block"
;         bzw. AX = 0 und BL = $B0 für "Block zu klein"

UMBRequest:
;Suchstrategie First Fit (DOS #0), keine MCB-Verwendung!
	  push ds
	  push si
	  push cx
	  push bx

	  push cs
	  pop  ds     ; DS aufs Codesegment
	  mov  si,OFFSET UMBList
	  xor  cx,cx  ; für Größenvergleich
	  cld         ; aufsteigende Richtung
UMBSearch:
	  lodsw       ; ein Eintrag der UMB-Liste
	  or   ax,ax  ; Listenende?
	  jz   UMBNFnd ; -> ja, Fehler
	  mov  bx,ax  ; Startsegment festhalten
	  lodsw
	  cmp  bx,-1  ; bereits voll?
	  jz   UMBSearch ; -> ja, nächster Eintrag
	  cmp  ax,cx  ; bis dato größter Block?
	  jb   ChkSize
	  mov  cx,ax  ; ja, Größe festhalten
ChkSize:  cmp  ax,dx  ; reicht die Größe?
	  jb   UMBSearch ; -> nein, nächster Block

	mov Word Ptr[si-4],-1 ; Block als vergeben markieren
	mov	dx,ax		;Größe in DX
	pop	ax		;altes BX vom Stack
	mov	ax,1		;Signal für Erfolg
	jmp	Short UMBDone

UMBNFnd:xor	ax,ax
	pop	bx			; BX-Original (BH)
	mov	bl,0B0h		; Annahme: Block nur zu klein
	mov	dx,cx		; zurückzuliefernde Blockgröße
	or	dx,dx		; irgendetwas gefunden?
	jnz	UMBDone		; -> ja, nur zu klein
	inc	bl		; gar nichts gefunden: BL = $B1
UMBDone:pop  cx
	  pop  si
	  pop  ds
	  popf
	  retf

; Kette von HIMEM.SYS
XMSOrgJmp dd ?       ; Originalwert
NewChain: jmp short NewCtrl
	  nop        ; an dieser Stelle würde der nächste
	  nop        ; Handler seinen Sprung einsetzen
	  nop
NewCtrl:  pushf
	  cmp  ah,10h  ; UMB request?
	  jz  UMBRequest ; Flags auf dem Stack!
	  popf
	jmp	[XMSOrgJmp]	;Near?

NewInt9:    PUSH AX			;Ansprung durch Tastendruck
		PUSH DS
		XOR AX,AX
		MOV DS,AX
		MOV AL,DS:[0417h]	;AL=Tastatur-Status
		AND AL,00001100b	;nur noch Ctrl & Alt - Flag
		CMP AL,00001100b	;Ctrl+Alt gedrückt ?
		JNZ intcont
		IN AL,60h		;AL=Make-Code der gedr. Taste
		CMP AL,53h		; Make-Code = >DEL< ?
		JNZ intcont		; NEIN -> dann weiter
;		mov ds:[word ptr 472h],1234h
		xor	ax,ax
		mov	bx,ax
		int	10h		;Videomodus 0
		mov	ah,2
		mov	dx,12*256+14
		int	10h		;Cursorpos.
		mov	si,offset txt
txto:		mov	al,cs:[si]
		or	al,al
		jz	$		;Warten bis ewig
		mov	ah,0eh
		int	10h
		inc	si
		jmp	short txto	;Warteschlange

INTCONT:	POP DS
		POP AX

		db	0eah		;Jump Far
OldInt9Ofs	dw	?		;Sprung-Adresse (Absturz !)
OldInt9Seg	dw	?

txt:		db	'Press RESET',0
; *** Beginn des nicht residenten Teils ***
CRLF      db 13,10,'$'

PrintString:
	  push dx
	  mov  dx,OFFSET CRLF
	  call DoPrint
	  pop  dx
	  call DoPrint
	  mov  dx,OFFSET CRLF
DoPrint:  mov  ah,09h
	  int  21h
	  ret

; Ausgabe einer Hex-Zahl mit vier Stellen (AX)
PrHex:	push	ax
	mov	al,ah
	call	ahex
	pop	ax
ahex:	push	ax		;Werte erhalten
	mov	cl,4		;oberes Nibble auf Bit 3...0
	shr	al,cl		; schieben
	call	ahex1
	pop	ax
ahex1:	and	al,0fh
	add	al,90h		;Hex -> ASCII
	daa
	adc	al,40h
	daa
	mov	dl,al
	mov	ah,2
	int	21h
	ret

; Ausgabe einer Fehlermeldung über die DOS-Funktion $09 und Abbruch
; Aufruf mit DS:DX = Text der Meldung
ErrOut:   call PrintString               ; Textausgabe
	  les  bx,[ParamAddr]
	  mov  Word Ptr es:[bx+irEndAddress],0 ; 0 Bytes Platzbedarf
	  mov  ax,8100h  ; Fehler (unspezifisch)
	  jmp  InitEnd

; Initialisierung - wird mit ES:DI = Parameterblock und ES,BX,AX/Flags
; auf dem Stack aufgerufen, d.h. muß sich um diese Register nicht
; mehr kümmern
InitFunc  PROC NEAR
	  push cx
	  push dx
	  push si
	  push di
	  push ds

	  push cs
	  pop  ds          ; DS aufs Codesegment

	  mov  Word Ptr es:[bx+irEndAddress],OFFSET CRLF
	  mov  Word Ptr es:[bx+irEndAddress+2],cs ; Endaddr eintragen
; HIMEM installiert?
	  mov  ax,4300h    ; Installationsprüfung von HIMEM
	  int  2Fh
	  cmp  al,80h      ; vorhanden?
	  jz   GetXMSCall  ; -> ja
	  mov  dx,OFFSET NoXMS$
	  jmp  ErrOut
; Kette ermitteln
GetXMSCall:
	  mov  ax,4310h    ; HIMEM: Einsprungadresse
	  int  2Fh
	  mov  Word Ptr [XMSOrgJmp+2],es
	mov	word ptr [xmsorgjmp],bx
; Existiert bereits ein UMB Server?
	  mov  ah,16
	  mov  dx,0FFFh    ; Anfordern eines Blocks mit 64 KByte
	  call [XMSOrgJmp]
	  cmp  bl,80h      ; nicht installiert?
	  jz   GetCmdLine  ; -> OK
	  mov  dx,OFFSET UMBPresent$
	  jmp  ErrOut      ; da ist schon wer!
; Kommandozeile auswerten
GetCmdLine:
	  les  di,[ParamAddr]  ; Parameterblock
	  les  di,es:[di+irParamAddress] ; ES:DI auf Kommandozeile
	  call SetList     ; Auswerten und Liste setzen
	  jnc  TestAreas   ; -> OK
	  mov  dx,OFFSET BadCmd$
	  jmp  ErrOut      ; Kommandozeile ausgeben
TestAreas:
	  mov  si,OFFSET UMBList
	  cld
NextUMB:  lodsw            ; Startadresse
	  or  ax,ax        ; Ende der Liste erreicht?
	  jz  AreasTested
	  mov es,ax        ; Startadresse in ES
	  lodsw            ; Größe (Paragraphs)
TstBlocks: xor  di,di
	  mov  bx,es:[di]   ; momentanen Inhalt lesen
	  mov  dx,bx
	  xor  dx,0FFFFh    ; Umdrehen
	  mov  es:[di],dx   ; und Schreiben
	  mov  cx,80h       ; einen Moment warten, falls die
	  loop $            ; Operation ins Leere gehen sollte
	  cmp  es:[di],dx   ; haben wir einen konstanten Wert?
	  mov  es:[di],bx   ; alten Inhalt zurück
	  jnz  NoRAM        ; -> kein RAM an dieser Stelle!
	  mov	bx,400h
	mov	dx,es
	add	dx,bx		;next 16K
	mov	es,dx
	sub	ax,bx
	jg	TstBlocks	;Länge minus 16K
	jmp	NextUMB      ; -> nächster UMB-Bereich

NoRAM:    push es           ; Segmentadresse des Blocks
	  mov  dx,OFFSET RAMFail1$
	  call DoPrint      ; Ausgabe ohne CRLF
	  pop  ax
	  call PrHex        ; Segmentadresse ausgeben
	  mov  dx,OFFSET RAMFail2$
	  jmp  ErrOut

; Hier geht es nach einen erfolgreichen RAM-Test mit dem Einsetzen
; des eigenen "Hook" in die von HIMEM.SYS aufgebaute Sprungkette weiter
AreasTested: les di,[XMSOrgJmp]

ScanHooks: mov  al,Byte Ptr es:[di]
	  cmp  al,0EBh      ; JMP SHORT?
	  jz   HookFound    ; OK - momentanes Kettenende
	  les  di,DWord Ptr es:[di+1] ; sonst nächstes Element
	  cmp  al,0EAh      ; war das überhaupt ein JMP FAR?
	  jz   ScanHooks
	  mov  dx,OFFSET HookErr$  ; Hook-Kette beschädigt
	  jmp  ErrOut
HookFound: cli               ; *keine* Störungen hier, bitte!
	  dec  Byte Ptr es:[di] ; JMP SHORT -> JMP FAR
	  mov  Word Ptr es:[di+1], OFFSET NewChain ; eigene Routine
	  mov  Word Ptr es:[di+3], cs   ; einsetzen
	  add  di,5         ; echte Originaladresse (hinter den NOPS)
	  mov  Word Ptr [XMSOrgJmp],di
	  mov  Word Ptr [XMSOrgJmp+2],es
	  sti
	  mov  ax,0100h    ; Initialisierung fehlerfrei
InitEnd:  pop  ds
	  pop  di
	  pop  si
	  pop  dx
	  pop  cx
	  ret
InitFunc  ENDP

Copyright$  db 'UMBADD c''t 8/91 - Peter Siering/Arne Schäpers/ToGraWu/h#s$'
NoXMS$      db 'Kein XMS-Treiber vorhanden!$'
UMBPresent$ db 'UMB Server bereits installiert!$'
BadCmd$     db 'Probleme mit den Bereichsangaben in CONFIG.SYS!',13,10
Help$	    db 'Format: DEVICE=UMBADD.EXE /Iaaaa-bbbb /Icccc-dddd ...(Anfang und Ende+1!)$'
RAMFail1$   db 13,10,'Kein RAM im Segment (hex) $'
RAMFail2$   db '- Installation abgebrochen -$'
HookErr$    db 'Hook-Chain von HIMEM.SYS beschädigt/unbekanntes Format!$'

; Aufruf mit ES:DI als Index in die Kommandozeile
; zurück mit DI+4, Wert bzw. 0 in DX und gelöschtem/gesetzten Z-Flag
HexToVal: push cx
	  mov  cx,0404h     ; Zähler, Shift-Zähler
	  xor  dx,dx        ; Wert
DoDigit:  shl  dx,cl        ; eine Stelle nach links
	  mov  al,es:[di]   ; ein Zeichen
	  inc  di
	  cmp  al,'9'
	  jbe  IsDigit
	  and  al,0DFh      ; Großbuchstaben
	  cmp  al,'A'
	  jb   NoChar       ; Fehler
	  cmp  al,'F'
	  ja   NoChar       ; dito
	  sub  al,55        ; 'A'..'F' -> 10..16
	  jmp  short SetDig ; Einsetzen
IsDigit:  cmp  al,'0'
	  jb   NoChar       ; Fehler
	  sub  al,'0'       ; '0'..'9' -> 0..9
SetDig:   or   dl,al        ; Einsetzen
	  dec  ch
	  jnz  DoDigit
	  jmp  Short DigDone

NoChar:   xor  dx,dx        ; Fehleranzeige: dx = 0
DigDone:  pop  cx
	  or   dx,dx
	  ret

; Auswertung der Kommandozeile. Aufruf mit ES:DI = Kommandozeile,
; zurück mit gesetzter UMBList und gesetztem/gelöschten Carry
SetList   PROC NEAR
;	  call ram_on
	  xor  bx,bx        ; Index in UMBList
DoElem:   mov  al,es:[di]
	  inc  di
	  cmp  al,13        ; CR?
	  jz   ListSet      ; -> ja, Ende
	  cmp  al,10        ; LF?
	  jz   ListSet      ; -> dito
	  cmp  al,'/'       ; Schrägstrich von "/I=..."?
	  jnz  DoElem
	  mov  ax,es:[di]
	  and  al,0DFh      ; Umsetzung 'i' -> 'I'
	  cmp  ax,'=I'      ; ist "I="? (verkehrtherum)
	  jnz  ListErr      ; -> nein, Fehler
	  add  di,2         ; Ok, überspringen
	  call HexToVal     ; Hexzahl auswerten -> DX
	  jz   ListErr      ; -> war nichts
	  mov  al,es:[di]
	  cmp  al,'-'       ; Trennstrich?
	  inc  di
	  mov  cx,dx        ; Startadresse festhalten
	  call HexToVal     ; Endadresse ermitteln
	  jz   ListErr      ; -> war nichts
	  mov  Word Ptr[UMBList+bx],cx  ; Eintrag Startadresse
	  mov  ax,dx
	  sub  ax,cx        ; Größe in Paragraphs
	  mov  Word Ptr[UMBList+bx+2],ax
	  add  bx,4
	  cmp  bx,40        ; mehr als 10 Einträge?
	  jb   DoElem       ; -> nein, weiter
ListErr:  stc
	  ret
ListSet:  sub  bx,1         ; setzt Carry, wenn BX = 0
	  ret
SetList   ENDP
HELP:
	push	cs
	pop	ds
	mov	dx,offset copyright$
	call	PrintString
	mov	dx,offset help$
	call	PrintString
	mov	ax,4c00h
	int	21h

	END	help
ram_on		proc near
		push ax
		push cx
		push es
		push si
		mov al,68h
		out 22h,al
		jmp a1
a1:		in al,23h
		or al,11110000b		;Speicher von D000-DFFF einblenden
		push ax
		mov al,68h
		out 22h,al
		jmp a2
a2:		pop ax
		out 23h,al		;in NEAD-Register schreiben
		mov al,65h
		out 22h,al
		jmp a3
a3:		in al,23h
		and al,10111111b	;R/W für D000-DFFF setzen
		push ax
		mov al,65h
		out 22h,al
		jmp a4
a4:		pop ax
		out 23h,al
		in al,70h
		push ax
		or al,10000000b
		out 70h,al		;NMI abschalten
		in al,71h		;Dummy read notwendig!!!
		mov ax,0D000h		;Seg. ADR begin Init
		mov es,ax
		mov si,0h
		cli
a5:		mov word ptr es:[si],0	;RAM Init
		inc si
		inc si
		cmp si,0
		jne a5
		sti
		in al,61h		;Port b,Parity-Enable-Bit
		xor al,14h
		out 61h,al		;setzen
		jmp a6
a6:		xor al,14h		;und zurücksetzen
		out 61h,al
		pop ax
		and al,10000000b
		out 70h,al
		in al,71h
		pop si
		pop es
		pop cx
		pop ax
		ret
ram_on		endp
	  END
Detected encoding: OEM (CP437)1
Wrong umlauts? - Assume file is ANSI (CP1252) encoded