'=========================================================================== ' Subject: UPDATED (12/97) EMS ROUTINES Date: 12-24-97 (15:25) ' Author: Sami Kyostila Code: QB, PDS ' Origin: hiteck@freenet.hut.fi Packet: MEMORY.ABC '=========================================================================== 'ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ' EMS Routines - Sami Ky”stil„ - 1997 'ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ' You may use this code freely, as long as you give me some credit for it. 'ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ' ' Here's a bunch of EMS routines for QuickBasic 4.5 (sorry QB v1.1 users) ' ' These routines make an excellent base for creating programs, that need ' a bit more memory. You can use up to 2 gigabytes of EMS, and you don't ' need to hassle around those awkward pages, the routines automatically ' switch pages when needed, just give them a Virtual byte offset, that ' is a plain byte value of what you want. Say you wanted to read the ' byte stored in EMS at the position 62426: ' ' PRINT GetByte(62426) ' ' That's how simple it is. Here's a listing of the main routines: ' ' InitEMS - Use this sub to initialize the EMS memory your program ' is going to use. ' PutByte - Writes a byte into EMS ' GetByte - Reads a byte from EMS ' PutString - Writes a string into EMS ' GetString - Reads a string from EMS ' NOTE: String reads/writes cannot cross EMS page boundary ' (Every 16384 bytes) ' CopyDataToEMS - Copies just about any data into EMS, uses ASM ' CopyDataFromEMS - Copies just about any data from EMS, uses ASM ' NOTE: Data reads/writes cannot cross EMS page boundary ' (Every 16384 bytes) ' CloseEMS - Closes up EMS memory. Remember to call this at the ' end of every program! ' ' NOTE: All of the other routines are also needed to use these routines. ' These routines just interface for the more complicated ones. ' ' Some of the functions were converted from C by Chris Butler. ' Author of FillChar/MemCopy routines unknown. 'ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ DEFINT A-Z DECLARE SUB CopyDataFromEMS (VOffset&, ToSegmen%, ToOffset%, Bytes%) DECLARE SUB FillChar (Segment%, offset%, Value%, Bytes%) DECLARE SUB CopyDataToEMS (FromOffset%, FromSegment%, VOffset&, Bytes%) DECLARE SUB Memcopy (fromseg%, FromOffset%, toseg%, ToOffset%, Bytes%) DECLARE FUNCTION GetString$ (VOffset&, Bytes&) DECLARE SUB PutString (VOffset&, St$) DECLARE SUB Gauge (XPos%, YPos%, XLen%, Value&, Max&, Char$) DECLARE FUNCTION GetByte% (VOffset&) DECLARE SUB PutByte (VOffset&, Value%) DECLARE SUB ChangePage (PageNeeded%) DECLARE SUB InitEMS (MemoryNeeded&) DECLARE SUB CloseEMS () DECLARE SUB MapEMS (EMMhandle%, PhysicalPage%, LogicalPage%) DECLARE SUB ReadPal () DECLARE FUNCTION AllocateEMSPages! (PagesNeeded%, EMMhandle%) DECLARE FUNCTION DeallocateEMSPages! (Handle%) DECLARE FUNCTION EMMInstalled! () DECLARE FUNCTION EnoughEMSAvail! (PagesNeeded%) DECLARE FUNCTION GetPageFrameAddress! (Segment&) DECLARE FUNCTION MapEMSPages! (EMMhandle%, PhysicalPage%, LogicalPage%) TYPE RegType ax AS INTEGER bx AS INTEGER cx AS INTEGER dx AS INTEGER bp AS INTEGER si AS INTEGER di AS INTEGER flags AS INTEGER END TYPE TYPE RegTypeX 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 ' The 7 lines below are needed for operation. You MUST copy them to every ' program that you are going to use these routines in. If you don't have ' them, the program will crash! CONST EMMInt = &H67, GetPageFrame = &H4100, GetUnallocPageCount = &H4200 CONST AllocatePages = &H4300, MapPages = &H4400, DeallocatePages = &H4500 CONST False = 0, True = NOT False DIM SHARED Handle% DIM SHARED Segment& DIM SHARED CurrentPage% DIM SHARED EmsBytes& SCREEN 0: CLS WIDTH 80, 25 InitEMS 64000 'Allocate 64k of EMS PRINT " þ EMS Initialized:"; EmsBytes&; "bytes of memory available:"; EmsBytes& \ 16384; "pages." PRINT " þ Testing EMS..." PRINT COLOR 4 PRINT " This program demostrates the use of the EMS functions. These functions" PRINT " can move bytes, string, and areas of data to EMS and back." PRINT COLOR 7 PRINT " 1 Byte: " COLOR 14 FOR i& = 0 TO EmsBytes& 'Scan through all allocated EMS PutByte i&, INT(i& MOD 256) 'Write a byte at virtual byte offset i& IF NOT GetByte(i&) = INT(i& MOD 256) THEN 'If returned byte different PRINT "Error!" 'from byte written, then print SOUND 1000, 1 'error message EXIT FOR END IF Gauge 10, 7, 40, i&, EmsBytes&, "[úþ]" 'Print gauge NEXT COLOR 7 PRINT PRINT PRINT " String: " a$ = "This is a test string" LOCATE 9, 55 PRINT CHR$(34) + a$ + CHR$(34) COLOR 14 FOR i& = 0 TO EmsBytes& - LEN(a$) STEP LEN(a$) 'Scan through EMS IF i& MOD 16384 < 16384 - LEN(a$) THEN 'Test if string would cross 'EMS page boundary, if yes, 'then skip write/read PutString i&, a$ 'Write string a$ to EMS at 'Virtual byte offset i& IF NOT GetString$(i&, LEN(a$)) = a$ THEN 'If returned string differs PRINT 'from string written, then PRINT LEN(GetString$(i&, LEN(a$))), i& 'give error message PRINT GetString$(i&, LEN(a$)) PRINT "Error!" SOUND 1000, 1 EXIT FOR END IF END IF Gauge 10, 9, 40, i&, EmsBytes&, "[úþ]" 'Print gauge NEXT COLOR 7 PRINT PRINT PRINT " Data: " PRINT " - Free string memory: ", : COLOR 11: PRINT FRE(-1): COLOR 7 PRINT " - Creating 16000 byte string array..." REDIM Buffer(0 TO 159, 0 TO 99) AS STRING * 1 PRINT " - Free string memory: ", : COLOR 11: PRINT FRE(-1): COLOR 7 PRINT " - Filling string array with character 'A'..." FillChar VARSEG(Buffer(0, 0)), VARPTR(Buffer(0, 0)), 65, &H3E80 PRINT " - Copying buffer to EMS..."; CopyDataToEMS VARSEG(Buffer(0, 0)), VARPTR(Buffer(0, 0)), 0, &H3E80 PRINT &H3E80; "bytes copied" PRINT " - Erasing array..." REDIM Buffer(0 TO 1, 0 TO 1) AS STRING * 1 PRINT " - Free string memory: ", : COLOR 11: PRINT FRE(-1): COLOR 7 PRINT " - Copying first 100 bytes into another array..." DIM Buffer2(0 TO 100) AS STRING * 1 CopyDataFromEMS 0, VARSEG(Buffer2(0)), VARPTR(Buffer2(0)), 100 PRINT " - Contents of Array 2: "; COLOR 11 FOR i& = 0 TO 100 PRINT Buffer2(i&); NEXT CloseEMS 'Close EMS memory - ALWAYS REMEMBER TO DO THIS AT THE END OF ' YOUR PROGRAMS! DEFSNG A-Z FUNCTION AllocateEMSPages (PagesNeeded%, EMMhandle%) DIM InRegs AS RegType, OutRegs AS RegType InRegs.ax = AllocatePages InRegs.bx = PagesNeeded% CALL INTERRUPT(EMMInt, InRegs, OutRegs) IF (OutRegs.ax \ 256) = 0 THEN EMMhandle% = OutRegs.dx AllocateEMSPages = ((OutRegs.ax \ 256) = 0) END FUNCTION DEFINT A-Z SUB ChangePage (PageNeeded) 'ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ' Changes the active EMS page to PageNeeded, unless it is already active. 'ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ IF PageNeeded <> CurrentPage% THEN MapEMS Handle%, 0, PageNeeded CurrentPage% = PageNeeded END IF END SUB SUB CloseEMS 'ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ' Releases allocated EMS memory, ALWAYS call at the end of the program. 'ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ IF NOT DeallocateEMSPages(Handle%) THEN PRINT "Unable to deallocate EMS Pages!" END IF END SUB SUB CopyDataFromEMS (VOffset&, ToSegment%, ToOffset%, Bytes%) 'ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ' Copies an area of data from EMS ' ' VOffset& - Virtual EMS offset ' ToSegment% - Dest. Segment ' ToOffset% - Dest. Offset ' Bytes% - n. of bytes copy 'ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ MapEMS Handle%, 0, VOffset& \ 16384 CurrentPage% = VOffset& \ 16384 Memcopy Segment& - 1 + 1, VOffset& MOD 16384, ToSegment%, ToOffset%, Bytes% END SUB SUB CopyDataToEMS (FromSegment%, FromOffset%, VOffset&, Bytes%) 'ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ' Copies an area of data to EMS ' ' FromSegment% - From Segment ' FromOffset% - From Offset ' VOffset& - Virtual EMS offset ' Bytes% - n. of bytes copy 'ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ MapEMS Handle%, 0, VOffset& \ 16384 CurrentPage% = VOffset& \ 16384 Memcopy FromSegment%, FromOffset%, Segment& - 1 + 1, VOffset& MOD 16384, Bytes% END SUB DEFSNG A-Z FUNCTION DeallocateEMSPages (Handle%) DIM InRegs AS RegType, OutRegs AS RegType InRegs.ax = DeallocatePages InRegs.dx = Handle% CALL INTERRUPT(EMMInt, InRegs, OutRegs) DeallocateEMSPages = ((OutRegs.ax \ 256) = 0) END FUNCTION FUNCTION EMMInstalled DIM InRegsX AS RegTypeX, OutRegsX AS RegTypeX InRegsX.ax = &H3567 ' Get vector for INT 67h CALL InterruptX(&H21, InRegsX, OutRegsX) DEF SEG = OutRegsX.es Test$ = CHR$(PEEK(&HA)) + CHR$(PEEK(&HB)) + CHR$(PEEK(&HC)) EMMInstalled = (Test$ = "EMM") END FUNCTION FUNCTION EnoughEMSAvail (PagesNeeded%) DIM InRegs AS RegType, OutRegs AS RegType InRegs.ax = GetUnallocPageCount CALL INTERRUPT(EMMInt, InRegs, OutRegs) EnoughEMSAvail = (((OutRegs.ax \ 256) = 0) AND (OutRegs.bx >= PagesNeeded)) END FUNCTION SUB FillChar (Segment%, offset%, Value%, Bytes%) 'ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ' Fills an area of memory with Value% 'ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ asm$ = "" asm$ = asm$ + CHR$(85) asm$ = asm$ + CHR$(137) + CHR$(229) asm$ = asm$ + CHR$(139) + CHR$(78) + CHR$(6) asm$ = asm$ + CHR$(139) + CHR$(86) + CHR$(8) asm$ = asm$ + CHR$(139) + CHR$(70) + CHR$(12) asm$ = asm$ + CHR$(30) asm$ = asm$ + CHR$(142) + CHR$(216) asm$ = asm$ + CHR$(139) + CHR$(94) + CHR$(10) asm$ = asm$ + CHR$(136) + CHR$(23) asm$ = asm$ + CHR$(67) asm$ = asm$ + CHR$(226) + CHR$(251) asm$ = asm$ + CHR$(31) asm$ = asm$ + CHR$(93) asm$ = asm$ + CHR$(203) DEF SEG = VARSEG(asm$) CALL Absolute(BYVAL Segment%, BYVAL offset%, BYVAL Value%, BYVAL Bytes%, SADD(asm$)) DEF SEG END SUB DEFINT A-Z SUB Gauge (XPos, YPos, XLen, Value&, Max&, Char$) LOCATE YPos, XPos PRINT LEFT$(Char$, 1); PRINT STRING$(Value& / Max& * (XLen - 2), MID$(Char$, 3, 1)); PRINT STRING$((XLen - 2) - (Value& / Max& * (XLen - 2)), MID$(Char$, 2, 1)); PRINT MID$(Char$, 4, 1); END SUB FUNCTION GetByte (VOffset&) 'ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ' Returns 1 byte from EMS at virtual byte position VOffset& 'ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ChangePage VOffset& \ 16384 DEF SEG = Segment& GetByte = PEEK(VOffset& MOD 16384) DEF SEG END FUNCTION DEFSNG A-Z FUNCTION GetPageFrameAddress (Segment&) DIM InRegs AS RegType, OutRegs AS RegType InRegs.ax = GetPageFrame CALL INTERRUPT(EMMInt, InRegs, OutRegs) IF (OutRegs.ax \ 256) = 0 THEN Segment& = OutRegs.bx END IF GetPageFrameAddress = ((OutRegs.ax \ 256) = 0) END FUNCTION DEFINT A-Z FUNCTION GetString$ (VOffset&, Bytes&) 'ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ' Returns a string stored at virtual byte offset VOffset&, Bytes& bytes long. 'ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ St$ = "" ChangePage VOffset& \ 16384 FOR i& = 0 TO Bytes& - 1 DEF SEG = Segment& St$ = St$ + CHR$(PEEK((VOffset& MOD 16384) + i&)) DEF SEG NEXT GetString$ = St$ END FUNCTION SUB InitEMS (MemoryNeeded&) 'ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ' Initializes MemoryNeeded& bytes of EMS memory. 'ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Pages = (MemoryNeeded& \ 16384) + 1 IF NOT EMMInstalled THEN PRINT "EMM not installed!" SYSTEM END IF IF NOT EnoughEMSAvail(Pages) THEN PRINT "Not enough EMM available!" SYSTEM END IF IF NOT AllocateEMSPages(Pages, Handle%) THEN PRINT "Unable to allocate EMS Pages!" SYSTEM END IF IF NOT MapEMSPages(Handle%, 0, 0) THEN PRINT "Unable to map EMS Pages!" IF NOT DeallocateEMSPages(Handle%) THEN PRINT "Unable to deallocate EMS Pages!" END IF SYSTEM END IF IF NOT GetPageFrameAddress(Segment&) THEN PRINT "Unable to get the Page Frame address!" IF NOT DeallocateEMSPages(Handle%) THEN PRINT "Unable to deallocate EMS Pages!" END IF SYSTEM END IF CurrentPage% = 0 MapEMS Handle%, 0, 0 EmsBytes& = (INT(Pages) * 16384&) - 1 END SUB DEFSNG A-Z SUB MapEMS (EMMhandle%, PhysicalPage%, LogicalPage%) IF NOT MapEMSPages(EMMhandle%, PhysicalPage%, LogicalPage%) THEN PRINT "Unable to map EMS Pages!" IF NOT DeallocateEMSPages(EMMhandle%) THEN PRINT "Unable to deallocate EMS Pages!" END IF SYSTEM END IF END SUB FUNCTION MapEMSPages (EMMhandle%, PhysicalPage%, LogicalPage%) DIM InRegs AS RegType, OutRegs AS RegType InRegs.ax = MapPages + (PhysicalPage% MOD 256) InRegs.bx = LogicalPage% InRegs.dx = EMMhandle% CALL INTERRUPT(EMMInt, InRegs, OutRegs) MapEMSPages = ((OutRegs.ax \ 256) = 0) END FUNCTION SUB Memcopy (fromseg%, FromOffset%, toseg%, ToOffset%, Bytes%) asm$ = "" asm$ = asm$ + CHR$(85) asm$ = asm$ + CHR$(137) + CHR$(229) asm$ = asm$ + CHR$(30) asm$ = asm$ + CHR$(139) + CHR$(70) + CHR$(10) asm$ = asm$ + CHR$(142) + CHR$(192) asm$ = asm$ + CHR$(139) + CHR$(70) + CHR$(14) asm$ = asm$ + CHR$(142) + CHR$(216) asm$ = asm$ + CHR$(139) + CHR$(118) + CHR$(8) asm$ = asm$ + CHR$(139) + CHR$(126) + CHR$(12) asm$ = asm$ + CHR$(139) + CHR$(78) + CHR$(6) asm$ = asm$ + CHR$(243) asm$ = asm$ + CHR$(164) asm$ = asm$ + CHR$(31) asm$ = asm$ + CHR$(93) asm$ = asm$ + CHR$(203) WAIT &H3DA, 8 DEF SEG = VARSEG(asm$) CALL Absolute(BYVAL fromseg%, BYVAL FromOffset%, BYVAL toseg%, BYVAL ToOffset%, BYVAL Bytes%, SADD(asm$)) DEF SEG END SUB DEFINT A-Z SUB PutByte (VOffset&, Value) 'ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ' Writes Value to EMS virtual byte offset VOffset& 'ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ChangePage VOffset& \ 16384 DEF SEG = Segment& POKE VOffset& MOD 16384, Value DEF SEG END SUB SUB PutString (VOffset&, St$) 'ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ' Writes string St$ to EMS virtual byte offset VOffset& 'ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ FOR i& = 0 TO LEN(St$) - 1 DEF SEG = VARSEG(St$) PutByte VOffset& + i&, PEEK(SADD(St$) + (i&)) DEF SEG NEXT END SUB