Pointer in Basic
#11
(07-16-2023, 09:57 PM)Kernelpanic Wrote: I've tried this now with VarPtr, Peek and Poke, and I didn't find any error in my exercise to achieve this. I don't see any at the moment either.

Remember that those commands were created to suit the 8-bit and 16-bit side of things. When Microsoft came up with WindowsXP they finished removing all the CALL INTERRUPT mechanism, the input/output ports and anything else that would make worthwhile accessing absolute memory locations to get serious work done. For a multitasking operating system using threads such commands could have caused a lot of harm and loss of money. PEEK and POKE made better sense when there was only a 64KB memory pool to work with, and everything had to fit in there: code, data, interpreter settings etc.

There used to be a page on the QB64 Wiki that listed the locations to use with PEEK and POKE to find functions in GW-BASIC and BASICA. It even had stuff to deal with the cassette recorder that was provided with the very first 16-bit IBM PC. It was extended for storing the current number of rows and columns of the terminal, for video memory and more.

QB64 emulates the DEF SEG for video memory but probably nothing else, and also what has to do with that for INP(), OUT and WAIT because an awful lot of programs written for QuickBASIC and QBasic rely on those ancient commands. Somebody programming exclusively in QB64 should never look back at that stuff. If he/she really wants pointers in BASIC "without the schmaltz" then use Freebasic or Purebasic, or just go into C or C++.
Reply
#12
@SagaraS - I haven't looked at it properly yet, those 8 bytes are on my mind.

Maybe it's like this: With an input, the start address is given, ok. If a second entry is then made, the limit of the two variables is displayed, and thus practically the new start address for the next entry. - It's just a thought. . . Let's see.

But maybe it is very simple. Everything great is simple . . . a philosopher once said.  Cool
Reply
#13
(07-18-2023, 12:17 AM)Kernelpanic Wrote: @SagaraS - I haven't looked at it properly yet, those 8 bytes are on my mind.

Maybe it's like this: With an input, the start address is given, ok. If a second entry is then made, the limit of the two variables is displayed, and thus practically the new start address for the next entry. - It's just a thought. . . Let's see.

But maybe it is very simple. Everything great is simple . . . a philosopher once said.  Cool
See in the description of my code I wrote in the last post.

QB64 reserves memory for the following data types:
_BYTE = 8 Byte
_UNSIGNED _BYTE = 8 Byte
INTEGER = 8 Byte
_UNSIGNED INTEGER = 8 Byte
LONG = 8 Byte
_UNSIGNED LONG = 8 Byte
_INTEGER64 = 8 Byte
_UNSIGNED INTEGER64 = 8 Byte
DOUBLE = 8 Byte
_FLOAT = 32 Byte

_MEM and _OFFSET work perfectly, but QB64 data types declaration does not.
QB64 reserves for 64Bit data types, and that for each data type.

You can check the output offset addresses with a memory viewer.
Reply
#14
(07-18-2023, 02:42 AM)SagaraS Wrote:
(07-18-2023, 12:17 AM)Kernelpanic Wrote: @SagaraS - I haven't looked at it properly yet, those 8 bytes are on my mind.

Maybe it's like this: With an input, the start address is given, ok. If a second entry is then made, the limit of the two variables is displayed, and thus practically the new start address for the next entry. - It's just a thought. . . Let's see.

But maybe it is very simple. Everything great is simple . . . a philosopher once said.  Cool
See in the description of my code I wrote in the last post.

QB64 reserves memory for the following data types:
_BYTE = 8 Byte
_UNSIGNED _BYTE = 8 Byte
INTEGER = 8 Byte
_UNSIGNED INTEGER = 8 Byte
LONG = 8 Byte
_UNSIGNED LONG = 8 Byte
_INTEGER64 = 8 Byte
_UNSIGNED INTEGER64 = 8 Byte
DOUBLE = 8 Byte
_FLOAT = 32 Byte

_MEM and _OFFSET work perfectly, but QB64 data types declaration does not.
QB64 reserves for 64Bit data types, and that for each data type.

You can check the output offset addresses with a memory viewer.

"but QB64 data types declaration does not."

Your table is not matching the Wiki:
https://qb64phoenix.com/qb64wiki/index.p...able_Types
2 for Integers, 4 for Long, 8 _Integer64
4, 8, 32 for Single, Double and _Float (only 10 used (seems a waste))
b = b + ...
Reply
#15
Mem is generally the easiest way to go with pointers in QB64.

Code: (Select All)
DIM i AS INTEGER, l AS LONG
i = 123
l = 456
PRINT i, PeekPE(_OFFSET(i), 2)
PokePE _OFFSET(i), 126, 2
PRINT i, PeekPE(_OFFSET(i), 2)

PRINT l, PeekPE(_OFFSET(l), 4)
PokePE _OFFSET(l), 456789, 4
PRINT l, PeekPE(_OFFSET(l), 4)




SUB PokePE (o AS _OFFSET, v AS _INTEGER64, b AS _BYTE)
    DIM m AS _MEM
    m = _MEM(o, b)
    SELECT CASE b
        CASE 1: _MEMPUT m, m.OFFSET, v AS _BYTE
        CASE 2: _MEMPUT m, m.OFFSET, v AS INTEGER
        CASE 4: _MEMPUT m, m.OFFSET, v AS LONG
        CASE 8: _MEMPUT m, m.OFFSET, v AS _INTEGER64
    END SELECT
END SUB

FUNCTION PeekPE (o AS _OFFSET, b AS _BYTE)
    DIM m AS _MEM
    m = _MEM(o, b)
    SELECT CASE b
        CASE 1: temp = _MEMGET(m, m.OFFSET, _BYTE)
        CASE 2: temp = _MEMGET(m, m.OFFSET, INTEGER)
        CASE 4: temp = _MEMGET(m, m.OFFSET, LONG)
        CASE 8: temp = _MEMGET(m, m.OFFSET, _INTEGER64)
    END SELECT
    PeekPE = temp
END FUNCTION

These 2 functions demonstrated here work more-or-less as PEEK and POKE do in QB45, with a few minor exceptions.  One, no DEF SEG is required to point to any particular block of memory, and two, you have to specify how many bytes you're reading and writing.  123 as a byte is only 1 byte after all, but 123 as an integer is 2 bytes of memory, and a long is 4 bytes.

Note that in this demo, I use PeekPE and PokePE to point to where two of my variables are in memory -- i and l.  If you don't know where you're pointing specifically, then I'd suggest not pointing at anything at all.  Just accessing memory at random isn't something I'd recommend to anyone.  At least use _MEMNEW to reserve a block of memory for your purposes, rather than pointing all willy-nilly at random areas of mem.
Reply
#16
@SagaraS - As already mentioned by bplus, this does not match the wiki. Either of them is wrong.

As one can see from the screenshot, the address actually shifts by 8 bytes. But what do SEGBlock.SIZE and SEGBlock.ELEMENTSIZE show then?

[Image: MEM-mit-drei-2023-07-18.jpg]
Reply
#17
Code: (Select All)
DIM a AS _BYTE
DIM b AS INTEGER
DIM c AS LONG
DIM d as _INTEGER64
DIM e as _FLOAT
DIM ends as _byte ' <-- should serve as the end

PRINT HEX$(_OFFSET(a))
PRINT HEX$(_OFFSET(b))
PRINT HEX$(_OFFSET(c))
PRINT HEX$(_OFFSET(d))
PRINT HEX$(_OFFSET(e))
PRINT HEX$(_OFFSET(ends))

The output shows you the memory addresses that are reserved for the variables.
Since they all follow each other in memory, you can view the addresses in a memory viewer.

QB64 actually reserves 8 bytes for each variable. Except for _FLOAT 32 bytes are reserved.

The wiki says nothing about how variables are reserved in memory.
It only says how many bytes are used.

Example:
A INTEGER used 2 bytes.
QB64 reserved 8 bytes for it in the memory.

The first 2 bytes of this reserved bytes are used. The rest remains unused.
This can be observed even via a memory viewer.

[Image: memn1fim.png]
Reply
#18
(07-18-2023, 04:26 PM)Kernelpanic Wrote: @SagaraS - As already mentioned by bplus, this does not match the wiki. Either of them is wrong.

As one can see from the screenshot, the address actually shifts by 8 bytes. But what do SEGBlock.SIZE and SEGBlock.ELEMENTSIZE show then?

[Image: MEM-mit-drei-2023-07-18.jpg]

DIM AS _UNSIGNED LONG wert, wert1, wert2 

Your SEGBlock.Offset is the memory address of where those variables are located in memory.  Even if there's a gap of 1000 bytes between the first variable and the second, that doesn't change the behavior of how we interact with those blocks of memory.  Most OSes don't tend to stack memory consecutively -- they place values with gaps between them as you're seeing with those offsets.  The reason is rather simple -- what if you start with a string of 4 bytes in memory, and then you add to it to make it a string of 6 bytes?  If all your data was stacked one bit of information packed tightly up against every other bit of information, you'd have to start moving data back and forth, and couldn't just append anything to the end of it.  <-- This is basically the reason why variable length strings aren't easily usable via mem -- the OS might move and shift them at any point, to optimize how much memory your program has left available to it.

The Segblock.TYPE is the total value of the memblock that you're looking at.  In the case above, the value is 1156 -- let's break it down as to what that represents:
1024 -- Unsigned
128 -- Integer (vs float/string/other -- it's the overall integer type and not 2-byte INTEGER)
4 -- Long

1024 + 128 + 4 = 1156 total  <-- that's what we see for the mem.TYPE here.

The Segblock.Elementsize tells you how large each element is in memory -- in this case, an unsigned long uses 4 bytes in memory.

The Segblock.Size tells you the total amount of memory in this block -- in this case, it'd be 4 bytes total.  (The same as the elementsize as we only have 1 element and not an array.)
Reply
#19
(07-18-2023, 04:49 PM)SagaraS Wrote: The output shows you the memory addresses that are reserved for the variables.
Since they all follow each other in memory, you can view the addresses in a memory viewer.
This isn't guaranteed.  It's up to the OS to decide where your variables go.  If it's a clean run of a program, things might fall into a pattern of being one after the other, but that's not guaranteed.  Once you get to the point that you're starting to free memory and reuse memory and redimension memory and all that, the position of where variables and such all fall in memory gets shuffled into unpredictable order.  You really can't count it variable2 being 4 bytes after variable 1 and 4 bytes before variable3, or any such thing as that.  If you want to know where a variable is stored in memory, use _OFFSET to get that value back for yourself, or else you might end up changing things you don't intend to.  Wink
Reply
#20
[Image: mem2a4f9l.png]

I can spin this any way I want. With _Byte, INTEGER and LONG and others 8 bytes are reserved.
See picture.

In the Memory Viewer it is stored as follows in the picture:
8 bytes for _BYTE - OFFSET 361F020
8 bytes for INTEGER - OFFSET 361F028
8 bytes for INTEGER64 - OFFSET 361F030
40 bytes for SEGBlock - OFFSET 361F038
4 bytes for STRING - OFFSET 361F060

Why doesn't it reserve me the string in OFFSET 361F021 - 361F027 ?
Why doesn't it reserve me the integer in OFFSET 361F021 - 361F027 ?
There is still free memory that is not used.   Huh


When I program in Visual Studio, it doesn't happen this way, but each variable gets the space it was dimensioned with, without wasting the space.
Reply




Users browsing this thread: 12 Guest(s)