Access 512KB RAM from within BASIC on AgonLight (TM)
Memory: BASIC (64KB) + CUSTOM (384KB) + MOS (64KB)
This collection of assembler functions provide access to the extended memory (including and beyond the first 64KB) attached to the EZ80 CPU. The entire external RAM resides at addresses &40000 to &BFFFF, with BASIC normally using the lower 64KB (&40000 to &4FFFF), and MOS using the upper 64KB (&B0000 to &BFFFF). Now, you have another 384KB of RAM accessible from BASIC, ranging from &50000 to &AFFFF. You can access any part of the whole 512KB, if you are very careful not to clobber memory that will cause BASIC or MOS to crash.
For example, the empCMBI% procedure, detailed further below, is designed to copy memory blocks, and can be
used to copy between RAM in the BASIC area (lower 64KB), and RAM in the extended area (above 64KB).
Versions:
V0.4 - 30-Apr-2023 - Couple of doc edits; no code changes.
V0.3 - 27-Apr-2023 - All tests performed to the extent designated by the "emtests.bas" program.
V0.2 - More tests added, but still testing "block" routines.
V0.1 - Initial release of code for beta testing of a number of routines.
NOTE: The provided routines do not modify or enhance the BASIC language, nor provide an easy way to allow you to write BASIC
programs that are larger than would normally fit within the 40+KB available for programs. They provide a way to store data variables and/or blocks of data bytes in
the upper RAM. This extends the variable/array capacity greatly. When used with the CHAIN command in BASIC, you could
write large programs with lots of data.
The assembler functions can read or write the entire memory range, implying that you should be very careful not to clobber
any memory used by BASIC, unless you know what effect it will have. It is up to you how to arrange or manage the upper memory.
No allocation, deallocation, or garbage collection is provided. You simply call routines that write or read using specific addresses determined by your application.
In the following descriptions, the prefix "emp" means "extended memory procedure". The prefix "emf" means "extended memory function". The prefix "emd" means "extended memory data", and refers to a data parameter
passed into or out of one of the extended memory procedures.
The name "var" is a placeholder for a variable name of your choosing.
Regarding parameters, with the exception of string values, numeric expressions are typically expected, and may be constants, variables, computation results, function results, etc.
Many of the routines work with arrays. When working with 8-, 16-, 24-, or 32-bit array item values, the code automatically assumes the size of an array item to be 1, 2, 3, or 4 bytes, respectively. When working with float values, each item is 5 bytes in size. When working with an array of string values, the maximum item size must be specified manually, because strings have various lengths. The maximum item size in a string array is 256 bytes, meaning that the maximum string length inside an item is 255 bytes, because of the trailing CR character stored with the string.
The maximum array index size for any array is 65535 (&FFFF). This does not prevent you from having larger arrays, because you can arrange two or more arrays in memory such that they are physically together. It only means that you may need to reference the second (or third) array separately from the first array, and be sure to use base zero indexes in each array (i.e., range 0 to 65535).
The code does not presently support storing record-oriented data
fields in an obvious manner; however, you can mimic that behavior
in a couple of ways: (1) store data fields in subsequent memory
locations such that they form a "record" (a set of consecutive
data values of various types), and store a series of records in
consecutive memory locations, or (2) use multiple arrays, with
one array per data field, where each array holds only items of a
particular data type. Typically, such arrays would have equal
lengths (the same number of array items).
Method (1) requires you to manage the
memory yourself, but method (2) helps with array indexing, and
only requires that the arrays do not overlap in memory.
This BASIC procedure will initialize the variables that are described in the following sections, with the addresses of the various assembler routines that may be called to read and to write extended memory. Call this procedure before calling the others.
Usage: PROC_empInit
This parameter is a 24-bit source address that is required by some routines, as specified below. To access the first 64KB of memory, the address must be in the range &40000 to &4FFFF, not &00000 to &0FFFF. Any address in the range &40000 to &BFFFF is allowed. Be careful when specifying addresses within the BASIC or MOS areas!
Usage: !emdSA% = sourceaddress
This parameter is a 24-bit destination address that is required by some routines, as specified below. To access the first 64KB of memory, the address must be in the range &40000 to &4FFFF, not &00000 to &0FFFF. Any address in the range &40000 to &BFFFF is allowed. Be careful when specifying addresses within the BASIC or MOS areas!
Usage: !emdDA% = destinationaddress
This parameter is a 16-bit array index that is required by some routines, as specified below. The array index ranges from 0 to 65535 (&FFFF). See comments above about splitting large arrays.
Usage: !emdAI% = arrayindex
This parameter is a 16-bit array item size that is required by some routines, as specified below. The item size ranges from 1 to 256 (&100). See comments above about splitting large arrays.
Usage: !emdIS% = itemsize
This parameter is a 24-bit repeat count that is required by some routines, as specified below.
Usage: !emdRC% = repeatcount
This parameter is an 8-bit value that is required by some routines, as specified below.
Usage: ?emdV8% = value
Or: !emdV8% = value
This parameter is a 16-bit value that is required by some routines, as specified below.
Usage: !emdV16% = value
This parameter is a 24-bit value that is required by some routines, as specified below.
Usage: !emdV24% = value
This parameter is a 32-bit value that is required by some routines, as specified below.
Usage: !emdV32% = value
This parameter is a string value that is required by some routines, as specified below. When stored in memory, the string will be terminated by a carriage-return (CR, 0DH) character; therefore, the original string should not contain a carriage-return.
Usage: $emdVS% = stringvalue
This procedure clears (to zero) the middle 384KB of memory.
Usage: CALL empI%
This function reads an 8-bit value from memory.
Usage: !emdSA% = sourceaddress: var%=USR(emfG8%)
This function reads a 16-bit value from memory.
Usage: !emdSA% = sourceaddress: var%=USR(emfG16%)
This function reads a 24-bit value from memory.
Usage: !emdSA% = sourceaddress: var%=USR(emfG24%)
This function reads a 32-bit value from memory.
Usage: !emdSA% = sourceaddress: var%=USR(emfG32%)
This procedure reads a string value from memory.
Usage: !emdSA% = sourceaddress: CALL empGS%: var$=$emdVS%
This procedure reads a float value from memory, and copies the value into the specified variable.
Usage: !emdSA% = sourceaddress: CALL empGF%,floatvariable
This function reads an 8-bit array item from memory.
Usage: !emdSA% = arrayaddress: !emdAI% = array index: var%=USR(emfG8AI%)
This function reads a 16-bit array item from memory.
Usage: !emdSA% = arrayaddress: !emdAI% = array index: var%=USR(emfG16AI%)
This function reads a 24-bit array item from memory.
Usage: !emdSA% = arrayaddress: !emdAI% = array index: var%=USR(emfG24AI%)
This function reads a 32-bit array item from memory.
Usage: !emdSA% = arrayaddress: !emdAI% = array index: var%=USR(emfG32AI%)
This function reads a string array item from memory. The given start address points to the start of the array (at item #0). The item size tells how big each array item is, which determines the maximum size of the string that can be stored there. For example, using an item size of 21, the maximum string length is 20, because of the trailing CR at the end of the string, while stored.
Usage: !emdSA% = arrayaddress: !emdIS% = itemsize: !emdAI% = array index: CALL empGSAI%: var$ = $emdVS%
This procedure reads a float array item from memory, and copies the value into the specified variable.
Usage: !emdSA% = arrayaddress: !emdAI% = array index: CALL empGFAI%,floatvariable
This procedure writes an 8-bit value to memory.
Usage: !emdDA% = destinationaddress: !emdV8% = value: CALL empP8%
Or: !emdDA% = destinationaddress: ?emdV8% = value: CALL empP8%
This procedure writes a 16-bit value to memory.
Usage: !emdDA% = destinationaddress: !emdV16% = value: CALL empP16%
This procedure writes a 24-bit value to memory.
Usage: !emdDA% = destinationaddress: !emdV24% = value: CALL empP24%
This procedure writes a 32-bit value to memory.
Usage: !emdDA% = destinationaddress: !emdV32% = value: CALL empP32%
This procedure writes a string value to memory. The original string must not contain a carriage-return (CR, 0DH) character, because BASIC will append one as a terminator when the $emdVS% construct is used in an assignment.
Usage: !emdDA% = destinationaddress: $emdVS% = stringvalue: CALL empPS%
This procedure copies the value from the specified float variable into memory.
Usage: !emdDA% = destinationaddress: CALL empPF%,floatvariable
This procedure writes an 8-bit array item to memory.
Usage: !emdDA% = arrayaddress: !emdAI% = array index: !emdV8% = value: CALL empP8AI%
Or: !emdDA% = arrayaddress: !emdAI% = array index: ?emdV8% = value: CALL empP8AI%
This procedure writes a 16-bit array item to memory.
Usage: !emdDA% = arrayaddress: !emdAI% = array index: !emdV16% = value: CALL empP16AI%
This procedure writes a 24-bit array item to memory.
Usage: !emdDA% = arrayaddress: !emdAI% = array index: !emdV24% = value: CALL empP24AI%
This procedure writes a 32-bit array item to memory.
Usage: !emdDA% = arrayaddress: !emdAI% = array index: !emdV32% = value: CALL empP32AI%
This procedure writes a string array item to memory. The given start address points to the start of the array (at item #0). The item size tells how big each array item is, which determines the maximum size of the string that can be stored there. For example, using an item size of 21, the maximum string length is 20, because of the trailing CR at the end of the string, while stored.
Usage: !emdDA% = arrayaddress: !emdIS% = itemsize: !emdAI% = array index: $emdVS% = stringvalue: CALL empPSAI%
This procedure copies the value from the specified float variable into a float array item in memory.
Usage: !emdDA% = arrayaddress: !emdAI% = array index: CALL empPFAI%,floatvariable
This procedure copies a memory block from one location to another location, while incrementing addresses. It expects that either the source block or the destination block do not overlap, or the source address is greater than (or equal to) the destination address. If you know that the two blocks overlap, and the source address is less than the destination address, use the empCMBD% routine instead.
The source address must equal the lowest address in the source memory block. The destination address must equal the lowest address in the destination memory block. When this routine completes, the addresses will each point just past the high end of each block.
This procedure copies bytes, so the repeat count is a number of bytes. To copy N entities of size M, set the repeat count to N*M.
To set an address to the start of a particular array item, set the address to A+I*S, where A is the address of the array, I is the array index, and S is the size of one array item.
Usage: !emdSA% = sourceaddress: !emdDA% = destinationaddress: !emdRC% = repeatcount: CALL empCMBI%
This procedure copies a memory block from one location to another location, while decrementing addresses. It expects that either the source block or the destination block do not overlap, or the source address is less than (or equal to) the destination address. If you know that the two blocks overlap, and the source address is greater than the destination address, use the empCMBI% routine instead.
The source address must equal the highest address in the source memory block, plus 1. The destination address must equal the highest address in the destination memory block, plus 1. When this routine completes, the addresses will each point at the low end of each block.
This procedure copies bytes, so the repeat count is a number of bytes. To copy N entities of size M, set the repeat count to N*M.
To set an address to the start of a particular array item, set the address to A+I*S, where A is the address of the array, I is the array index, and S is the size of one array item. Note that to copy array item #I, you would set the address to point to array item #(I+1), because this routine copies bytes in reverse.
Usage: !emdSA% = sourceaddress: !emdDA% = destinationaddress: !emdRC% = repeatcount: CALL empCMBD%
This procedure moves the data from the source memory block into the destination memory block, and vice versa. The two blocks must not overlap.
This procedure copies bytes, so the repeat count is a number of bytes. To exchange N entities of size M, set the repeat count to N*M.
To set an address to the start of a particular array item, set the address to A+I*S, where A is the address of the array, I is the array index, and S is the size of one array item.
Usage: !emdSA% = sourceaddress: !emdDA% = destinationaddress: !emdRC% = repeatcount: CALL empXMB%
This procedure fills the destination memory block with zeros, erasing any data that were there.
This procedure fills bytes, so the repeat count is a number of bytes. To fill N entities of size M, set the repeat count to N*M.
To set an address to the start of a particular array item, set the address to A+I*S, where A is the address of the array, I is the array index, and S is the size of one array item.
Usage: !emdDA% = destinationaddress: !emdRC% = repeatcount: CALL empZMB%