'=========================================================================== ' Subject: EXPANDED MEMORY ROUTINES Date: Unknown Date ' Author: Unknown Author(s) Code: QB, PDS ' Origin: QB TidBits Packet: MEMORY.ABC '=========================================================================== 'Program to store data in Expanded memory with QuickBasic. DECLARE SUB CallEmm (EmmFuncNbr%) DECLARE FUNCTION EmmDriverExists2% () DECLARE FUNCTION EmmDriverExists1% () DECLARE SUB EmmPrintStatus (Status%) DECLARE FUNCTION FmtPointer$ (P AS ANY) DECLARE FUNCTION Hi% (Operand%) DECLARE FUNCTION Lo% (Operand%) 'If you use the PDS product, change the next line to include 'the QBX.BI include file instead of the QB.BI file '$INCLUDE: 'QB.BI' DEFINT A-Z CONST EmsInt = &H67 'EMS interrupt number CONST IoCtlFunc = &H44 'IOCtl DOS Function number CONST PageLen = 16384 'Length of memory page CONST MsgLen = 16 'Message length CONST MsgsPerPage = PageLen \ MsgLen CONST NumMsgs = 5000 '*** Emm functions *** CONST GetStatus = &H40 CONST GetPageFrameAddr = &H41 CONST GetUnallocPages = &H42 CONST GetEmmVersion = &H46 CONST AllocatePages = &H43 CONST MapHandlePage = &H44 CONST DeallocatePages = &H45 CLS TYPE address Segment AS LONG Offset AS LONG END TYPE DIM P0 AS address 'Pointer to physical page 1 DIM P1 AS address 'Pointer to physical page 2 DIM P2 AS address 'Pointer to physical page 3 DIM P3 AS address 'Pointer to physical page 4 DIM MsgBuf AS address 'Pointer into mapped memory DIM Buff AS STRING * 16 'Buffer for message to store in EM DIM I AS INTEGER 'Dummy variable DIM SHARED EmmRegs AS RegType 'Registers for interrupt calls DIM Page AS LONG 'Page frame address DIM Index AS LONG 'Index into page frame DIM StrNum AS STRING * 6 'Holds record # for EMM msg 'Test for the existence of the EMM driver 'You can choose from 1 of 2 methods 'IF EmmDriverExists1% THEN 'Method 1 IF EmmDriverExists2% THEN 'Method 2 PRINT "EMM driver exists" ELSE PRINT "No EMM driver detected." END IF 'Print the current status of the EMM driver PRINT "Checking EMM status" CALL CallEmm(GetStatus) PRINT "EMM status ok" 'Print the version number of the EMM driver CALL CallEmm(GetEmmVersion) PRINT "EMS driver version = "; AL% = Lo%(EmmRegs.ax) MajorVersion = AL% \ 16 MinorVersion = AL% AND &HF PRINT USING "!."; RIGHT$(STR$(MajorVersion), 1); PRINT USING "!"; RIGHT$(STR$(MinorVersion), 1) IF AL% < &H32 THEN PRINT "Error - EMM version is earlier than 3.2" SYSTEM END IF '***** Print the page frame & physical window addresses ***** CALL CallEmm(GetPageFrameAddr) P0.Segment = EmmRegs.bx 'Window 0 -> P0 = BX:0000 P1.Segment = EmmRegs.bx 'Window 1 -> P1 = BX:4000 P2.Segment = EmmRegs.bx 'Window 2 -> P2 = BX:8000 P3.Segment = EmmRegs.bx 'Window 3 -> P3 = BX:C000 P0.Offset = &H0 P1.Offset = &H4000 P2.Offset = &H8000 P3.Offset = &HC000 PRINT "Page frame segment address = "; HEX$(EmmRegs.bx) PRINT "Physical page 0 address = "; FmtPointer$(P0) PRINT "Physical page 1 address = "; FmtPointer$(P1) PRINT "Physical page 2 address = "; FmtPointer$(P2) PRINT "Physical page 3 address = "; FmtPointer$(P3) '***** Print # of unallocated pages and total # of pages ***** CALL CallEmm(GetUnallocPages) PRINT "Total EMS pages = "; EmmRegs.dx PRINT "Unused EMS pages = "; EmmRegs.bx '***** Allocate some pages of expanded memory ***** EmmRegs.bx = (NumMsgs + MsgsPerPage) \ MsgsPerPage CALL CallEmm(AllocatePages) PRINT "Allocated "; EmmRegs.bx; " pages to handle #"; EmmRegs.dx EmmHandle = EmmRegs.dx '***** Load EMS RAM with data ***** MsgBuf = P0 PRINT "Storing messages into extended memory page frame" LastPageNbr = -1 FOR I = 0 TO NumMsgs - 1 LOCATE 14, 50: PRINT USING "#,###"; I StrNum = STR$(I) Buff = " EMS msg #" + StrNum Page = I \ MsgsPerPage Index = I MOD MsgsPerPage MsgBuf.Offset = Index * LEN(Buff) '***** Map indicated logical page into physical page 0 **** IF Page <> LastPageNbr THEN AH = MapHandlePage AL = 0 EmmRegs.ax = AH * 256 + AL 'Map EMS page & Physical page 0 EmmRegs.bx = Page EmmRegs.dx = EmmHandle 'EMM RAM handle CALL INTERRUPT(EmsInt, EmmRegs, EmmRegs) LastPageNbr = Page END IF AH = Hi%(EmmRegs.ax) IF AH = 0 THEN ' Set message into memory DEF SEG = MsgBuf.Segment FOR J = 0 TO MsgLen - 1 POKE MsgBuf.Offset + J, ASC(MID$(Buff, J + 1, 1)) NEXT J DEF SEG ELSE CALL EmmPrintStatus(AH) EXIT FOR END IF NEXT I PRINT 'Allow user to access any message in the buffer I = &HFF WHILE I <> -1 INPUT "Enter message # to retrieve, or -1 to quit: "; I IF (I >= 0) AND (I < NumMsgs) THEN MsgBuf = P3 Page = I \ MsgsPerPage Index = I MOD MsgsPerPage '***** Map indicated page into physical page 3 ***** AH = MapHandlePage 'Map EMM page AL = 3 ' using physical page 3 EmmRegs.ax = AH * 256 + AL EmmRegs.bx = Page 'Logical page number EmmRegs.dx = EmmHandle 'EMM RAM handle CALL INTERRUPT(EmsInt, EmmRegs, EmmRegs) AH = Hi%(EmmRegs.ax) IF AH = 0 THEN MsgBuf.Offset = MsgBuf.Offset + Index * LEN(Buff) 'Move the bytes from memory to a local variable DEF SEG = MsgBuf.Segment FOR J = 0 TO MsgLen - 1 MID$(Buff, J + 1, 1) = CHR$(PEEK(MsgBuf.Offset + J)) NEXT J DEF SEG PRINT "Retrieved message -> "; Buff; PRINT " from page #"; Page; " Index"; Index ELSE CALL EmmPrintStatus(AH) I = -1 END IF END IF WEND '***** Free the EMS RAM back to the EMM driver ***** EmmRegs.dx = EmmHandle CALL CallEmm(DeallocatePages) PRINT "Released all memory for handle "; EmmRegs.dx END 'Error handling routine oops: SELECT CASE ERR CASE 53 'File/device not found. PRINT "No EMM driver found" SYSTEM CASE ELSE PRINT "Unknown error #"; ERR SYSTEM END SELECT SUB CallEmm (EmmFuncNbr) EmmRegs.ax = EmmFuncNbr * 256 CALL INTERRUPT(EmsInt, EmmRegs, EmmRegs) AH = Hi%(EmmRegs.ax) IF AH <> 0 THEN CALL EmmPrintStatus(AH) SYSTEM END IF END SUB FUNCTION EmmDriverExists1% DIM EmsDriver AS address DIM EmsIdString AS STRING * 8 EmmDriverExists1% = 0 'False DEF SEG = 0 VectorAddr = &H67 * 4 EmsDriver.Segment = PEEK(VectorAddr + 3) * 256& + _ PEEK(VectorAddr + 2) IF EmsDriver.Segment <> 0 THEN DEF SEG = EmsDriver.Segment EmsDriver.Offset = 10 FOR I = 0 TO 7 MID$(EmsIdString, I + 1, 1) = CHR$(PEEK(EmsDriver.Offset + I)) NEXT I IF EmsIdString = "EMMXXXX0" THEN EmmDriverExists1% = -1 END IF END IF DEF SEG END FUNCTION FUNCTION EmmDriverExists2% DIM EmmHandle AS INTEGER 'Handle for EMM allocated pages ON ERROR GOTO oops EmmDriverExists2% = -1 'Set default return value to TRUE OPEN "I", 1, "EMMXXXX0" EmmRegs.ax = IoCtlFunc * 256& 'Call IOCtl Function EmmRegs.bx = FILEATTR(1, 2) 'Set DOS file handle# CALL INTERRUPT(&H21, EmmRegs, EmmRegs) 'Call DOS CLOSE 1 IF (EmmRegs.flags AND 1) = 0 THEN 'Call successfull IF (EmmRegs.dx AND &H80) = &H80 THEN 'Handle is for a dev PRINT "Handle refers to a device" ELSE PRINT "Handle refers to a file" PRINT "Unable to contact EMM driver if present" SYSTEM END IF ELSE 'Call unsuccessfull SELECT CASE EmmRegs.ax CASE 1: PRINT "Invalid IOCtl subfunction" CASE 5: PRINT "Access to IOCTL denied" CASE 6: PRINT "Invalid handle" CASE ELSE PRINT "Unknown error # "; EmmRegs.ax END SELECT PRINT "Unable to contact EMM driver" SYSTEM END IF EXIT FUNCTION END FUNCTION SUB EmmPrintStatus (Status%) SELECT CASE Status% CASE &H0: S$ = "Status ok" CASE &H80: S$ = "Driver malfunction" CASE &H81: S$ = "Hardware malfunction" CASE &H83: S$ = "Bad Handle" CASE &H84: S$ = "Undefined function" CASE &H85: S$ = "No free handles" CASE &H86: S$ = "Page map context error" CASE &H87: S$ = "Insufficient memory pages" CASE &H88: S$ = "Not enough free pages" CASE &H89: S$ = "Can't allocate zero pages" CASE &H8A: S$ = "Logical page out of range" CASE &H8B: S$ = "Physical page out of range" CASE &H8C: S$ = "Page map hardware RAM full" CASE &H8D: S$ = "Page map already has a handle" CASE &H8E: S$ = "Page map not mapped to handle" CASE &H8F: S$ = "Undefined subfunction number" CASE ELSE S$ = "Unknown status number $" + HEX$(Status%) END SELECT PRINT "EMM: " + S$ END SUB FUNCTION FmtPointer$ (P AS address) F$ = "$" + RIGHT$(HEX$(P.Segment), 4) F$ = F$ + ":$" + RIGHT$(HEX$(P.Offset), 4) FmtPointer$ = F$ END FUNCTION FUNCTION Hi% (Operand%) Hi% = Operand% \ 256 END FUNCTION FUNCTION Lo% (Operand%) Lo% = Operand% MOD 256 END FUNCTION