--- /dev/null
+' Using EMS in QuickBASIC: Part 1 of 3\r
+' Source Code\r
+'\r
+' By Jon Petrosky (Plasma)\r
+' www.phatcode.net\r
+'\r
+' [Remember to start QB with the /L switch to enable interrupts]\r
+\r
+DEFINT A-Z\r
+'$DYNAMIC\r
+'$INCLUDE: 'QB.BI'\r
+\r
+DIM SHARED Regs AS RegTypeX\r
+DIM SHARED EMS.Error 'Holds the error code of the last operation\r
+\r
+DECLARE FUNCTION EMS.ErrorMsg$ ()\r
+DECLARE FUNCTION EMS.Init ()\r
+DECLARE FUNCTION EMS.Version$ ()\r
+DECLARE FUNCTION EMS.PageFrame ()\r
+DECLARE FUNCTION EMS.FreeHandles ()\r
+DECLARE FUNCTION EMS.FreePages ()\r
+DECLARE FUNCTION EMS.TotalPages ()\r
+DECLARE FUNCTION EMS.AllocPages (NumPages)\r
+DECLARE SUB EMS.DeallocPages (Handle)\r
+DECLARE SUB EMS.MapPage (Physical, Logical, Handle)\r
+DECLARE SUB EMS.MapXPages (PhysicalStart, LogicalStart, NumPages, Handle)\r
+DECLARE SUB EMS.CopyMem (Length&, SrcHandle, SrcSegment, SrcOffset, DstHandle, DstSegment, DstOffset)\r
+DECLARE SUB EMS.ExchMem (Length&, SrcHandle, SrcSegment, SrcOffset, DstHandle, DstSegment, DstOffset)\r
+\r
+CLS\r
+\r
+IF NOT EMS.Init THEN\r
+ PRINT "No EMM detected."\r
+ END\r
+END IF\r
+\r
+COLOR 14, 1\r
+PRINT SPACE$(22); "Using EMS in QuickBasic: Part 1 of 3"; SPACE$(22)\r
+COLOR 15, 0\r
+PRINT STRING$(31, 196); " EMS Information "; STRING$(32, 196)\r
+COLOR 7\r
+PRINT "EMM Version: "; EMS.Version$\r
+\r
+IF EMS.Version$ < "4.0" THEN\r
+ PRINT\r
+ PRINT "EMM 4.0 or later must be present to use some of the EMS functions."\r
+ END\r
+END IF\r
+\r
+PRINT "Page frame at: "; HEX$(EMS.PageFrame); "h"\r
+PRINT "Free handles:"; EMS.FreeHandles\r
+\r
+IF EMS.FreeHandles = 0 THEN\r
+ PRINT\r
+ PRINT "You need at least one free handle to run this demo."\r
+ END\r
+END IF\r
+\r
+PRINT "Total EMS:"; EMS.TotalPages; "pages /"; EMS.TotalPages * 16&; "KB /"; EMS.TotalPages \ 64; "MB"\r
+PRINT "Free EMS:"; EMS.FreePages; "pages /"; EMS.FreePages * 16&; "KB /"; EMS.FreePages \ 64; "MB"\r
+\r
+IF EMS.FreePages < 64 THEN\r
+ PRINT\r
+ PRINT "You need at least 64 pages (1 MB) free EMS to run this demo."\r
+ END\r
+END IF\r
+\r
+PRINT\r
+COLOR 15, 0\r
+PRINT STRING$(31, 196); " Allocation Test "; STRING$(32, 196)\r
+COLOR 7\r
+PRINT "Allocating 64 pages (1 MB) of EMS...";\r
+\r
+Handle = EMS.AllocPages(64)\r
+IF EMS.Error THEN\r
+ PRINT "error!"\r
+ PRINT EMS.ErrorMsg$\r
+ END\r
+ELSE\r
+ PRINT "ok!"\r
+END IF\r
+\r
+PRINT "Pages allocated to handle"; Handle\r
+PRINT\r
+COLOR 15, 0\r
+PRINT STRING$(30, 196); " Page Map/Copy Test "; STRING$(30, 196)\r
+COLOR 7\r
+PRINT "Mapping logical page 0 to physical page 0...";\r
+\r
+EMS.MapPage 0, 0, Handle\r
+IF EMS.Error THEN\r
+ PRINT "error!"\r
+ PRINT EMS.ErrorMsg$\r
+ END\r
+ELSE\r
+ PRINT "ok!"\r
+END IF\r
+\r
+PRINT "Mapping logical pages 0-3 to physical pages 0-3...";\r
+\r
+EMS.MapXPages 0, 0, 4, Handle\r
+IF EMS.Error THEN\r
+ PRINT "error!"\r
+ PRINT EMS.ErrorMsg$\r
+ END\r
+ELSE\r
+ PRINT "ok!"\r
+END IF\r
+\r
+PRINT "Copying logical pages 0-31 to logical pages 32-63...";\r
+\r
+EMS.CopyMem 512288, Handle, 0, 0, Handle, 32, 0\r
+IF EMS.Error THEN\r
+ PRINT "error!"\r
+ PRINT EMS.ErrorMsg$\r
+ END\r
+ELSE\r
+ PRINT "ok!"\r
+END IF\r
+\r
+PRINT "Exchanging logical pages 0-31 with logical pages 32-63...";\r
+\r
+EMS.ExchMem 512288, Handle, 0, 0, Handle, 32, 0\r
+IF EMS.Error THEN\r
+ PRINT "error!"\r
+ PRINT EMS.ErrorMsg$\r
+ END\r
+ELSE\r
+ PRINT "ok!"\r
+END IF\r
+\r
+PRINT\r
+COLOR 15, 0\r
+PRINT STRING$(22, 196); " 10-Second Speed Test (Please Wait) "; STRING$(22, 196)\r
+COLOR 7\r
+\r
+Mapped& = 0\r
+StartTime! = TIMER\r
+DO UNTIL StartTime! + 5 <= TIMER\r
+ EMS.MapXPages 0, 0, 4, Handle\r
+ Mapped& = Mapped& + 4\r
+LOOP\r
+\r
+Copied& = 0\r
+StartTime! = TIMER\r
+DO UNTIL StartTime! + 5 <= TIMER\r
+ EMS.CopyMem 512288, Handle, 0, 0, Handle, 32, 0\r
+ Copied& = Copied& + 1\r
+LOOP\r
+\r
+PRINT "Pages Mapped/Sec:"; Mapped& \ 5\r
+PRINT "Bytes copied/Sec:"; (Copied& * 512288) \ 5\r
+PRINT\r
+COLOR 15, 0\r
+PRINT STRING$(30, 196); " Deallocation Test "; STRING$(31, 196)\r
+COLOR 7\r
+PRINT "Deallocating 64 pages...";\r
+\r
+EMS.DeallocPages (Handle)\r
+IF EMS.Error THEN\r
+ PRINT "error!"\r
+ PRINT EMS.ErrorMsg$\r
+ END\r
+ELSE\r
+ PRINT "ok!";\r
+END IF\r
+\r
+KeyPress$ = INPUT$(1)\r
+CLS\r
+END\r
+\r
+FUNCTION EMS.AllocPages (NumPages)\r
+\r
+ 'Allocates the number of pages in [NumPages] and\r
+ 'returns the EMS handle the memory is allocated to.\r
+\r
+ Regs.ax = &H4300 'Allocate [NumPages] pages of EMS\r
+ Regs.bx = NumPages\r
+ InterruptX &H67, Regs, Regs\r
+ EMS.Error = (Regs.ax AND &HFF00&) \ &H100 'Store the status code\r
+\r
+ EMS.AllocPages = Regs.dx 'Return the handle\r
+\r
+END FUNCTION\r
+\r
+SUB EMS.CopyMem (Length&, SrcHandle, SrcSegment, SrcOffset, DstHandle, DstSegment, DstOffset)\r
+\r
+ 'Copies memory from EMS or base memory to EMS or base memory, where:\r
+ '\r
+ 'Length& = Length of memory to copy in bytes\r
+ 'SrcHandle = EMS handle of source memory (use 0 if source is base memory)\r
+ 'SrcSegment = Segment of source memory (or page number if source is EMS)\r
+ 'SrcOffset = Offset of source memory\r
+ 'DstHandle = EMS handle of destination memory (use 0 if destination is base memory)\r
+ 'DstSegment = Segment of destination memory (or page number if destination is EMS)\r
+ 'DstOffset = Offset of destination memory\r
+\r
+ 'Determine the source and destination memory types by checking the handles\r
+ IF SrcHandle = 0 THEN SrcType$ = CHR$(0) ELSE SrcType$ = CHR$(1)\r
+ IF DstHandle = 0 THEN DstType$ = CHR$(0) ELSE DstType$ = CHR$(1)\r
+\r
+ 'Create a buffer containing the copy information\r
+ CopyInfo$ = MKL$(Length&) + SrcType$ + MKI$(SrcHandle) + MKI$(SrcOffset) + MKI$(SrcSegment) + DstType$ + MKI$(DstHandle) + MKI$(DstOffset) + MKI$(DstSegment)\r
+\r
+ Regs.ax = &H5700 'Copy the memory region\r
+ Regs.ds = VARSEG(CopyInfo$) 'described in the buffer\r
+ Regs.si = SADD(CopyInfo$)\r
+ InterruptX &H67, Regs, Regs\r
+ EMS.Error = (Regs.ax AND &HFF00&) \ &H100 'Store the status code\r
+\r
+END SUB\r
+\r
+SUB EMS.DeallocPages (Handle)\r
+\r
+ 'Deallocates the EMS pages allocated the EMS handle [Handle].\r
+ 'You MUST remember to call the sub before your program ends\r
+ 'if you allocate any memory!\r
+\r
+ Regs.ax = &H4500 'Release the pages allocated to [Handle]\r
+ Regs.dx = Handle\r
+ InterruptX &H67, Regs, Regs\r
+ EMS.Error = (Regs.ax AND &HFF00&) \ &H100 'Store the status code\r
+\r
+END SUB\r
+\r
+FUNCTION EMS.ErrorMsg$\r
+\r
+ 'Returns a text string describing the error code in EMS.Error.\r
+\r
+ SELECT CASE EMS.Error\r
+ CASE &H0: Msg$ = "successful"\r
+ CASE &H80: Msg$ = "internal error"\r
+ CASE &H81: Msg$ = "hardware malfunction"\r
+ CASE &H82: Msg$ = "busy -- retry later"\r
+ CASE &H83: Msg$ = "invalid handle"\r
+ CASE &H84: Msg$ = "undefined function requested by application"\r
+ CASE &H85: Msg$ = "no more handles available"\r
+ CASE &H86: Msg$ = "error in save or restore of mapping context"\r
+ CASE &H87: Msg$ = "insufficient memory pages in system"\r
+ CASE &H88: Msg$ = "insufficient memory pages available"\r
+ CASE &H89: Msg$ = "zero pages requested"\r
+ CASE &H8A: Msg$ = "invalid logical page number encountered"\r
+ CASE &H8B: Msg$ = "invalid physical page number encountered"\r
+ CASE &H8C: Msg$ = "page-mapping hardware state save area is full"\r
+ CASE &H8D: Msg$ = "save of mapping context failed"\r
+ CASE &H8E: Msg$ = "restore of mapping context failed"\r
+ CASE &H8F: Msg$ = "undefined subfunction"\r
+ CASE &H90: Msg$ = "undefined attribute type"\r
+ CASE &H91: Msg$ = "feature not supported"\r
+ CASE &H92: Msg$ = "successful, but a portion of the source region has been overwritten"\r
+ CASE &H93: Msg$ = "length of source or destination region exceeds length of region allocated to either source or destination handle"\r
+ CASE &H94: Msg$ = "conventional and expanded memory regions overlap"\r
+ CASE &H95: Msg$ = "offset within logical page exceeds size of logical page"\r
+ CASE &H96: Msg$ = "region length exceeds 1 MB"\r
+ CASE &H97: Msg$ = "source and destination EMS regions have same handle and overlap"\r
+ CASE &H98: Msg$ = "memory source or destination type undefined"\r
+ CASE &H9A: Msg$ = "specified alternate map register or DMA register set not supported"\r
+ CASE &H9B: Msg$ = "all alternate map register or DMA register sets currently allocated"\r
+ CASE &H9C: Msg$ = "alternate map register or DMA register sets not supported"\r
+ CASE &H9D: Msg$ = "undefined or unallocated alternate map register or DMA register set"\r
+ CASE &H9E: Msg$ = "dedicated DMA channels not supported"\r
+ CASE &H9F: Msg$ = "specified dedicated DMA channel not supported"\r
+ CASE &HA0: Msg$ = "no such handle name"\r
+ CASE &HA1: Msg$ = "a handle found had no name, or duplicate handle name"\r
+ CASE &HA2: Msg$ = "attempted to wrap around 1M conventional address space"\r
+ CASE &HA3: Msg$ = "source array corrupted"\r
+ CASE &HA4: Msg$ = "operating system denied access"\r
+ CASE ELSE: Msg$ = HEX$(EMS.Error) '"undefined error"\r
+ END SELECT\r
+\r
+ EMS.ErrorMsg$ = Msg$\r
+\r
+END FUNCTION\r
+\r
+SUB EMS.ExchMem (Length&, SrcHandle, SrcSegment, SrcOffset, DstHandle, DstSegment, DstOffset)\r
+\r
+ 'Exhanges memory from EMS or base memory to EMS or base memory, where:\r
+ '\r
+ 'Length& = Length of memory to exchange in bytes\r
+ 'SrcHandle = EMS handle of source memory (use 0 if source is base memory)\r
+ 'SrcSegment = Segment of source memory (or page number if source is EMS)\r
+ 'SrcOffset = Offset of source memory\r
+ 'DstHandle = EMS handle of destination memory (use 0 if destination is base memory)\r
+ 'DstSegment = Segment of destination memory (or page number if destination is EMS)\r
+ 'DstOffset = Offset of destination memory\r
+\r
+ 'Determine the source and destination memory types by checking the handles\r
+ IF SrcHandle = 0 THEN SrcType$ = CHR$(0) ELSE SrcType$ = CHR$(1)\r
+ IF DstHandle = 0 THEN DstType$ = CHR$(0) ELSE DstType$ = CHR$(1)\r
+\r
+ 'Create a buffer containing the copy information\r
+ ExchInfo$ = MKL$(Length&) + SrcType$ + MKI$(SrcHandle) + MKI$(SrcOffset) + MKI$(SrcSegment) + DstType$ + MKI$(DstHandle) + MKI$(DstOffset) + MKI$(DstSegment)\r
+\r
+ Regs.ax = &H5701 'Exchange the memory region\r
+ Regs.ds = VARSEG(ExchInfo$) 'described in the buffer\r
+ Regs.si = SADD(ExchInfo$)\r
+ InterruptX &H67, Regs, Regs\r
+ EMS.Error = (Regs.ax AND &HFF00&) \ &H100 'Store the status code\r
+\r
+END SUB\r
+\r
+FUNCTION EMS.FreeHandles\r
+\r
+ 'Returns the number of free (available) EMS handles.\r
+\r
+ Regs.ax = &H4B00 'Get the # of handles in use\r
+ InterruptX &H67, Regs, Regs\r
+ UsedHandles = Regs.bx\r
+\r
+ Regs.ax = &H5402 'Get the total # of handles\r
+ InterruptX &H67, Regs, Regs\r
+ EMS.Error = (Regs.ax AND &HFF00&) \ &H100 'Store the status code\r
+ TotalHandles = Regs.bx\r
+\r
+ EMS.FreeHandles = TotalHandles - UsedHandles 'Subtract to get the # of free handles\r
+\r
+END FUNCTION\r
+\r
+FUNCTION EMS.FreePages\r
+\r
+ 'Returns the number of free (available) EMS pages\r
+ '(Multiply by 16 to get the amount free EMS in KB.)\r
+\r
+ Regs.ax = &H4200 'Get the # of free pages\r
+ InterruptX &H67, Regs, Regs\r
+ EMS.Error = (Regs.ax AND &HFF00&) \ &H100 'Store the status code\r
+ EMS.FreePages = Regs.bx\r
+\r
+END FUNCTION\r
+\r
+FUNCTION EMS.Init\r
+\r
+ 'Returns true (-1) if an EMM is installed\r
+ 'or false (0) if an EMM is not installed.\r
+\r
+ Regs.ax = &H3567 'Get the interrupt vector for int 67h\r
+ InterruptX &H21, Regs, Regs\r
+ DEF SEG = Regs.es 'Point to the interrupt segment\r
+ FOR x = 10 TO 17 'Store the 8 bytes at ES:0A in EMM$\r
+ EMM$ = EMM$ + CHR$(PEEK(x))\r
+ NEXT\r
+ IF EMM$ <> "EMMXXXX0" THEN\r
+ EMS.Init = 0 'EMM not installed\r
+ ELSE\r
+ EMS.Init = -1 'EMM installed\r
+ END IF\r
+\r
+END FUNCTION\r
+\r
+SUB EMS.MapPage (Physical, Logical, Handle)\r
+\r
+ 'Maps the logical EMS page [Logical] (allocated to the handle [Handle])\r
+ 'to the physical page [Physical] in the EMS page frame.\r
+\r
+ Regs.ax = &H4400 + Physical 'Map the logical page [Logical]\r
+ Regs.bx = Logical 'to the physical page [Physical]\r
+ Regs.dx = Handle\r
+ InterruptX &H67, Regs, Regs\r
+ EMS.Error = (Regs.ax AND &HFF00&) \ &H100 'Store the status code\r
+\r
+END SUB\r
+\r
+SUB EMS.MapXPages (PhysicalStart, LogicalStart, NumPages, Handle)\r
+\r
+ 'Maps up to 4 logical EMS pages to physical pages in the page frame, where:\r
+ '\r
+ 'PhysicalStart = Physical page first logical page is mapped to\r
+ 'LogicalStart = First logical page to map\r
+ 'NumPages = Number of pages to map (1 to 4)\r
+ 'Handle = EMS handle logical pages are allocated to\r
+\r
+ 'Create a buffer containing the page information\r
+ FOR x = 0 TO NumPages - 1\r
+ MapInfo$ = MapInfo$ + MKI$(LogicalStart + x) + MKI$(PhysicalStart + x)\r
+ NEXT\r
+\r
+ Regs.ax = &H5000 'Map the pages in the buffer\r
+ Regs.cx = NumPages 'to the pageframe\r
+ Regs.dx = Handle\r
+ Regs.ds = VARSEG(MapInfo$)\r
+ Regs.si = SADD(MapInfo$)\r
+ InterruptX &H67, Regs, Regs\r
+ EMS.Error = (Regs.ax AND &HFF00&) \ &H100 'Store the status code\r
+\r
+END SUB\r
+\r
+FUNCTION EMS.PageFrame\r
+\r
+ 'Returns the segment of the EMS page frame\r
+\r
+ Regs.ax = &H4100 'Get the segment of the page frame\r
+ InterruptX &H67, Regs, Regs\r
+ EMS.Error = (Regs.ax AND &HFF00&) \ &H100 'Save the status code\r
+ EMS.PageFrame = Regs.bx\r
+\r
+END FUNCTION\r
+\r
+FUNCTION EMS.TotalPages\r
+\r
+ 'Returns the total number of EMS pages\r
+ '(Multiply by 16 to get the total amount of EMS in KB.)\r
+\r
+ Regs.ax = &H4200 'Get the # of total pages\r
+ InterruptX &H67, Regs, Regs\r
+ EMS.Error = (Regs.ax AND &HFF00&) \ &H100 'Store the status code\r
+ EMS.TotalPages = Regs.dx\r
+\r
+END FUNCTION\r
+\r
+FUNCTION EMS.Version$\r
+\r
+ 'Returns a string containing the EMM version.\r
+ '(Must be "4.0" or greater to use our routines.)\r
+\r
+ Regs.ax = &H4600 'Get the EMM version\r
+ InterruptX &H67, Regs, Regs\r
+ EMS.Error = (Regs.ax AND &HFF00&) \ &H100 'Save the status code\r
+\r
+ Version = Regs.ax AND &HFF 'Split the version number into\r
+ Major = (Version AND &HF0) \ &H10 'its major and minor counterparts\r
+ Minor = Version AND &HF\r
+ EMS.Version$ = LTRIM$(STR$(Major)) + "." + LTRIM$(STR$(Minor))\r
+\r
+END FUNCTION\r
+\r