First, to access variables check the official docs:
For local storage variables or state variables, a single Yul
identifier is not sufficient, since they do not necessarily occupy a
single full storage slot. Therefore, their “address” is composed of a
slot and a byte-offset inside that slot. To retrieve the slot pointed
to by the variable x, you use x.slot, ...
The first 32 bytes of a bytes array are the length and the rest is the data.
To calculate a pointer to an array's data in assembly it will be add(array, 32).
To read the array's length it will be mload(array).
Combining them together it should be
let result := delegatecall(gas, _delegate, add(data, 32), mload(data), 0, 0)
When you declare a parameter as storage you are limiting the function to only accept parameters with that qualifier.
The qualifiers storage, memory and calldata specify where the data is located. You can convert implicitly storage to memory and calldata to memory, but the reverse is not possible.
string public stor = "banana";
function test(string ...
The problem is that a ends up pointing to a very large memory address and that causes the EVM to run out of gas, since you need to pay for memory used.
The first storage slot was modified by this line
test = "good";
Since it is a "short" string (less than 31 bytes) it is stored in one slot in compact form (data at the right and length x ...
This description distinguishes between storage and non-storage.
It seems to be using 'memory' in order to refer to non-storage.
It's probably not the best choice of words, as the keyword memory is used in order to indicate that a given variable is allocated on the heap.
In other words, you have 3 data sections:
Storage, where consistent data is located
As you said the struct is packed. Since sload(0) returns 32 bytes it is returning the whole struct.
To access the indiviual parts of the struct use bit shifts and masks.
let w := sload(0)
a := and(w, 0xffffffffffffffffffffffffffffffff)
b := shr(128, w)