Memory & Pointers

On 8-bit machines, PEEK(address) read a byte of memory and POKE address, value wrote one.

29.1 Historical PEEK and POKE

On 8-bit machines, PEEK(address) read a byte of memory and POKE address, value wrote one. Hardware (video, sound, input) was memory-mapped, so POKE was how BASIC programs controlled it:

On the IBM PC, QBasic used segmented memory: DEF SEG = segment then PEEK(offset)/POKE offset, v. Text screen memory started at segment &HB800 (color) and SCREEN 13 video memory at &HA000:

QB64 emulates common legacy addresses (screen memory, keyboard flags at DEF SEG = 0: PEEK(1047)) for compatibility, but cannot touch real hardware.

10 REM COMMODORE 64
20 POKE 53280, 0: POKE 53281, 0   : REM BLACK BORDER AND BACKGROUND
30 POKE 1024, 1                   : REM PUT SCREEN CODE 1 ("A") IN THE TOP-LEFT CELL
40 PRINT PEEK(197)                : REM CODE OF THE KEY CURRENTLY PRESSED
50 SYS 64738                      : REM JUMP TO MACHINE CODE (RESET)
' QBasic / QB64 (QB64 emulates these legacy memory areas)
DEF SEG = &HB800
POKE 0, ASC("Q")        ' character in the top-left cell
POKE 1, &H1E            ' attribute: yellow on blue
DEF SEG                 ' restore the default segment

29.2 VARPTR, VARSEG, SADD

Returns (QBasic) offset address of a variable segment address of a variable offset of a string's data address encoded as a string, for DRAW/PLAY "X" commands

They were used with CALL ABSOLUTE (run machine code stored in an array), BLOAD/BSAVE (load/save raw memory, e.g., screen images), and interrupts (CALL INTERRUPT in QuickBASIC). QB64 keeps VARPTR/VARSEG for compatibility within its emulated memory; for real memory work it provides _OFFSET and _MEM.

29.3 QB64 _MEM blocks

A _MEM variable describes a memory region (offset, size, type) safely, with bounds checking:

_MEM(var) / _MEM(arr()) / _MEM(offset, size) block for a variable, array, or raw address allocate a new block block covering an image's pixels _MEMGET, _MEMPUT read / write values _MEMFILL, _MEMCOPY fill a range / copy between blocks release the block

.OFFSET, .SIZE, .ELEMENTSIZE, .TYPE _OFFSET type, _OFFSET(var)

DIM arr(1 TO 10) AS LONG
FOR i = 1 TO 10: arr(i) = i * i: NEXT

DIM m AS _MEM
m = _MEM(arr())                                    ' memory block covering the whole array
PRINT "Bytes:"; m.SIZE; " Element size:"; m.ELEMENTSIZE

DIM v AS LONG
_MEMGET m, m.OFFSET + 4 * 2, v                     ' read the third element (zero-based offset)
PRINT "arr(3) ="; v
_MEMPUT m, m.OFFSET, 999 AS LONG                   ' write the first element
PRINT "arr(1) ="; arr(1)
PRINT "arr(4) ="; _MEMGET(m, m.OFFSET + 12, LONG)  ' function form
_MEMFREE m

DIM buf AS _MEM
buf = _MEMNEW(1024)                                ' allocate 1 KB
_MEMFILL buf, buf.OFFSET, buf.SIZE, 0 AS _UNSIGNED _BYTE
_MEMFREE buf                                       ' always free what you allocate

29.4 Fast pixel access with _MEMIMAGE

This is far faster than PSET for full-screen effects (plasma, fire, image filters). $CHECKING:OFF ... $CHECKING:ON around hot loops removes safety checks for extra speed — use only on debugged code.

SCREEN _NEWIMAGE(640, 480, 32)
DIM m AS _MEM, o AS _OFFSET, x AS LONG, y AS LONG
m = _MEMIMAGE(0)                                   ' 0 = the screen image
FOR y = 0 TO 479
    o = m.OFFSET + y * 640 * 4                     ' 4 bytes per 32-bit pixel
    FOR x = 0 TO 639
        _MEMPUT m, o, _RGB32((x XOR y) AND 255, x MOD 256, y MOD 256) AS _UNSIGNED LONG
        o = o + 4
    NEXT
NEXT
_MEMFREE m
SLEEP

29.5 Safety rules

Never write outside a block; _MEM checks help, raw _OFFSET arithmetic does not.

Free every _MEMNEW and _MEMIMAGE block; free images and sounds you load.

Dynamic arrays and strings can move in memory when resized — re-acquire their _MEM after REDIM.

FreeBASIC: true C-style pointers: Dim p As Integer Ptr = @x, *p = 5, p[3], Allocate/Deallocate, Peek/Poke on real addresses, StrPtr(s), VarPtr(x), ProcPtr(MySub) for function pointers. VB.NET: managed memory and garbage collection; unsafe pointers are not available in VB (unlike C#). Interop uses IntPtr, Marshal.AllocHGlobal, Marshal.Copy, and Span(Of T) for efficient buffers.