Векторы: индексирование по позиции
Вектор — это массив: последовательность чисел, хранящихся рядом друг с другом в памяти. В предыдущем уроке вы суммировали его, перемещая указатель вперёд на 4 байта для каждого элемента. Тот же метод работает и здесь. Есть ещё один распространённый способ добраться до элемента: по индексу. Индекс — это позиция элемента, отсчитываемая от начала, где первый элемент имеет индекс 0, второй имеет индекс 1, и так далее.
Суммирование вектора с продвижением указателя
.dataa: .word 5, 10, 15, 20 # четыре элемента.textla t0, a # t0 = адрес ali t1, 4 # t1 = количество оставшихся элементовli t2, 0 # t2 = текущая суммаloop:beq t1, zero, donelw t3, 0(t0) # загружает текущий элементadd t2, t2, t3 # прибавляет к суммеaddi t0, t0, 4 # переходит к следующему слову (4 байта)addi t1, t1, -1 # на один меньшеj loopdone:mv a0, t2 # печатает итог: 5+10+15+20 = 50li a7, 1ecallli a7, 10ecall
Цикл выполняется следующим образом. t0 хранит адрес a, а t1 хранит количество оставшихся элементов, начиная с 4. На каждом проходе завершает цикл, когда счётчик достигает нуля. загружает элемент по адресу в t0. добавляет это значение к текущей сумме. продвигает указатель к следующему слову. уменьшает счётчик. После четырёх проходов (5 + 10 + 15 + 20) done выводит 50. Это тот же шаблон, использованный в уроке о памяти, теперь применённый к вектору.
От индекса к адресу
Индексация вычисляет адрес из базового указателя и позиции: address = base + index * 4. Число 4 — это размер слова в байтах. Компилятор генерирует такое же вычисление для a[i] в таком языке, как C. Сам индекс никогда не обращается к памяти. Это делает только вычисленный из него адрес.
slli t5, t3, 2 # t5 = индекс * 4 (размер слова)add t6, t0, t5 # t6 = адрес element[index]lw t6, 0(t6) # t6 = element[index]
сдвигает индекс в t3 влево на 2 бита, что умножает его на 4 (два удвоения: 2 x 2) — размер одного слова. Добавление результата к базовому указателю даёт адрес element[index]. Этот сдвиг — тот же метод степеней двойки из урока арифметики, и это стандартный способ масштабирования индекса на размер слова.
a[0]: индекс 0, смещение 0.
i как позиция и адрес, который эта позиция порождает после того, как масштабирует её на 4 и добавляет к базе. Шестнадцатеричные числа — это фактические адреса, которые занимает a, когда симулятор запускает эту программу. Цель — предсказать следующий адрес до того, как он появится. Это позволяет читать внутри инкрементирующего цикла или a[i] в C-подобном языке и представлять адрес памяти, к которому он обращается, вместо того чтобы воспринимать запись как чёрный ящик.Суммирование вектора по индексу
.dataa: .word 5, 10, 15, 20 # четыре элемента.textla t0, a # t0 = базовый адрес ali t1, 4 # t1 = количество элементовli t2, 0 # t2 = индекс, начинается с 0li t3, 0 # t3 = текущая суммаloop:beq t2, t1, doneslli t4, t2, 2 # t4 = индекс * 4 (смещение в байтах)add t5, t0, t4 # t5 = адрес a[index]lw t5, 0(t5) # t5 = a[index]add t3, t3, t5 # total += a[index]addi t2, t2, 1 # переходит к следующему индексуj loopdone:mv a0, t3 # печатает итог: 5+10+15+20 = 50li a7, 1ecallli a7, 10ecall
Цикл выполняется следующим образом. t2 — это индекс, начиная с 0. На каждом проходе завершает цикл, когда индекс достигает 4 — количества элементов, хранящегося в t1. преобразует индекс в байтовое смещение. добавляет это смещение к базовому адресу в t0, чтобы получить адрес a[index]. загружает значение, хранящееся там. Это значение добавляется к текущей сумме в t3, а переходит к следующему индексу. После четырёх проходов (5 + 10 + 15 + 20) done выводит 50.
Сложение двух векторов, элемент за элементом
.dataa: .word 3, 6, 9, 12 # первый векторb: .word 1, 2, 3, 4 # второй векторc: .space 16 # место для 4 слов результата.textla t0, a # базовый адрес ala t1, b # базовый адрес bla t2, c # базовый адрес cli t3, 0 # индекс, начинается с 0li t4, 4 # количество элементовli s0, 0 # текущая суммаloop:beq t3, t4, doneslli t5, t3, 2 # смещение в байтах = индекс * 4add t6, t0, t5 # адрес a[index]lw t6, 0(t6) # значение a[index]add a0, t1, t5 # адрес b[index]lw a0, 0(a0) # значение b[index]add t6, t6, a0 # a[index] + b[index]add a0, t2, t5 # адрес c[index]sw t6, 0(a0) # c[index] = a[index] + b[index]add s0, s0, t6 # итог += c[index]addi t3, t3, 1 # переходит к следующему индексуj loopdone:mv a0, s0 # печатает итог: 4+8+12+16 = 40li a7, 1ecallli a7, 10ecall
Цикл выполняется следующим образом. преобразует индекс t3 в байтовое смещение. Добавление его к каждому базовому указателю (t0, t1, t2) даёт адреса a[i], b[i] и c[i]. На каждом проходе оба элемента загружаются, складываются, сохраняются в c и добавляются к текущей сумме в s0. переходит к следующему индексу, и цикл повторяется, пока все четыре элемента не будут обработаны.
