1
0
mirror of synced 2026-01-12 00:02:46 +00:00

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.
This commit is contained in:
Matthieu Bucchianeri 2025-03-07 22:00:26 -08:00 committed by GitHub
parent 553ca58dff
commit 0fbbc0070c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 1711 additions and 94 deletions

View File

@ -49,6 +49,9 @@
// Revision 10 02/17/2025
// - Use a lookup table for the memory map
//
// Revision 11 03/01/2025
// - Refactor lookup table and add support for UMBs
//
//------------------------------------------------------------------------
//
// Copyright (c) 2024 Ted Fried
@ -170,12 +173,10 @@
#define PSRAM_RESET_VALUE 0x01400000
#define PSRAM_CLK_HIGH 0x02000000
#define EMS_BASE_IO 0x260 // Must be a multiple of 8.
#define EMS_BASE_MEM 0xD0000
#define MMAN_BASE 0x260 // Must be a multiple of 16.
#define EMS_MAX_SIZE (16*1024*1024)
#define EMS_TOTAL_SIZE (16*1024*1024)
#define SD_BASE 0x280 // Must be a multiple of 8.
#define SD_BASE 0x280 // Must be a multiple of 8.
#define SD_CONFIG_BYTE 0
@ -196,7 +197,9 @@ uint8_t isa_data_out = 0;
uint8_t nibble_in =0;
uint8_t nibble_out =0;
uint8_t read_byte =0;
uint16_t ems_frame_pointer[4] = {0, 0, 0, 0};
uint32_t umb_base_segment =0;
uint16_t ems_frame_pointer[4] = {0xffff, 0xffff, 0xffff, 0xffff};
uint32_t ems_base_segment =0;
uint8_t spi_shift_out =0;
uint8_t sd_spi_datain =0;
uint32_t sd_spi_cs_n = 0x0;
@ -205,10 +208,36 @@ uint8_t sd_scratch_register[6] = {0, 0, 0, 0, 0, 0};
uint16_t sd_requested_timeout = 0;
elapsedMillis sd_timeout;
uint8_t XTMax_MEM_Response_Array[16];
enum MemResponse {
AutoDetect,
DontRespond,
Respond,
};
DMAMEM uint8_t internal_RAM1[0x60000];
uint8_t internal_RAM2[0x40000];
MemResponse XTMax_MEM_Response_Array[16] = {
/* 640KB conventional memory */
/* 00000 - 0FFFF */ AutoDetect,
/* 10000 - 1FFFF */ AutoDetect,
/* 20000 - 2FFFF */ AutoDetect,
/* 30000 - 3FFFF */ AutoDetect,
/* 40000 - 4FFFF */ AutoDetect,
/* 50000 - 5FFFF */ AutoDetect,
/* 60000 - 6FFFF */ AutoDetect,
/* 70000 - 7FFFF */ AutoDetect,
/* 80000 - 8FFFF */ AutoDetect,
/* 90000 - 9FFFF */ AutoDetect,
/* TBD - can be used for UMB */
/* A0000 - AFFFF */ Respond,
/* B0000 - BFFFF */ Respond,
/* C0000 - CFFFF */ Respond,
/* D0000 - DFFFF */ Respond,
/* E0000 - EFFFF */ Respond,
/* Reserved (BIOS) */
/* F0000 - FFFFF */ DontRespond,
};
DMAMEM uint8_t internal_RAM1[0x7A000]; /* 0 - 488KB */
uint8_t internal_RAM2[0x76000]; /* 488 - 640KB + 320KB UMB */
uint8_t psram_cs =0;
@ -220,7 +249,38 @@ enum Region {
SdCard
};
Region memmap[512];
#define RAM_64K \
Ram, Ram, Ram, Ram, Ram, Ram, Ram, Ram, Ram, Ram, Ram, Ram, Ram, Ram, Ram, Ram, \
Ram, Ram, Ram, Ram, Ram, Ram, Ram, Ram, Ram, Ram, Ram, Ram, Ram, Ram, Ram, Ram
#define UNUSED_64K \
Unused, Unused, Unused, Unused, Unused, Unused, Unused, Unused, Unused, Unused, Unused, Unused, Unused, Unused, Unused, Unused, \
Unused, Unused, Unused, Unused, Unused, Unused, Unused, Unused, Unused, Unused, Unused, Unused, Unused, Unused, Unused, Unused
// Uncomment to disable conventional memory
//#define RAM_64K UNUSED_64K
Region memmap[512] = {
/* 640KB conventional memory */
/* 00000 - 0FFFF */ RAM_64K,
/* 10000 - 1FFFF */ RAM_64K,
/* 20000 - 2FFFF */ RAM_64K,
/* 30000 - 3FFFF */ RAM_64K,
/* 40000 - 4FFFF */ RAM_64K,
/* 50000 - 5FFFF */ RAM_64K,
/* 60000 - 6FFFF */ RAM_64K,
/* 70000 - 7FFFF */ RAM_64K,
/* 80000 - 8FFFF */ RAM_64K,
/* 90000 - 9FFFF */ RAM_64K,
/* TBD - can be used for EMS or UMB */
/* A0000 - AFFFF */ UNUSED_64K,
/* B0000 - BFFFF */ UNUSED_64K,
/* C0000 - CFFFF */ UNUSED_64K,
/* D0000 - DFFFF */ UNUSED_64K,
/* E0000 - EFFFF */ UNUSED_64K,
/* Reserved (BIOS) */
/* F0000 - FFFFF */ UNUSED_64K,
};
// --------------------------------------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------
@ -305,25 +365,8 @@ void setup() {
//Serial.begin(9600);
// Populate the memory map.
// Patch the memory map.
unsigned int i;
for (i = 0; i < sizeof(memmap)/sizeof(memmap[0]); i++) {
memmap[i] = Unused;
}
for (i = 0;
i < (0xA0000 >> 11);
i++) {
memmap[i] = Ram;
}
static_assert((EMS_BASE_MEM & 0x7FF) == 0);
for (i = (EMS_BASE_MEM >> 11);
i < ((EMS_BASE_MEM+0x10000) >> 11);
i++) {
memmap[i] = EmsWindow;
}
static_assert((BOOTROM_ADDR & 0x7FF) == 0);
static_assert((sizeof(BOOTROM) % 2048) == 0, "BootROM must be in blocks of 2KB");
for (i = (BOOTROM_ADDR >> 11);
@ -331,7 +374,6 @@ void setup() {
i++) {
memmap[i] = BootRom;
}
memmap[(BOOTROM_ADDR+sizeof(BOOTROM)) >> 11] = SdCard;
}
@ -454,7 +496,7 @@ inline void PSRAM_Configure() {
inline uint8_t PSRAM_Read(uint32_t address_in) {
if (address_in >= EMS_TOTAL_SIZE) {
if (address_in >= EMS_MAX_SIZE) {
return 0xff;
}
if (address_in >= 0x7FFFFF) psram_cs=1; else psram_cs=0;
@ -502,7 +544,7 @@ inline uint8_t PSRAM_Read(uint32_t address_in) {
// --------------------------------------------------------------------------------------------------
inline void PSRAM_Write(uint32_t address_in , int8_t local_data) {
if (address_in >= EMS_TOTAL_SIZE) {
if (address_in >= EMS_MAX_SIZE) {
return;
}
if (address_in >= 0x7FFFFF) psram_cs=1; else psram_cs=0;
@ -539,20 +581,13 @@ inline void PSRAM_Write(uint32_t address_in , int8_t local_data) {
// --------------------------------------------------------------------------------------------------
inline uint8_t Internal_RAM_Read() {
uint8_t local_temp;
if (isa_address<0x60000) local_temp = internal_RAM1[isa_address];
else local_temp = internal_RAM2[isa_address-0x60000];
return local_temp;
if (isa_address<sizeof(internal_RAM1)) return internal_RAM1[isa_address];
else return internal_RAM2[isa_address-sizeof(internal_RAM1)];
}
inline void Internal_RAM_Write() {
if (isa_address<0x60000) internal_RAM1[isa_address] = 0xFF & ADDRESS_DATA_GPIO6_UNSCRAMBLE;
else internal_RAM2[isa_address-0x60000] = 0xFF & ADDRESS_DATA_GPIO6_UNSCRAMBLE;
return;
if (isa_address<sizeof(internal_RAM1)) internal_RAM1[isa_address] = 0xFF & ADDRESS_DATA_GPIO6_UNSCRAMBLE;
else internal_RAM2[isa_address-sizeof(internal_RAM1)] = 0xFF & ADDRESS_DATA_GPIO6_UNSCRAMBLE;
}
// --------------------------------------------------------------------------------------------------
@ -567,10 +602,10 @@ inline void Mem_Read_Cycle()
case EmsWindow:
page_base_address = (isa_address & 0xFC000);
if (page_base_address == (EMS_BASE_MEM | 0xC000)) { psram_address = (ems_frame_pointer[3]<<14) | (isa_address & 0x03FFF); }
else if (page_base_address == (EMS_BASE_MEM | 0x8000)) { psram_address = (ems_frame_pointer[2]<<14) | (isa_address & 0x03FFF); }
else if (page_base_address == (EMS_BASE_MEM | 0x4000)) { psram_address = (ems_frame_pointer[1]<<14) | (isa_address & 0x03FFF); }
else if (page_base_address == (EMS_BASE_MEM | 0x0000)) { psram_address = (ems_frame_pointer[0]<<14) | (isa_address & 0x03FFF); }
if (page_base_address == ((ems_base_segment<<4) | 0xC000)) { psram_address = (ems_frame_pointer[3]<<14) | (isa_address & 0x03FFF); }
else if (page_base_address == ((ems_base_segment<<4) | 0x8000)) { psram_address = (ems_frame_pointer[2]<<14) | (isa_address & 0x03FFF); }
else if (page_base_address == ((ems_base_segment<<4) | 0x4000)) { psram_address = (ems_frame_pointer[1]<<14) | (isa_address & 0x03FFF); }
else if (page_base_address == ((ems_base_segment<<4) | 0x0000)) { psram_address = (ems_frame_pointer[0]<<14) | (isa_address & 0x03FFF); }
GPIO7_DR = MUX_ADDR_n_LOW + CHRDY_OUT_LOW + trigger_out;
GPIO8_DR = sd_pin_outputs + MUX_DATA_n_HIGH + CHRDY_OE_n_LOW + DATA_OE_n_LOW ; // Assert CHRDY_n=0 to begin wait states
@ -588,17 +623,11 @@ inline void Mem_Read_Cycle()
isa_data_out = Internal_RAM_Read();
GPIO7_DR = GPIO7_DATA_OUT_UNSCRAMBLE + MUX_ADDR_n_LOW + CHRDY_OUT_LOW + trigger_out;
// XTMax_MEM_Response_Array
// - Array holds value 0,1,2
// 0 = unitiailzed - add wait states and snoop
// 1 = No wait states and no response
// 2 = No wait states and yes respond
//
// If XTMax has not seen a read access to this 64 KB page yet, add wait states to give physical RAM (if present) a chance to respond
if (XTMax_MEM_Response_Array[(isa_address>>16)] == 2) {
if (XTMax_MEM_Response_Array[(isa_address>>16)] == Respond) {
GPIO8_DR = sd_pin_outputs + MUX_DATA_n_HIGH + CHRDY_OE_n_HIGH + DATA_OE_n_LOW; // Physical RAM is NOT present at this page so XTMax will respond
}
else if (XTMax_MEM_Response_Array[(isa_address>>16)] == 0) {
else if (XTMax_MEM_Response_Array[(isa_address>>16)] == AutoDetect) {
GPIO8_DR = sd_pin_outputs + MUX_DATA_n_HIGH + CHRDY_OE_n_LOW + DATA_OE_n_HIGH; // Assert CHRDY_n=0 to begin wait states
delayNanoseconds(800);
GPIO8_DR = sd_pin_outputs + MUX_DATA_n_HIGH + CHRDY_OE_n_HIGH + DATA_OE_n_HIGH; // De-assert CHRDY
@ -607,10 +636,10 @@ inline void Mem_Read_Cycle()
data_in = 0xFF & ADDRESS_DATA_GPIO6_UNSCRAMBLE;
if (data_in == isa_data_out) {
XTMax_MEM_Response_Array[(isa_address>>16)] = 1; // Physical RAM is present at this page so XTMax should not respond
XTMax_MEM_Response_Array[(isa_address>>16)] = DontRespond; // Physical RAM is present at this page so XTMax should not respond
}
else {
XTMax_MEM_Response_Array[(isa_address>>16)] = 2;
XTMax_MEM_Response_Array[(isa_address>>16)] = Respond;
GPIO8_DR = sd_pin_outputs + MUX_DATA_n_HIGH + CHRDY_OE_n_HIGH + DATA_OE_n_LOW; // Physical RAM is NOT present at this page so XTMax will respond
}
}
@ -662,10 +691,10 @@ inline void Mem_Write_Cycle()
case EmsWindow:
page_base_address = (isa_address & 0xFC000);
if (page_base_address == (EMS_BASE_MEM | 0xC000)) { psram_address = (ems_frame_pointer[3]<<14) | (isa_address & 0x03FFF); }
else if (page_base_address == (EMS_BASE_MEM | 0x8000)) { psram_address = (ems_frame_pointer[2]<<14) | (isa_address & 0x03FFF); }
else if (page_base_address == (EMS_BASE_MEM | 0x4000)) { psram_address = (ems_frame_pointer[1]<<14) | (isa_address & 0x03FFF); }
else if (page_base_address == (EMS_BASE_MEM | 0x0000)) { psram_address = (ems_frame_pointer[0]<<14) | (isa_address & 0x03FFF); }
if (page_base_address == ((ems_base_segment<<4) | 0xC000)) { psram_address = (ems_frame_pointer[3]<<14) | (isa_address & 0x03FFF); }
else if (page_base_address == ((ems_base_segment<<4) | 0x8000)) { psram_address = (ems_frame_pointer[2]<<14) | (isa_address & 0x03FFF); }
else if (page_base_address == ((ems_base_segment<<4) | 0x4000)) { psram_address = (ems_frame_pointer[1]<<14) | (isa_address & 0x03FFF); }
else if (page_base_address == ((ems_base_segment<<4) | 0x0000)) { psram_address = (ems_frame_pointer[0]<<14) | (isa_address & 0x03FFF); }
GPIO7_DR = GPIO7_DATA_OUT_UNSCRAMBLE + MUX_ADDR_n_HIGH + CHRDY_OUT_LOW + trigger_out;
GPIO8_DR = sd_pin_outputs + MUX_DATA_n_LOW + CHRDY_OE_n_LOW + DATA_OE_n_HIGH; // Steer data mux to Data[7:0] and Assert CHRDY_n=0 to begin wait states
@ -736,16 +765,18 @@ inline void IO_Read_Cycle()
{
isa_address = 0xFFFF & ADDRESS_DATA_GPIO6_UNSCRAMBLE;
if ((isa_address&0x0FF8)==EMS_BASE_IO) { // Location of 16 KB Expanded Memory page frame pointers
if ((isa_address&0x0FF0)==MMAN_BASE) { // Location of Memory MANager registers
switch (isa_address) {
case EMS_BASE_IO : isa_data_out = ems_frame_pointer[0]; break;
case EMS_BASE_IO+1: isa_data_out = ems_frame_pointer[0] >> 8; break;
case EMS_BASE_IO+2: isa_data_out = ems_frame_pointer[1]; break;
case EMS_BASE_IO+3: isa_data_out = ems_frame_pointer[1] >> 8; break;
case EMS_BASE_IO+4: isa_data_out = ems_frame_pointer[2]; break;
case EMS_BASE_IO+5: isa_data_out = ems_frame_pointer[2] >> 8; break;
case EMS_BASE_IO+6: isa_data_out = ems_frame_pointer[3]; break;
case EMS_BASE_IO+7: isa_data_out = ems_frame_pointer[3] >> 8; break;
case MMAN_BASE+0 : isa_data_out = ems_frame_pointer[0]; break;
case MMAN_BASE+1 : isa_data_out = ems_frame_pointer[0] >> 8; break;
case MMAN_BASE+2 : isa_data_out = ems_frame_pointer[1]; break;
case MMAN_BASE+3 : isa_data_out = ems_frame_pointer[1] >> 8; break;
case MMAN_BASE+4 : isa_data_out = ems_frame_pointer[2]; break;
case MMAN_BASE+5 : isa_data_out = ems_frame_pointer[2] >> 8; break;
case MMAN_BASE+6 : isa_data_out = ems_frame_pointer[3]; break;
case MMAN_BASE+7 : isa_data_out = ems_frame_pointer[3] >> 8; break;
case MMAN_BASE+15: isa_data_out = memmap[umb_base_segment >> 7]; break; // Useful for debugging
default: isa_data_out = 0xff; break;
}
GPIO7_DR = GPIO7_DATA_OUT_UNSCRAMBLE + MUX_ADDR_n_LOW + CHRDY_OUT_LOW + trigger_out;
@ -785,28 +816,53 @@ inline void IO_Write_Cycle()
{
isa_address = 0xFFFF & ADDRESS_DATA_GPIO6_UNSCRAMBLE;
if ((isa_address&0x0FF8)==EMS_BASE_IO) { // Location of 16 KB Expanded Memory page frame pointers
if ((isa_address&0x0FF0)==MMAN_BASE) { // Location of Memory MANager registers
GPIO7_DR = GPIO7_DATA_OUT_UNSCRAMBLE + MUX_ADDR_n_HIGH + CHRDY_OUT_LOW + trigger_out;
GPIO8_DR = sd_pin_outputs + MUX_DATA_n_LOW + CHRDY_OE_n_HIGH + DATA_OE_n_HIGH;
delayNanoseconds(50); // Give some time for write data to be available after IOWR_n goes low
gpio6_int = GPIO6_DR;
data_in = 0xFF & ADDRESS_DATA_GPIO6_UNSCRAMBLE;
switch (isa_address) {
case MMAN_BASE+0 : ems_frame_pointer[0] = (ems_frame_pointer[0] & 0xFF00) | data_in; break;
case MMAN_BASE+1 : ems_frame_pointer[0] = (ems_frame_pointer[0] & 0x00FF) | ((uint16_t)data_in << 8); break;
case MMAN_BASE+2 : ems_frame_pointer[1] = (ems_frame_pointer[1] & 0xFF00) | data_in; break;
case MMAN_BASE+3 : ems_frame_pointer[1] = (ems_frame_pointer[1] & 0x00FF) | ((uint16_t)data_in << 8); break;
case MMAN_BASE+4 : ems_frame_pointer[2] = (ems_frame_pointer[2] & 0xFF00) | data_in; break;
case MMAN_BASE+5 : ems_frame_pointer[2] = (ems_frame_pointer[2] & 0x00FF) | ((uint16_t)data_in << 8); break;
case MMAN_BASE+6 : ems_frame_pointer[3] = (ems_frame_pointer[3] & 0xFF00) | data_in; break;
case MMAN_BASE+7 : ems_frame_pointer[3] = (ems_frame_pointer[3] & 0x00FF) | ((uint16_t)data_in << 8); break;
case MMAN_BASE+10: ems_base_segment = (ems_base_segment & 0xFF00) | data_in; break;
case MMAN_BASE+11: ems_base_segment = (ems_base_segment & 0x00FF) | ((uint16_t)data_in << 8); break;
case MMAN_BASE+12: // Num 16K pages + commit operation
if (ems_base_segment >= 0xA000 && ems_base_segment + (data_in << 10) <= 0xF000) {
const unsigned int base = ems_base_segment >> 7; // 64K segment to 2KB offset
const unsigned int count = data_in << 3; // 16KB pages to 2KB pages
for (unsigned int i = 0; i < count; i++) {
memmap[base + i] = EmsWindow;
}
}
break;
case MMAN_BASE+13: umb_base_segment = (umb_base_segment & 0xFF00) | data_in; break;
case MMAN_BASE+14: umb_base_segment = (umb_base_segment & 0x00FF) | ((uint16_t)data_in << 8); break;
case MMAN_BASE+15: // Num 2K pages + commit operation
if (umb_base_segment >= 0xA000 && umb_base_segment + (data_in << 7) <= 0xF000) {
const unsigned int base = umb_base_segment >> 7; // 64K segment to 2KB offset
for (unsigned int i = 0; i < data_in; i++) {
memmap[base + i] = Ram;
}
}
break;
default:
break;
}
while ( (gpio9_int&0xF0) != 0xF0 ) { // Wait here until cycle is complete
gpio6_int = GPIO6_DR;
gpio9_int = GPIO9_DR;
}
data_in = 0xFF & ADDRESS_DATA_GPIO6_UNSCRAMBLE;
switch (isa_address) {
case EMS_BASE_IO : ems_frame_pointer[0] = (ems_frame_pointer[0] & 0xFF00) | data_in; break;
case EMS_BASE_IO+1: ems_frame_pointer[0] = (ems_frame_pointer[0] & 0x00FF) | ((uint16_t)data_in << 8); break;
case EMS_BASE_IO+2: ems_frame_pointer[1] = (ems_frame_pointer[1] & 0xFF00) | data_in; break;
case EMS_BASE_IO+3: ems_frame_pointer[1] = (ems_frame_pointer[1] & 0x00FF) | ((uint16_t)data_in << 8); break;
case EMS_BASE_IO+4: ems_frame_pointer[2] = (ems_frame_pointer[2] & 0xFF00) | data_in; break;
case EMS_BASE_IO+5: ems_frame_pointer[2] = (ems_frame_pointer[2] & 0x00FF) | ((uint16_t)data_in << 8); break;
case EMS_BASE_IO+6: ems_frame_pointer[3] = (ems_frame_pointer[3] & 0xFF00) | data_in; break;
case EMS_BASE_IO+7: ems_frame_pointer[3] = (ems_frame_pointer[3] & 0x00FF) | ((uint16_t)data_in << 8); break;
}
GPIO7_DR = GPIO7_DATA_OUT_UNSCRAMBLE + MUX_ADDR_n_LOW + CHRDY_OUT_LOW + trigger_out;
GPIO8_DR = sd_pin_outputs + MUX_DATA_n_HIGH + CHRDY_OE_n_HIGH + DATA_OE_n_HIGH;
}
@ -862,10 +918,9 @@ void loop() {
gpio6_int = GPIO6_DR;
gpio9_int = GPIO9_DR;
if ((gpio9_int&0x80000010)==0) IO_Read_Cycle(); // Isolate and check AEN and IO Rd/Wr
else if ((gpio9_int&0x80000020)==0) IO_Write_Cycle();
else if ((gpio9_int&0x00000040)==0) Mem_Read_Cycle();
else if ((gpio9_int&0x00000080)==0) Mem_Write_Cycle();
else if ((gpio9_int&0x00000080)==0) Mem_Write_Cycle();
}
}

View File

@ -3027,18 +3027,25 @@ instmsg PROC NEAR
RET
instmsg ENDP
;--------------------------------------------------------------------
; Check EMS i/o port.
; Initialize EMS window and check EMS i/o port.
; output
; for lo-tech EMS board, we cannot read from the page registers
; hence we just return OK anyway.
; cf = 0 : OK
; cf = 1 : NG
;--------------------------------------------------------------------
ckemsio PROC NEAR
MOV DX,CS:EMSIO
; JJP IN AL,DX
; JJP CMP AL,255 ;check TRS flag
; JJP JE ckems3
ADD DX,10 ; EMS segment register
MOV AX,CS:PAGE_FRAME_SEG
OUT DX,AX
ADD DX,3 ; debug base register
OUT DX,AX
DEC DX ; EMS pages count register
MOV AL,PHYS_PAGES
OUT DX,AL
ADD DX,3 ; debug register
IN AL,DX
CMP AL,2 ; Region::EmsWindow
JNE ckems3
ckems4:
CLC ;reset CF
RET
@ -3520,9 +3527,9 @@ info:
; EMM driver initial routine work data area
;--------------------------------------------------------------------
start_msg db CR,LF
DB 'XTEMM: EMM Driver for XTMax r02',CR,LF
DB 'XTEMM: EMM Driver for XTMax r03',CR,LF
db ' Based on Lo-tech EMM Driver for the Lo-tech 2MB EMS board.',CR,LF
db ' Based on modifications by Michael Karcher.',CR,LF
db ' Based on modifications by Michael Karcher.',CR,LF
db ' Based on original works Copyright (c) 1988, Alex Tsourikov.',CR,LF
db ' All rights reserved.',CR,LF
db 'Using EMS '

View File

@ -29,7 +29,6 @@ Syntax: DEVICE=LTEMM.EXE [/switches]
/i:nnn - EMS i/o port base address (260)
/h:nnn - Maximal number of handles (64)
/d:nn - Depth of contest saves (5)
/f:nnn - First page number (0)
/n - Bypass memory test
/x - Perform long memory test
/3 - Use only EMS 3.2 functions

130
XTMax/Drivers/README.md Normal file
View File

@ -0,0 +1,130 @@
# XTMax Device Drivers
## BIOS Extension ROM for SD Card (BootROM)
The XTMax maps a BIOS Extension ROM at address 0xCE000-0xCE7FF, plus a bank of registers at address 0xCE800-0xCEFFF.
The BIOS Extension ROM presents the SD Card on the Teensy as a fixed disk drive that can be used for booting and/or for
storage in MS-DOS and other operating systems using INT13h fixed disk services (note: OS/2 does not use INT13h fixed
disk services and therefore cannot use the SD Card).
The ROM can be relocated by changing the address `BOOTROM_ADDR` in the [`bootrom.h`](../Code/XTMax/bootrom.h) and
re-deploying the Teensy sketch. The address **must** be a multiple of 2048 bytes (2KB) and must be within the range
0xC8000-DF800 (this is the range that the BIOS searches).
### Preparing an SD Card for use with MS-DOS
The preferred method for preparing an SD Card for use is to use the machine with the XTMax. From an MS-DOS prompt, begin
with re-writing the Master Boot Record (MBR) onto the SD Card. This is needed in order to make sure that the SD Card can
be used for booting. Modern devices are shipped pre-formatted with an MBR that is not compatible with older x86
micro-processors.
```
A:\> FDISK /MBR
```
Next, partitions can be created on the SD Card with `FDISK`.
```
A:\> FDISK
```
If there is another fixed disk drive, be sure to select the correct drive from the `FDISK` menu. The existing partition
on the SD Card must first be deleted, before creating a new Primary Partition. **This will destroy the entire content of
the SD Card**. The new Primary Partition must be set as Active if the SD Card must be used for booting.
The partition on the SD Card will now appear as a new logical drive, typically `C:` or `D:` depending on availability.
Finally, the partition must be formatted. Use `FORMAT` and use the `/S` switch if the SD Card must be used for booting.
Note: MS-DOS will typically limit a partition size to 2GB. However, it is possible to create 3 additional Extended
Partitions to increase the usage of the SD Card. These partitions will appear as new logical drives, such as `E:`, `F:`,
etc... depending on availability.
## MS-DOS Driver for SD Card (XTSD)
This driver is provided as a back-up option for machines without support for BIOS Extension ROMs. It is based on the
[SDPP](https://github.com/nilseuropa/sdpp) driver originally written by Robert Armstrong and later improved by Dan
Marks.
**This driver is not necessary when the BIOS Extension ROM is loaded by the BIOS during boot.**
The device driver can be found here: [`XTSD.SYS`](https://raw.githubusercontent.com/MicroCoreLabs/Projects/master/XTMax/Drivers/XTSD.SYS).
Once downloaded, it must be placed on a drive accessible during boot and must be invoked from `CONFIG.SYS` as shown in
the example below:
```
DEVICE=A:\XTSD.SYS
```
When loaded, the driver will create a new logical drive for the SD Card, typically `C:` or `D:` depending on
availability.
### Preparing an SD Card for use with MS-DOS
The SD Card can be prepared from another machine. In this example, the SD Card is prepared from a Windows 10/11 machine.
Use `DISKPART`. First, use `LIST DISK` to find the disk number of the SD Card then `SELECT DISK #` with the appropriate
disk number.
Next, use `CLEAN` to delete all partitions. **This will destroy the entire content of the SD Card**.
Finally, create a new partition with `CREATE PART PRIMARY SIZE=#` by specifying the desired partition size in MB.
Depending on the version of MS-DOS on the machine that will be using the SD Card, there are limit on the size (typically
2048 ie 2GB).
The partition must be formatted, for example with `FORMAT #: /FS:FAT /Q`.
## EMS Driver for MS-DOS (XTEMM)
This driver provides LIM 4.0 Expanded Memory (EMS) through the [PSRAM](https://www.pjrc.com/store/psram.html) on the
Teensy. It is based on the LoTech [`LTEMM.EXE`](https://www.lo-tech.co.uk/wiki/LTEMM.EXE) driver and inspired by work by
Alex Tsourikov and Michael Karcher. Note that while the driver is compliant with EMS 4.0, it does not support memory
back-filling.
The device driver can be found here: [`XTEMM.EXE`](https://raw.githubusercontent.com/MicroCoreLabs/Projects/master/XTMax/Drivers/XTEMM.EXE).
Once downloaded, it must be placed on a drive accessible during boot and must be invoked from `CONFIG.SYS` as shown in
the example below:
```
DEVICE=A:\XTEMM.EXE /N
```
The address of the memory window used by the EMS driver is 0xD0000-0xDFFFF by default and can be changed with the `/P`
argument. For example, to use segment 0xE000 (mapping to 0xE0000-0xEFFFF), the syntax is as follows:
```
DEVICE=A:\XTEMM.EXE /P:E000 /N
```
## UMB Driver for MS-DOS (XTUMBS)
This driver provides Upper Memory Blocks (UMBs) in unused regions of the Upper Memory Area (UMA). It is based on the
`USE!UMBS` driver originally written by Marco van Zwetselaar and later rewritten by Krister Nordvall and published on
the [VCFED forum](https://forum.vcfed.org/index.php?threads/loading-dos-high-on-a-xt.32320/).
The device driver can be found here: [`XTUMBS.SYS`](https://raw.githubusercontent.com/MicroCoreLabs/Projects/master/XTMax/Drivers/XTUMBS.SYS).
Once downloaded, it must be placed on a drive accessible during boot and must be invoked from `CONFIG.SYS` as shown in
the example below:
```
DEVICE=A:\XTUMBS.SYS D000-E000
```
In the example above, the memory region at 0xD0000-0xDFFFF will be used as an UMB. **Care must be taken to not conflict
with other memory regions used by periperals or other drivers.** For example, the region at 0xD0000 may also be used
by the EMS driver, and both drivers shall not be configured to conflict with each other.
The XTMax can create UMBs at any address within the 0xA0000-0xEFFFF range. The only restriction is that the address and
size of the UMB must be a multiple of 2048 bytes (2KB).
The `XTUMBS` driver can accept several ranges for UMBs, as shown in the example below with two distinct ranges:
```
DEVICE=A:\XTUMBS.SYS A000-B000 D000-E000
```
**Note that the XTMax only enables RAM for the UMBs upon loading the `XTUMBS` driver. Therefore, tools such as
[`TEST!UMB.EXE](https://raw.githubusercontent.com/MicroCoreLabs/Projects/master/XTMax/Drivers/TEST!UMB.EXE) cannot be used to
identify available UMBs.** Instead, the user must identify the unavailable ranges (such as video RAM or ROMs) with tools
like CheckIt! and determine which ranges are safe to use as UMBs.

BIN
XTMax/Drivers/TEST!UMB.EXE Normal file

Binary file not shown.

2
XTMax/Drivers/USEUMB/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
*.sys
*.exe

20
XTMax/Drivers/USEUMB/.vscode/tasks.json vendored Normal file
View File

@ -0,0 +1,20 @@
{
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "2.0.0",
"tasks": [
{
"label": "Build with NASM",
"type": "shell",
"command": ".\\build.cmd && copy *.SYS ..\\",
"options": {
"cwd": "${workspaceFolder}"
},
"problemMatcher": [],
"group": {
"kind": "build",
"isDefault": true
}
}
]
}

View File

@ -0,0 +1,24 @@
==============================================================
Please read the manual. It's only 14 pages, several of which
you may skip. It is included in this archive under the name
USE!UMBS.DOC. If you want to print it out (which I strongly
recommend), simply type this command at the dos prompt:
TYPE USE!UMBS.DOC >LPT1
--------------------------------------------------------------
I dedicated all of this software to the public domain. Feel
free to copy it and give it to anyone you wish. If you do so,
please copy the *entire* archive, with all of the following
files included:
--------------------------------------------------------------
README : this file Marco van.Zwetselaar
USE!UMBS.DOC : documentation Oorsprongpark 5
USE!UMBS.SYS : the device driver 3581 ES Utrecht (NL)
USE!UMBS.ASM : the source code Fido NetMail 2:281/701
TEST!UMB.EXE : tests for UMBs Phone 030-313128
==============================================================

View File

@ -0,0 +1,217 @@
TITLE Test_umb will test for Upper Memory Blocks
CR EQU 0Dh
LF EQU 0Ah
fwd EQU 00h
bwd EQU 0FFh
yes EQU 00h
no EQU 0FFh
cseg segment public
assume cs:cseg, ds:cseg, es:nothing
;-------------------------------------------------------------------
; Code
init: push cs
pop ds
mov ah, 09h
mov dx, offset hello$
int 21h
cld ;lets be sure
mov es, [initadr] ;start there (A000)
nxtBlk: call ChkRAM
mov [jmpsze], 0400h ;take 400h size steps
mov [direct], fwd ;move to the right again
cmp [flip], yes
jnz short sayNo
sayYes: mov dx, offset yesRAM$ ;found RAM
mov [flip], yes ; indicate with flip1
jmp short showMs
sayNo: mov dx, offset noRAM$ ;found no RAM
mov [flip], no ; indicate with flip0
showMs: call messge ;and tell it
move: cmp [direct], bwd ;backward?
jz short backw
mov di, es
add di, [jmpsze] ;jump forward
mov es, di
jc endIt
jmp short cont
backw: mov di, es
sub di, [jmpsze] ;jump backward
mov es, di
cont: call ChkRAM ;check location for RAM
jz move ;if no flip - keep jump'n
xor [direct], 0FFh ;invert direction
mov ax, [jmpsze] ;get jmpsze
shr ax, 1 ;divide by 2
mov [jmpsze], ax ;and store new
jnz move ;jump other way half dist'ce
cmp [direct], bwd ;if direction bwd, then we are
jnz goOn ;already on begin new blk, so we
mov di, es ;we must recede one position
dec di ;for end of last blk
mov es, di ;so move left one paragraph
xor [flip], 0FFh ;don't worry, we'll flip back
goOn: call endMes ;print this (end)location
xor [flip], 0FFh ;but if stand before RAMflip wrong
mov di, es ;and proceed to begin next block
inc di
mov es, di
jmp nxtBlk
endIt: mov dx, offset last$
mov ah, 09h
int 21h
mov dx, offset bye$
int 21h
mov dx, offset warn$
int 21h
mov ax, 4C00h
int 21h
;-------------------------------------------------------------------
; Variables
jmpsze dw ? ;jumpsize
initadr dw 0A000h ;starting address
direct db fwd ;direction
flip db yes ;ram at last loc
hello$ db CR,LF,'ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ»',CR,LF, \
'º Test_UMB checks for RAM in Upper Memory º',CR,LF, \
'º ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ º$'
yesRAM$ db CR,LF,'º ³ Found RAM at paragraph $'
noRAM$ db CR,LF,'º ³ None at paragraph $'
contd$ db '#### until $'
endMes$ db '#### ³ º$'
last$ db 'FFFF ³ º',CR,LF,'$'
bye$ db 'º ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ º',CR,LF, \
'º Author: Marco van Zwetselaar 8 Nov 1991 º',CR,LF, \
'ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍͼ',CR,LF,'$'
warn$ db CR,LF,'PLEASE NOTE: Not all RAM displayed can be used!',CR,LF, \
' In *general*, segment A, D and E are safe.',CR,LF,CR,LF,'$'
;-------------------------------------------------------------------
; Procedures
chkram proc near
;takes : es : is paragraph to test
;destr : di, bx, dx, cx
;retur : zeroflag if flips equal, dl=old flip (FF=none, 00=RAM)
xor di, di
mov bx, es:[di] ;get memory contents at es:0
mov dx, bx ;store in dx
xor dx, 0FFFFh ;invert all bits
mov es:[di], dx ;and store in memory again
mov cx, 080h ;Pause a moment, so that
loop $ ;we don't read what we wrote
cmp es:[di], dx ;compare to what we put there
mov es:[di], bx ;and restore original value
mov dl, [flip] ;save old flip
jz flip0 ;found ram?
mov [flip], no ; NOT - flip to no
jmp short pfff
flip0: mov [flip], yes ; YES - flip to yes
pfff: cmp dl, [flip] ;set ZFlag if flipped
ret
chkram endp
messge proc near
;takes : ds:dx points to mess (no/yesRAM)
; : es is segment-addr = paragr
;destr : di, bx, ax
;retur : unimportant - will print mess & addresses
mov ah, 09h ;print yes/noRAM
int 21h
mov dx, es ;paragraph into dx
mov di, offset contd$ ;point di to location where
push es
push cs ; it should be patched in
pop es
mov bx, offset digs ;ds:bx point to begin xtable
mov ax, dx ;dx is paragraph address
xchg al, ah ;ah patched 1st: must be in al
call makehex
mov ax, dx ;then patch al in
call makehex
mov dx, offset contd$ ;then print rest of messge
mov ah, 09h
int 21h
pop es
ret
messge endp
endMes proc near
mov dx, es ;paragraph number in dx=es
mov di, offset endMes$ ;point es:di to patch
push es ;but save es for later!
push cs
pop es
mov bx, offset digs ;point ds:bx to xlat tbl
mov ax, dx
xchg al, ah
call makehex
mov ax, dx
call makehex
mov dx, offset endMes$ ;and show endlocation
mov ah, 09h
int 21h
pop es ;reset es
ret
endMes endp
makehex proc near
;takes : al : byte to be conv'ted to ascii (like FCh - 'FC')
; ds:bx : points to beginning of xlat table
; es:di : points to location where "XX" should come
;destr : ax, di will point two bytes further
;retur : ascii digits in locations es:di and es:di+1
db 0D4h, 010h ;adjust for ascii multip: ah->ax/16
; al->rest
xchg al, ah ;high nibble first
xlat ;ds:bx must point to right dig
stosb ;store the string-byte
xchg al, ah
xlat ;low level
stosb
ret
digs db '0123456789ABCDEF'
makehex endp
cseg ends
end

View File

@ -0,0 +1,470 @@
; 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,'$'

View File

@ -0,0 +1,692 @@
USE!UMBS Documentation Page 1
==============================================================
DOCUMENTATION FOR USE!UMBS.SYS Ver 2.0 Utrecht, 21 Nov 91
--------------------------------------------------------------
PROGRAM USE!UMBS.SYS
Upper Memory Block Manager for PC/XT/ATs
PURPOSE This program is a device driver that will handle
calls for Upper Memory Blocks. In doing so, it gives
you the possibility to save conventional memory by
storing device drivers and resident programs in the
UMBs and hence save conventional memory. It takes up
only 256 bytes of your (conventional) memory and is
fully compatible with MS-DOS 5.0.
REQUIRES It will work on any PC/XT/AT or higher, either with
or without extended memory. What you obviously
*must* have are UMBs, and MS-DOS Version 5.0. It
will not be useful on 386sx or higher.
EXTRA'S Since Video-RAM is RAM in Upper Memory too, you can
use it just as well as other UMBs. You only have to
take care that you use an UNused part of Video memo-
ry.
PROBLEMS Since ExPANded Memory is remapped to the range of
addresses where UMBs would normally be, I think this
program will not cooperate with your expanded memory
manager. Since I could not test the program on many
different configurations, I kindly ask you to report
any problems to me. I don't expect many problems
though, since USE!UMBS is a `friendly' programme: it
complies completely with the rules of MS-DOS 5.0.
AUTHOR Marco van Zwetselaar Phone: 030-313128
Oorsprongpark 5 Email: zwets@rivm.nl
3581 ES Utrecht
The Netherlands
==============================================================
USE!UMBS Documentation Page 2
==============================================================
I wrote this program for my own entertainment, not in order to
make money. Therefore I dedicate it to the Public Domain. Feel
free to copy it and pass it on to friends - just make sure
this documentation file is included.
If you find this program useful enough to reward me for writ-
ing it, you may send me a donation anuwhere from 50 cents upto
25 guilders. My giro number is 5636618, my snailmail address
is displayed above.
If you have any questions, just contact me via one of the
above-mentioned addresses. I will be pleased to help you out -
whether you are a donator or not. Also, if you find any incon-
sistencies or mistakes in this documentation, please notify me
of them, so that I can correct them in a possible next versi-
on. Finally, if you think you can improve the driver, please
do so. For this purpose I have included the asm-code code in
the archive. But if you do so: keep it well documented.
Marco van Zwetselaar.
==============================================================
CONTENTS
1. General Introduction & Definitions 3
2. Preparations 6
3. Using VideoRAM 7
4. Patching in the Addresses 9
5. Installation 12
Appendix 1. Functional description of driver (freaks) 13
Appendix 2. Detailed description of driver (utter freaks) 14
==============================================================
USE!UMBS Documentation Page 3
==============================================================
1. General Introduction & Definition of Terms:
In this first section I will explain how the memory on IBM-
compatibles is organized, and what UMBs actually are. If you
are not interested in `backgrounds', you may skip this section
and move on to section 2 (page 6) rightaway. (I advise you to
at least have a glance at this section though.)
Conventional Memory
Conventional memory is the memory that is present on any
IBM or compatible. Its maximum size is 640K, and nearly
all IBM-compatible computers have exactly that amount.
Conventional memory is used in a conventional way, i.e.
any user program may make use of it, as may the operating
system. Actually, most user programs won't even use
anything except conventional memory (unless specifically
told to do so). Conventional memory resides in the first
640K of the adressable memory. That is, at the addresses
00000-9FFFF.
Addressable Memory
The computer can address more than just 640K of conven-
tional memory. The maximum amount a specific computer can
address depends on the type of CPU it has (8086, 80286,
and so on). But whatever CPU it may have, it can *always*
address the first 1M of memory. (That's why it is called
Adressable Memory, I suppose.)
Addressable Memory consists of two parts: the first 640K
are Conventional Memory (adresses 00000-9FFFF), the
remaining 384K are Upper Memory (adresses A0000-FFFFF).
Upper Memory
Upper Memory is the upper 384K of Adressable Memory,
which begins just beyond the 640K conventional memory
border. This implies that it is located at the addresses
A0000-FFFFF.
Since Upper Memory is normally addressable on any PC, you
might wonder why programs don't make use of it. The
reasons are the following:
(1) The operating system and the hardware make use of
parts of it already. For example, anything you see
on the screen is stored in an area in UM - you can
probably imagine what will happen if you mess around
in that area. Also the computer itself will store
some of its vital data in UM - and we don't want to
embarrass MS-DOS, who's having hard times already.
==============================================================
USE!UMBS Documentation Page 4
==============================================================
(2) Even if there are parts of Upper Memory that are not
in use already, these parts may not be RAM. What
this means is that one may *read* data from those
locations, but not *write* anything into them. (In
fact, you can try and write something into them, but
it will vanish mysteriously into thin air.)
Most computers don't have RAM in these areas, first-
ly because that would make the computer more expen-
sive, and secondly because MS-DOS was not designed
to use anything above the 640K border anyway.
(3) Even if there are unused parts of UM that do consist
of RAM (readable & writeable memory), dos will not
normally make them available as conventional memory.
This is precisely why I wrote this device driver:
since Version 5.0, Dos can use the UMBs to store
prog- rams, namely device drivers and TSRs. Device
drivers are the files that you install via the DEVI-
CE=... lines in your config.sys; they take care of
interfacing dos with your hardware. Dos usually
loads these devices in conventional memory. If you
have UMBs (or a 386) you can load the devices into
them using the DEVICEHIGH=... statement, instead of
the DEVICE=... statement. TSRs (Terminate and Stay
Resident) programs are programs that stay in memory
after they are executed. Some of these programs stay
resident so that you can invoke them during other
programs using a HotKey (sidekick for example),
others stay resident because they perform tasks in
the background (like screenblankers, autopark, dos-
key, fastopen, etc). Normally, TSRs will be loaded
into conventional memory. Dos 5.0 provides the com-
mand LOADHIGH (may be abbreviated to LH) to put them
into the UMBs. You do this by preceding the TSR's
invocation line in the autoexec.bat by LH (or LOAD-
HIGH). So, if you 'd normally use "FASTOPEN C:=200",
you now put "LH FASTOPEN C:=200" in the autoexec.
Extended Memory
Extended Memory is, by definition, all memory that is
located beyond the 1MB border. So, its addresses start at
100000. Since a computer needs to have more than 20
address lines in order to address such large addresses,
PCs and XTs can't have extended memory (they have preci-
sely 20 lines). On an AT or higher one can access exten-
ded memory by enabling address lines A20 (and higher - if
you start counting at 0, that is the 21st line and hig-
her).
==============================================================
USE!UMBS Documentation Page 5
==============================================================
So in order to make use of extended memory, a program
must have a special design - more and more programs are
offering eXtended memory support. But since many programs
use eXtended memory in many different ways, conflicts may
arise. In order to resolve these, a standard way of
accessing extended memory was developed.
This is specified in the XMS (eXtended Memory Specifica-
tion). MS-Dos 5.0 provides you with a manager for XM -
this manager will 'hand out' extended memory to programs
that make a request according to XMS specs. The problem
with dos's XMS-manager (HIMEM.SYS) is that it doesn't
handle requests for UMBs (they simply haven't implemented
that function, probably because most XTs and ATs don't
have UMBs anyway). What MicroSoft does provide is
EMM386.EXE, which is an exPANded memory manager that
handles UMB-requests as a side-effect. The problem is
that this manager can only be installed on 386s or hig-
her. The reason for this is that only a 386 has the
ability to 'remap' expanded memory to UMB locations, thus
providing RAM in Upper Memory locations.
So, what can you do if you have UMBs on an XT or AT?
Suppose you have extended memory - so you can install
HIMEM.SYS - then you still can't use the UMBs because
EMM386.EXE won't work on your machine... Now say you
have no extended memory *at all* (which will always be
the case on an XT), then you can't load himem in the
first place! In both cases, USE!UMBS will be the right
thing to use.
HMA - High Memory Area
The HMA is the first block of 64K of extended memory (so
with addresses 100000-10FFFF). Since Dos 5.0, it is
possible to load the system files into this area. (System
files are *not* the devices that you install: dos in-
stalls them at boot-time!)
In order to highload these systemfiles, you must have
extended memory, and insert the lines DEVICE=HIMEM.SYS
and DOS=HIGH at the front of your config.sys file. If you
don't have an HMA, then you can not use dos=high. You can
use dos=umb though.
Expanded Memory
Expanded memory can't be defined in terms of 'adresses',
since it is organized altogether differently. It resides
on a separate 'card' that you plug into a free slot in
your computer, and it cannot be accessed all at once but
in 'pages'.
==============================================================
USE!UMBS Documentation Page 6
==============================================================
What happens is that pages of say 16K are 'remapped' from the
expanded memory board to addresses in Upper Memory. If the
computers wants to find something in an area elsewhere on the
card, an expanded memory manager must take care of storing the
present page and making the new page active. As was the case
with XMS, people have also devised a standard for access to
expanded memory. This standard is the LIM/EMS specification
(now at revision 4.0). Dos 5.0 provides a manager for it:
EMM386.EXE. As said before, this manager will only work on a
386, and since it takes care of the UMBs too, you are advised
to rather use EMM386 when you have a 386 or higher. (USE!UMBS
will, in fact, not install if it finds out that a UMB manager
was installed already.)
==============================================================
2. Preparations
This program will install a UMB manager on any PC, XT, AT or
higher, whether it has extended or expanded memory or not. In
order to be able use it, it must have UMBs, i.e. holes in the
memory-area between 640k and 1M that are filled with RAM.
How do you find out whether you have UMBs? Firstly, let me say
that there are not many PC/XT/ATs that do have them (refer to
section 1 if you want to find out why). One XT that I know of
certainly has them, namely the Philips 31xx series XTs. I ori-
ginally wrote the program for precisely that machine. Later on
I found out it might be useful for other machines as well.
If you want to find out whether you have UMBs, run the program
TEST!UMB.EXE. This program will run through the upper memory
and try if it can write information there. (By the way: don't
worry. It won't destroy anything while doing that!)
The locations where TEST!UMB can succesfully change values are
RAM locations. It will display a table with its findings, sta-
ting:
"None at paragraphs xxxx until xxxx" or
"Found RAM at paragraphs xxxx until xxxx"
Don't be too optimistic if it displays a range of adresses
where it finds RAM: it will always find at least one such
range! That range is occupied with Video-RAM, and you can't
simply use all of that as a UMB (refer to section 1 for de-
tails).
==============================================================
USE!UMBS Documentation Page 7
==============================================================
So now comes the tricky bit: which ranges can you use?
First of all, if TEST!UMB shows that there is RAM in the range
D000 until EFFF, then you have "true" UMBs. And you can use my
device driver rightaway. These two blocks (the D and the E
block) provide you with 128K of Upper Memory - probably by far
enough to store all of your device drivers and TSRs.
So, if you are in that lucky situation and don't want to
complicate matters, you can move on to section 5 (Installa-
tion, page 12) rightway.
If you were less fortunate and TEST!UMB showed that there is
no RAM in that range, or in only a part of that range, or if
you don't want to use all of that range, or if you want to use
more than one range, then you should read section 3 and/or
section 4.
==============================================================
3. Using VideoRAM or other ranges as a UMB
If you don't have RAM in the D000-EFFF range, or if you want
to use a different range of memory, then you must make a small
modification to the driver: you must `patch in' the addresses
that it should manage.
This may sound difficult, and yes, it is not simple... The
point is that I had wanted to use command line parameters to
specify the range(s) the driver should manage... but I don't
know how to program that option. [So, If YOU are a proficient
Assembly programmer, please change the source code and include
that option!]
Anyway, let's go for it:
If TEST!UMB showed that you have RAM outside the D000-EFFF
range, that RAM may be of three kinds:
(1) Completely free RAM - not used by the videocard or by any
other program. This is good news: you can use it as a UMB
without any problems - you only have to patch the addres-
ses into USE!UMBS.SYS.
(2) Graphics VideoRAM - this is only used by the video system
when you are working in graphics mode. If you don't use
graphics, you may use this RAM as a UMB. Beware to boot
your computer without USE!UMBS.SYS if you intend to use
graphics programs! (It won't damage anything, though,
your computer will simply hang once it switches to grap-
hics mode.)
(3) Text VideoRAM - sorry, but you really can't use this. If
you would, then how could you get anything on the screen?
==============================================================
USE!UMBS Documentation Page 8
==============================================================
Finding out to which of the three categories your RAM belongs
is complicated: it depends on your videocard. I will try and
describe as well as I can what ranges each videocard uses. As
said above: you can use any range as long as it doesn't inclu-
de the Text Range; and you may use the Graphics Range, but
only if you don't switch to graphics mode. Read the Advice
carefully - but note that this doesn't guarantee anything!
(Thanks to Eef Hartman for the information about addresses.)
Monochrome Display Adapter (MDA)
TEXT : B000-B0FF (4K)
GRAPHICS : None (0K)
ADVICE This is a very old-fashioned one. It was in the
original IBMs. It can't do any graphics, only text.
So if you appear to have *any* RAM outside the text
range specified above: use it.
Color Graphics Adapter (CGA)
TEXT : B800-B8FF (4K)
GRAPHICS : B800-BBFF (16K)
ADVICE If there is any RAM outside the graphics range
(B800-BBFF) then that RAM is not used by the CGA
adaptor, and you may problemlessly use it.
If you want to use the graphics range, take care:
this may give problems since the CGA adapter often
uses it as 4 pages of text.
Hercules Adapter
TEXT : B000-B0FF (4K)
GRAPHICS : B000-BFFF (64K) full
or : B000-B7FF (32K) half
ADVICE If there is any RAM outside the graphics range
(B000-BFFF) then that RAM is not used by the Hercu-
les adaptor, and you may problemlessly use it.
If you want to use the graphics range, take care:
the hercules adapter may use the first half of its
RAM (B000-B7FF) to store several text pages. So if
you want to use the graphics range, use B800-BFFF.
If you have a half (1 page) hercules, then you don't
have that range.
==============================================================
USE!UMBS Documentation Page 9
==============================================================
Enhanced Graphics Adapter (EGA)
TEXT : B000-B0FF (4K) mono mode
: B800-B8FF (4K) color mode
GRAPHICS : A000-AFFF (64K)
ADVICE As you see, you can use the A segment as a UMB if
you don't use graphics applications.
You may also use parts of the B segment, but notice
that the EGA card will use one of the two text are-
as: the lower one when it is in mode MONO, the upper
one when it is in mode CO80.
V? Graphics Adapter (VGA)
TEXT : same as EGA
GRAPHICS : same as EGA, but sometimes also the range
B000-BFFF (64K)
ADVICE There is a large variety of VGA cards. I can't tell
you precisely what ranges you may use. What is sure
is that you can follow the advice of the EGA card:
as long as you don't use graphics, the A000-AFFF
range is at your disposal.
==============================================================
4. Patching in the addresses
This is the hardest bit. As I mentioned before, I wrote this
driver for the Philips 35xx series, so it will by default only
manage the block from D000 until EFFF. If you don't have that
entire block at your disposal, you will have to change some
code in the file. I will describe below how you can patch in
the adresses using the DEBUG program, which you will have,
since it came with the MS-DOS package.
First of all, you need to know the addresses of the block(s)
you want to patch in. The maximum number of separate blocks
you can patch in is three. I don't think you will need more
than that; if you think you do, contact me, and I can fix it
for you. (As long as the RAM is contiguous, you can specify it
in one block, however long the contiguous block is.)
Once you know the starting and the ending address of a block,
you should calculate its length (in paragraphs). How do you do
that? You simply subtract the beginning address from the end
adress, USING HEXADECIMAL CALCULATION. [Hex calculation goes
just like decimal calculation, only that the numbers 10
through 15 are changed to A through F]
==============================================================
USE!UMBS Documentation Page 10
==============================================================
Some examples:
End address : AFFF BFFF BBFF E7FF EFFF EFFF
Begin address : A000 B800 B0FF E000 D7FF D000
------------- - ---- ---- ---- ---- ---- ----
Length : 0FFF 07FF 0B00 07FF 1800 1FFF
So, what you do is (just as with decimal calculation): go from
right to left and each time subtract two digits. If you have
to `borrow' you can do so. Keep in mind that e.g. F-7=8 (bec-
ause 15-7=8) and that 10-8=8, because 16-8=8.
Ok, now you must patch the starting address and the length of
each block into USE!UMBS.SYS. Only ... there is a twist now:
both values have to be reversed bytewise before being patched
in. It's best to explain this using an example: suppose you
have a block, which starts at B800 and has length 07FF, then
you reverse the bytes as follows:
Address Length
B8 00 07 FF
\ / \ /
/ \ / \
00 B8 FF 07
So, the sequence B800 07FF becomes 00 B8 FF 07. And this is
the sequence we will patch in. If you have more blocks, trans-
pose them in the same way, and append them to this sequence.
(But *never* more than three blocks in total!!!)
Then startup the debug program with the following command:
DEBUG USE!UMBS.SYS
and debug will report with its prompt:
-
(if this doesn't happen: make sure debug is in the search path
and use!umbs.sys is in the current directory. You can type Q
to exit from debug).
Now type:
-E153
(don't type the hyphen, and finish with carriage return)
and debug will say this
xxxx:0153 00._
==============================================================
USE!UMBS Documentation Page 11
==============================================================
Now, type the first byte of your sequence, and finish with a
SPACE, NOT A CARRIAGE RETURN!!!. So, if your first byte was
(for example) AB, you will now see something like this:
xxxx:0153 00.AB D0._
Now, type the next byte, AGAIN FOLLOWED BY A SPACE, NOT A
CARRIAGE RETURN! And keep on doing this until you have entered
the whole sequence (which amounts to 4 bytes for one block, 8
bytes for two blocks, 12 for three blocks). After having
completed this, STILL DON'T PRESS CARRIAGE RETURN, but enter
another four bytes, all with value 00, every time using the
spacebar to move to the next. (These 00-bytes signal the end
of the list.)
If you have done that, you may now finally press RETURN to get
back to the debug-prompt. If you made any mistakes, you can
now press Q to quit without changes, but if everything went
alright, press W to write away the changes. (And afterwards Q
to exit the programme.)
Well, that was it... Now you can continue to the next section
and finally install USE!UMBS.SYS.
==============================================================
USE!UMBS Documentation Page 12
==============================================================
5. Installation
In order to install USE!UMBS.SYS, follow the following three
steps carefully.
STEP I: Tell DOS to install the device driver.
You do this by adding this line to your config.sys:
DEVICE=USE!UMBS.SYS
(Provided USE!UMBS.SYS is in the root directory.)
The position at which this line is placed is important! If you
have an extended or expanded memorymanager (like HIMEM.SYS),
then the line DEVICE=USE!UMBS.SYS must come *after* the line
specifying the other manager. On the other hand, it must come
*before* any other line that starts with DEVICE=.
On the whole: put it as much as possible toward the beginning
of your config.sys, but never before the installation line of
an XMS or EMS driver.
STEP II: Tell DOS to actually use the UMBs
You do this as follows: If there is a line saying DOS=HIGH in
your config.sys, then change it to DOS=HIGH,UMB. If there is
no such line, then add the line DOS=UMB to the config.sys.
Also here, position is important: make sure the line is on the
*very first* line of your config.sys. (And therefore comes
somewhere before DEVICE=USE!UMBS.SYS)
STEP III: Tell DOS which things to put in the UMBs
There are two kinds of things that can be put in the UMBs:
device drivers and TSRs. Device drivers are `highloaded' by
changing their lines in the config.sys from DEVICE=... to
DEVICEHIGH=.... The TSRs are highloaded by preceding their
invocation line in the autoexec.bat with LOADHIGH (or LH). So:
if there formerly was a line saying AUTOPARK in your auto-
exec.bat, now it should become LH AUTOPARK.EXE or LOADHIGH
AUTPARK.EXE.
[NOTE: Some people load TSRs via the config.sys instead of the
autoexec.bat by using the line INSTALL=.... If this is the
case, you better remove them from the config.sys and LOADHIGH
them via the autoexec.bat.]
Well, that's all there is to it.
Once you have made all the changes, try MEM to see how much
memory you have left. Then reboot (and see USE!UMBS sign on),
wait for the DOS-prompt and then run MEM again. Calculate the
difference USE!UMBS makes. Now divide this difference by 2000.
The result is - in my humble opinion - a reasonable donation
for the author. (But, of course, it's up to you...)
==============================================================
USE!UMBS Documentation Page 13
==============================================================
Appendix 1. Functional description of the driver
(for the freaks).
This program is a device driver that will install a routine
that handles requests for UMBs. These requests are made by
MS-Dos if it has read the statement DOS=[high,]UMB in the
config.sys file and encounters a DEVICEHIGH or a LOADHIGH
statement. (For more information about devicehigh and loadhigh
see the MS-Dos 5.0 manual.)
The UMB-requests are normally sent to the extended memory
manager, which, in the case of Dos 5.0, is HIMEM.SYS. There
are two problems here. Firstly, if you don't have extended
memory, you cannot have Himem.sys installed. And since PCs and
XTs can't even have extended memory, they can never install
himem.sys. Secondly, even if you DO have extended memory AND
install Himem.sys, you still have no access to the UMBs becau-
se Himem doesn't implement a UMB manager, only an XMS handler.
The UMB-handler is provided by a separate device driver,
namely EMM386.SYS. And yes, as the name suggests, you can't
install this on an AT (286).
So, what this USE!UMBS does is the following: it will install
a routine that will intercept any requests to the extended
memory manager. It then checks this request in order to see if
it is a request for UMBs. If it is, then it will handle the
request, otherwise it will pass them on to the XMS handler. If
you don't have an XMS driver installed (which is quite fair if
you don't have eXtended memory), USE!UMBS will not forward the
request but answer it with a polite "no", so that nothing will
hang or mess up. (So, in a way, USE!UMBS will install a HIMEM
manager too - but since there is no high memory to manage, it
won't have much to do anyway.)
==============================================================
USE!UMBS Documentation Page 14
==============================================================
Appendix 2. Technical description of the driver
(details for the real freaks)
A request to the XMS manager goes in two steps. First, the
caller will want to find out whether there is a manager and,
if so, where it is. Second, it will call the manager with a
specification of what it should do.
The first step is done via interrupt 2Fh (the multiplexer).
This interrupt handles a lot of very different requests, which
it classifies by looking in the AH register. If this contains
43h, the request is for the XMS manager. So what we do is
chain a little bit of code to the front of the interrupt 2Fh
handler - this added code will determine if AH=43h. If it is
not, it gives control back to the old INT 2F handler, if it
is, it will take over.
Once it has taken over, it will check the AL register. This
register specifies the precise nature of the question. It can
contain only two possible values: 00h and 10h. If it contains
00h, this means that the question is "Hullo! Is there any
XMS-manager installed?", and our response should be "Yo!"
(because that's exactly what we are installing). We signal a
yes by returning 80h in the AL register. If AL contains 10h
upon entry, then the question is "Well then, where can I find
that XMS manager?". So in this case we return its address in
the registers ES:BX. The address we pass is, yes, the address
of the XMS manager that we are installing.
The second step. After the caller has used the interrupt
described above to find out about the existence (4300) and the
whereabouts (4310) of the XMS manager, it will at some point
call it. It calls it by simply making a FAR JMP to the address
that was previously specified in ES:BX.
Upon entering the XMS manager, we first have to check its AH
register in order to find out whether the request is for UMBs
or for extended memory. If it is anything else than 10h (= "I
want a UMB"), our UMB-manager will do the following: (a) IF
another XMS-manager (like MS-Dos's HIMEM.SYS) was loaded
before, it will pass the request on to that manager, (b) IF
NOT, it will return an errorcode saying that there is no XMS
memory. If the request actually is for UMBs (AH=10h), then it
will provide them as long as there are any.
[END OF DOCS]
==============================================================

View File

@ -0,0 +1 @@
..\Driver_Build_Tools\NASM\nasm.exe -f bin -o XTUMBS.SYS USE!UMBS.ASM

Binary file not shown.

BIN
XTMax/Drivers/XTUMBS.SYS Normal file

Binary file not shown.