I can not understand the condition "When writing a program to index arrays of structures, use byte by address I_struc". The x86 addressing system does not allow single-byte memory operands to address something. And to overload a byte from memory to the register, increase by 1 and save back into memory and all in order to multiply it later by the size of the structure and get to the array element through it, this is an obvious search. To go to the next element it is much easier to simply shift the pointer in the register to the size of the structure.
Here, from my point of view, the optimal algorithm for filling such a tree. All memory operations and the use of constants inside loops are minimized.
mov ax, Data1 mov ds, ax push offset A3 ; Сохраняем адреса уровеней дерева на будущее, в обратном порядке (т.к. стек) push offset A2 push offset A1 mov ax, type Node ; AX - размер структуры, для быстрого использования mov dh, 1 ; DH - количество элементов на текущем уровне дерева mov di, offset A0 ; DI - Адрес второго уровня дерева, в начале цикла он станет первым уровнем (SI) mov cx, 3 ; CX - Количество оставшихся уровней дерева LevelUP: ; Начало цикла по уровням дерева mov si, di ; SI - Указатель на текущий элемент первого уровня (прошлый второй уровень) pop di ; Загружаем из стека новый адрес второго уровня mov bx, di ; BX - смещение текущего элемента второго уровня mov dl, dh ; DL - оставшееся количество не обработанных элементов в первого уровня NextNode: ; Цикл по элементам первого уровня mov [si].Node.field1[0], bx add bx, ax ; Смещаем указатель на следующий элемент второго уровня mov [si].Node.field1[1], bx add bx, ax add si, ax ; Смещаем указатель на следующий элемент первого уровня dec dl jnz NextNode shl dh, 1 ; На следующем уровне в 2 раза больше элементов loop LevelUP