QB64Team / qb64

BASIC for the modern era.
https://www.qb64.org
Other
666 stars 94 forks source link

`REDIM _PRESERVE` multi-dimensional array shifts data #3

Open QB64Bot opened 4 years ago

QB64Bot commented 4 years ago

Issue by Kroc Monday Jan 22, 2018 at 21:49 GMT Originally opened as https://github.com/Galleondragon/qb64/issues/27


Though the unpredictability of changing the sizes of non-last dimensions in a multi-dimension arrays is common knowledge in most forms of BASIC, the QB64 documentation does not specifically call this out. The documentation implies that it should work, and the compiler / run-time gives no warnings or error. Your data just magically gets messed up!

Test code:

PRINT "REDIM TEST"
REDIM TEST(1 TO 2, 1 TO 3) AS INTEGER
LET TEST(1, 1) = 100
LET TEST(1, 2) = 200
LET TEST(1, 3) = 300
PRINT TEST(1, 1); TEST(1, 2); TEST(1, 3)
REDIM _PRESERVE TEST(1 TO 3, 1 TO 3) AS INTEGER
PRINT TEST(1, 1); TEST(1, 2); TEST(1, 3)
END

Suggested solutions:

I am building a two dimensional cross-check array, where the bounds are increasing as new data is read in. It would be difficult to store all the data (without knowing the final bounds until read) and dimension the array at the end.

QB64Bot commented 4 years ago

Comment by ghost Monday Jan 22, 2018 at 23:59 GMT


Verified.

QB64 indeed gets this wrong somehow, and I'm not entirely certain why INTEGER is taking up 4 bytes instead of 2 originally, or why it switches to require 6 bytes instead of the original 4, though it at least sets the correct segment when it comes to usage with VARSEG:

Before:
   100     200     300    // Value
7C31B0  7C31B4  7C31B8    // real offset (_MEM)
9FFF:0  9FFF:4  9FFF:8    // VARSEG:VARPTR

After:
   100       0       0    // Value
7C31A0  7C31A6  7C31AC    // real offset (_MEM)
9FFE:0  9FFE:6  9FFE:C    // VARSEG:VARPTR

The proper output would be something like the following:

Before:
   100     200     300    // Value
7C31B0  7C31B2  7C31B4    // real offset (_MEM)
9FFF:0  9FFF:2  9FFF:4    // VARSEG:VARPTR

After:
   100     200     300    // Value
7C31A0  7C31A2  7C31A4    // real offset (_MEM)
9FFE:0  9FFE:2  9FFE:4    // VARSEG:VARPTR

The same bug occurs even if allocated outside of the "cmem" block:

Before:
   100     200     300
000B20  000B24  000B28

After:
   100       0       0
000B20  000B26  000B2C

Expected output, assuming no reallocation:

Before:
   100     200     300
000B20  000B22  000B24

After:
   100     200     300
000B20  000B22  000B24
SkyCharger001 commented 4 years ago

this is not an actual bug, but a mismatch between QB64's Little-Endian array logic address = leftDimensionAddress + (rightDimensionAddress * leftDimensionSize) and the Big-Endian array logic of the previous posters. address = rightDimensionAddress + (leftDimensionAddress * rightDimensionSize)

edit: the fact that the addresses were shifting by 4 bytes (2 integers) and 6 bytes (3 integers) respectively should have clued them in on this.