'===========================================================================
' Subject: EMS ROUTINES                       Date: 12-14-97 (17:51)       
'  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 16376 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 16376 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& \ 16376; "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 16376 < 16376 - 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& \ 16376
CurrentPage% = VOffset& \ 16376
Memcopy Segment& - 1 + 1, VOffset& MOD 16376, 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& \ 16376
CurrentPage% = VOffset& \ 16376
Memcopy FromSegment%, FromOffset%, Segment& - 1 + 1, VOffset& MOD 16376, 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& \ 16376
DEF SEG = Segment&
GetByte = PEEK(VOffset& MOD 16376)
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& \ 16376
FOR i& = 0 TO Bytes& - 1
	DEF SEG = Segment&
	St$ = St$ + CHR$(PEEK((VOffset& MOD 16376) + i&))
	DEF SEG
NEXT
GetString$ = St$

END FUNCTION

SUB InitEMS (MemoryNeeded&)
'ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
' Initializes MemoryNeeded& bytes of EMS memory.
'ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

Pages = (MemoryNeeded& \ 16376) + 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) * 16376&) - 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& \ 16376
DEF SEG = Segment&
POKE VOffset& MOD 16376, 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
