'=========================================================================== ' Subject: COMPLETE EMS ROUTINES FOR QB Date: 10-02-99 (21:10) ' Author: Aaron Severn Code: QB, PDS ' Origin: rlsevern@idirect.com Packet: MEMORY.ABC '=========================================================================== '**************************************************************************** ' EMS.BAS ' Programmed by Aaron Severn (bob_severn@msn.com) ' Oct 10/97 - Oct 11/97 ' ' This program contains several routines for using EMS in QuickBasic. The ' routines included are used for detecting EMS, allocating and deallocating ' EMS, and reading and writing to EMS. Each is explained in detail below. ' ' It seems this program doesn't run properly in Windows, so run it from DOS. ' Also, you'll need run QB with the qb.qlb library loaded. ' ' This code is public domain. I am in no way responsible for any damage of ' any kind caused by it. If it doesn't cause any damage and you find it ' useful and use it in your own programs please give me the credit I deserve. ' If you have any questions on how to use these routines, feel free to e-mail ' me and I'll do my best to explain. ' ' SetUpEMS - Details - This routine runs three routines need to detect ' and set up the EMS. Use this routine instead ' using GetEMSStatus, GetPageCount, and ' GetPageFrameSeg separately. ' GetEMSStatus - Details - Detects EMS and makes sure it's working ' properly. This routine is run by SetUpEMS. ' - Returns - 0: EMS is present and working properly; <> 0: ' EMS is not present or not working properly. ' GetPageCount - Details - Gets the total number of EMS pages and the ' total number of available EMS pages. This ' routine is run by SetUpEMS. ' - Params - free: the total number of available EMS pages ' is returned in this variable; total: the total ' number of EMS pages is returned in this ' variable. ' - Returns - 0: successful; <> 0: failed. ' GetPageFrameSeg - Details - Returns the segment address of the EMS physical ' page frame (in conventional memory). This ' routine is run by SetUpEMS. ' - Returns - 0: successful; <> 0: failed. ' AllocatePages - Details - Allocates the specified number of EMS pages ' to the program. ' - Params - numPages: number of pages to be allocated. ' - Returns - If successful the handle of the EMS pages ' (used for referencing the pages later) is ' returned. If failed 0 is returned. ' DeallocatePages - Details - Deallocates the pages associated with an EMS ' handle. Must be run at the end of a program ' using EMS otherwise the computer will think ' the memory is still in use. ' - Params - handle: the EMS handle returned by the ' AllocatePages routine. ' - Returns - 0: successful; <> 0: failed. ' MapPages - Details - Maps conventional memory pages to EMS pages. ' See details on EMS for a better explanation. ' - Params - physicalPage: the conventional memory page ' to be mapped to (0, 1, 2, or 3); logicalPage: ' the EMS page to be mapped to (0 to number of ' pages allocated - 1); handle: the EMS handle ' returned by AllocatePages. ' - Returns - 0: successful; <> 0: failed. ' WriteByte - Details - Writes a byte into a conventional memory page ' that has been mapped to an expanded memory ' page, thus writing to EMS. ' - Params - physicalPage: the conventional memory page to ' write the byte to; offset: the offset of where ' to write the byte in conventional memory; byte: ' the byte to be written. ' ReadByte - Details - Reads a byte from a conventional memory page ' that has been mapped to an expanded memory ' page, thus reading from EMS. ' - Params - physicalPage: the conventional memory page to ' read from; offset: the offset of where to read ' the byte from. ' - Returns - the byte at the desired location. ' CopyToEMS - Details - Copies a portion of memory (up to 1MB) from ' conventional memory into expanded memory. ' - Params - length&: the length in bytes of the region to ' be copied; startSeg: the segment address of the ' first byte to be copied; startOff: the offset ' address of the first byte to be copied; handle: ' the EMS handle of the expanded memory to copy ' to; logicPage: the EMS page to copy to; offset: ' the offset of where the first byte will be ' copied. ' - Returns - 0: successful; <> 0: failed. ' CopyFromEMS - Details - Copies a portion of expanded memory (up to 1MB) ' into conventional memory. ' - Params - length&: the length in bytes of the region to ' be copied; handle: the EMS handle of the ' expanded memory to copy from; logicPage: the ' EMS page to copy from; startOff: the offset ' of the first byte to be copied; endSeg: the ' segment address of where the memory will be ' copied; endOff: the offset address of where the ' memory will be copied. ' - Returns - 0: successful; <> 0: failed. ' ' The details of how EMS works. ' ' EMS is divided into hundreds (depending on how much memory you have) of ' 16k "pages" of memory. Thus when you allocate one page you are allocating ' 16k of memory to your program. In order to read and write to EMS you can ' either use a function provided by EMS to copy memory (useful for copying ' large amounts of memory) or read and write bytes manually (better for ' copying smaller amounts of memory). The first one is easy to do using ' the functions provided with this code, the second is more difficult. ' ' When you write to EMS you actually write to conventional memory but the ' memory is placed into EMS because the conventional memory has been mapped ' to EMS. So you must first map the conventional memory to EMS. ' ' Like EMS, the mappable conventional memory is divided into 16k pages. ' There are 4 of them available for use. Use the MapPages function to ' map a physical page (conventional) to a logical page (EMS). Then if ' you write a byte to the conventional memory using the WriteByte routine ' the byte will go straight into EMS. Similarly, if you read from a ' mapped physical page, you read from the logical page that is mapped to it. '**************************************************************************** DEFINT A-Z DECLARE FUNCTION AllocatePages (numPages) DECLARE FUNCTION CopyFromEMS (length&, handle, logicPage, startOff, endSeg, endOff) DECLARE FUNCTION CopyToEMS (length&, startSeg, startOff, handle, logicPage, offset) DECLARE FUNCTION DeallocatePages (handle) DECLARE FUNCTION GetEMSStatus () DECLARE FUNCTION GetPageCount (free, total) DECLARE FUNCTION GetPageFrameSeg () DECLARE FUNCTION MapPages (physicalPage, logicalPage, handle) DECLARE SUB SetUpEMS () DECLARE FUNCTION ReadByte (physicalPage, offset) DECLARE SUB WriteByte (physicalPage, offset, byte) ' pfSeg holds the segment address of the mappable conventional memory. Don't ' forget to include this variable. COMMON SHARED pfSeg TYPE RegType 'Dos registers for BIOS calls. AX AS INTEGER BX AS INTEGER CX AS INTEGER DX AS INTEGER BP AS INTEGER SI AS INTEGER DI AS INTEGER FLAGS AS INTEGER DS AS INTEGER ES AS INTEGER END TYPE TYPE MemCopyType 'Type for info that is sent length AS LONG 'to the expanded memory sourceType AS STRING * 1 'manager when memory copies sourceHandle AS INTEGER 'are performed. sourceOffset AS INTEGER sourceSeg AS INTEGER destType AS STRING * 1 destHandle AS INTEGER destOffset AS INTEGER destSeg AS INTEGER END TYPE ' Since the program plays around in memory, variables should be dynamic to ' make sure it's all consistent. '$DYNAMIC DIM SHARED inRegs AS RegType 'Dos registers. DIM SHARED outRegs AS RegType ' This makes sure that if an error occurs the expanded memory that the ' program is using will be returned to the expanded memory manager. ON ERROR GOTO endHere CLS SetUpEMS EMSHandle = AllocatePages(12) 'Allocate some EMS pages IF EMSHandle THEN PRINT "Expanded memory pages allocated successfully." ELSE PRINT "Could not allocate expanded memory pages." SYSTEM END IF SLEEP SCREEN 13 CLS ctr = 0 FOR y = 0 TO 199 FOR x = 0 TO 319 ctr = (ctr + 1) MOD 256 PSET (x, y), ctr NEXT NEXT SLEEP IF CopyToEMS(&HFA00&, &HA000, 0, EMSHandle, 0, 0) THEN PRINT "Could not copy to EMS." isGood = DeallocatePages(handle) 'Attempt to copy what's on SYSTEM 'the screen into the EMS ELSE 'that was allocated. CLS PRINT "The screen has been copied to EMS." END IF SLEEP CLS FOR i = 0 TO 99: LINE (0 + i * 1.6, i)-(319 - i * 1.6, 199 - i), i, BF: NEXT SLEEP IF CopyToEMS(&HFA00&, &HA000, 0, EMSHandle, 4, 0) THEN PRINT "Could not copy to EMS." isGood = DeallocatePages(handle) 'Attempt to copy what's on SYSTEM 'the screen into the EMS ELSE 'that was allocated. CLS PRINT "The screen has been copied to EMS." END IF SLEEP CLS FOR i = 319 TO 0 STEP -1 LINE (0, i)-(ABS(i - 319), 199), i LINE (319, ABS(i - 319))-(319 - ABS(i - 319), 0), i NEXT i SLEEP IF CopyToEMS(&HFA00&, &HA000, 0, EMSHandle, 8, 0) THEN PRINT "Could not copy to EMS." isGood = DeallocatePages(handle) 'Attempt to copy what's on SYSTEM 'the screen into the EMS ELSE 'that was allocated. CLS PRINT "The screen has been copied to EMS." END IF SLEEP startPage = 0 DO IF CopyFromEMS(&HFA00&, EMSHandle, startPage, 0, &HA000, 0) THEN PRINT "Could not copy from EMS." isGood = DeallocatePages(EMSHandle) 'Attempt to copy what was SYSTEM 'just copied into EMS back END IF 'on to the screen. startPage = (startPage + 4) MOD 12 endTime! = TIMER + .5 DO: LOOP WHILE endTime! > TIMER AND INP(&H60) <> 1 LOOP UNTIL INP(&H60) = 1 SCREEN 0: WIDTH 80: CLS ' This section of code demonstrates reading and writing individual bytes. ' Note that two different values are written to the same place in ' conventional memory, the only difference is the memory is mapped to a ' different location in expanded memory. When the bytes are read (both from ' the same location in conventional memory) the first byte has not been ' overwritten since it was stored in EMS, not conventional memory. physPage = 0: logicPage = 0 IF MapPages(physPage, logicPage, EMSHandle) THEN GOTO endHere byte = 64 WriteByte physPage, 0, byte PRINT "A value of"; byte; "was written to physical page"; physPage; "mapped to logical page"; logicPage; "at offset 0." physPage = 0: logicPage = 1 IF MapPages(physPage, logicPage, EMSHandle) THEN GOTO endHere byte = 16 WriteByte physPage, 0, byte PRINT "A value of"; byte; "was written to physical page"; physPage; "mapped to logical page"; logicPage; "at offset 0." physPage = 0: logicPage = 0 IF MapPages(physPage, logicPage, EMSHandle) THEN GOTO endHere byte = ReadByte(physPage, 0) PRINT "A value of"; byte; "was read from physical page"; physPage; "mapped to logical page"; logicPage; "at offset 0." physPage = 0: logicPage = 1 IF MapPages(physPage, logicPage, EMSHandle) THEN GOTO endHere byte = ReadByte(physPage, 0) PRINT "A value of"; byte; "was read from physical page"; physPage; "mapped to logical page"; logicPage; "at offset 0." endHere: ' Call the DeallocatePages function to give the memory back to the expanded ' memory manager. It is important that you do this at the end of the program ' otherwise EMM will think the memory is still in use and other programs ' won't be able to use it. You could end up running out of memory. IF DeallocatePages(EMSHandle) THEN PRINT "Could not deallocate expanded memory pages." SYSTEM ELSE PRINT "Expanded memory pages deallocated successfully." END IF REM $STATIC ' BIOS call to allocate EMS pages. FUNCTION AllocatePages (numPages) inRegs.AX = &H4300 inRegs.BX = numPages CALL interrupt(&H67, inRegs, outRegs) AllocatePages = outRegs.DX IF (outRegs.AX \ 256) THEN AllocatePages = 0 END FUNCTION ' BIOS call to run an extremely quick memory copy routine that is provided ' by EMS. FUNCTION CopyFromEMS (length&, handle, logicPage, startOff, endSeg, endOff) DIM moveStruct AS MemCopyType moveStruct.length = length& moveStruct.sourceType = CHR$(1) moveStruct.sourceHandle = handle moveStruct.sourceOffset = startOff moveStruct.sourceSeg = logicPage moveStruct.destType = CHR$(0) moveStruct.destHandle = 0 moveStruct.destOffset = endOffset moveStruct.destSeg = endSeg inRegs.AX = &H5700 inRegs.DS = VARSEG(moveStruct) inRegs.SI = VARPTR(moveStruct) CALL interruptx(&H67, inRegs, outRegs) CopyFromEMS = outRegs.AX \ 256 END FUNCTION ' BIOS call to run an extremely quick memory copy routine that is provided by ' EMS. FUNCTION CopyToEMS (length&, startSeg, startOff, handle, logicPage, endOffset) DIM moveStruct AS MemCopyType moveStruct.length = length& moveStruct.sourceType = CHR$(0) moveStruct.sourceHandle = 0 moveStruct.sourceOffset = startOff moveStruct.sourceSeg = startSeg moveStruct.destType = CHR$(1) moveStruct.destHandle = handle moveStruct.destOffset = endOffset moveStruct.destSeg = logicPage inRegs.AX = &H5700 inRegs.DS = VARSEG(moveStruct) inRegs.SI = VARPTR(moveStruct) CALL interruptx(&H67, inRegs, outRegs) CopyToEMS = outRegs.AX \ 256 END FUNCTION ' BIOS call to deallocate EMS pages. FUNCTION DeallocatePages (handle) inRegs.AX = &H4500 inRegs.DX = handle CALL interrupt(&H67, inRegs, outRegs) DeallocatePages = outRegs.AX \ 256 END FUNCTION ' BIOS call to check the status of EMS. FUNCTION GetEMSStatus inRegs.AX = &H4000 CALL interrupt(&H67, inRegs, outRegs) GetEMSStatus = outRegs.AX \ 256 END FUNCTION ' BIOS call to get the total and available number of EMS pages. FUNCTION GetPageCount (free, total) inRegs.AX = &H4200 CALL interrupt(&H67, inRegs, outRegs) free = outRegs.BX total = outRegs.DX GetPageCount = outRegs.AX \ 256 END FUNCTION ' BIOS call to get the segment address of the mappable conventional memory. FUNCTION GetPageFrameSeg inRegs.AX = &H4100 CALL interrupt(&H67, inRegs, outRegs) pfSeg = outRegs.BX GetPageFrameSeg = outRegs.AX \ 256 END FUNCTION ' BIOS call to map EMS to conventional memory. FUNCTION MapPages (physicalPage, logicalPage, handle) inRegs.AX = &H4400 OR (physicalPage MOD 256) inRegs.BX = logicalPage inRegs.DX = handle CALL interrupt(&H67, inRegs, outRegs) MapPages = outRegs.AX \ 256 END FUNCTION ' Pull a byte out of conventional memory that has been mapped to EMS. FUNCTION ReadByte (physicalPage, offset) realOffset& = offset + physicalPage * &H4000& DEF SEG = pfSeg ReadByte = PEEK(realOffset&) END FUNCTION ' Function to run all necessary set up stuff for EMS. SUB SetUpEMS IF GetEMSStatus THEN PRINT "EMS not present or not working properly." SYSTEM ELSE PRINT "EMS detected and working properly." END IF IF GetPageCount(free, total) THEN PRINT "Error returning page count." SYSTEM ELSE PRINT "Free pages:"; free PRINT "Total pages:"; total IF total = 0 THEN PRINT "No expanded memory pages have been detected. If you are running Windows please" PRINT "drop to DOS." SYSTEM END IF END IF IF GetPageFrameSeg THEN PRINT "Error returning page frame segment address." SYSTEM ELSE PRINT "Page frame segment address: &H"; HEX$(pfSeg) END IF END SUB ' Put a byte into conventional memory that has been mapped to EMS. SUB WriteByte (physicalPage, offset, byte) realOffset& = offset + physicalPage * &H4000& DEF SEG = pfSeg POKE realOffset&, byte END SUB