Различия между версиями 9 и 10
Версия 9 от 2019-03-01 16:42:51
Размер: 25025
Редактор: FrBrGeorge
Комментарий:
Версия 10 от 2019-03-01 16:44:37
Размер: 25039
Редактор: FrBrGeorge
Комментарий:
Удаления помечены так. Добавления помечены так.
Строка 194: Строка 194:
00400000: 24080014 addiu $8,$0,0x00000014 li $t0, 20
00400004: 24090005 addiu $9,$0,0x00000005 start:     li   $t1, 5
00400008: 00085021 addu $10,$0,$8 move       $t2, $t0
0040000c: 11400002 beq $10,$0,0x00000002 loop:     beqz   $t2, fin
00400010: 01495023 subu $10,$10,$9 subu       $t2, $t2, $t1
00400014: 0401fffd bgez $0,0xfffffffd b loop
00400018: 00084043 sra $8,$8,0x00000001 fin:     sra   $t0, $t0, 1
0040001c: 15000001 bne $8,$0,0x00000001 bnez       $t0, end
00400020: 08100001 j 0x00400004 j start
00400024: 00000000 nop end:     nop
00400000: 24080014 addiu $8,$0,0x00000014         li   $t0, 20
00400004: 24090005 addiu $9,$0,0x00000005 start: li $t1, 5
00400008: 00085021 addu $10,$0,$8         move $t2, $t0
0040000c: 11400002 beq $10,$0,0x00000002 loop: beqz $t2, fin
00400010: 01495023 subu $10,$10,$9         subu $t2, $t2, $t1
00400014: 0401fffd bgez $0,0xfffffffd         b loop
00400018: 00084043 sra $8,$8,0x00000001 fin: sra $t0, $t0, 1
0040001c: 15000001 bne $8,$0,0x00000001         bnez $t0, end
00400020: 08100001 j 0x00400004         j   start
00400024: 00000000 nop end: nop

Регистры и модель памяти

Базовая лекция на Moodle

Вступление: понятие о конвенциях

Регистры

Регистр

Программное имя

Регистр

Программное имя

r0

zero

r16

s0

r1

at

r17

s1

r2

v0

r18

s2

r3

v1

r19

s3

r4

a0

r20

s4

r5

a1

r21

s5

r6

a2

r22

s6

r7

a3

r23

s7

r8

t0

r24

t8

r9

t1

r25

t9

r10

t2

r26

k0

r11

t3

r27

k1

r12

t4

r28

gp

r13

t5

r29

sp

r14

t6

r30

s8, fp

r15

t7

r31

ra

  • Только два регистра особенные — zero (r0, всегда равен 0) и ra (r31, автоматически заполняется некоторыми командами)

    • Теоретически можно использовать любой, но это сильно затрудняет написание работающих программ

    • ⇒ возникает понятие конвенции (договорённости)

  • Регистр at (r1) используется псевдоинструкциями (например, для адреса при косвенной адресации)

  • Регистры a0 - a3 (r4 - r7) используются для передачи параметров подпрограммам

    • Очевидно, у подпрограмм может быть более 4 параметров, так что здесь тоже вступают в силу конвенции, и очень разнообразные
  • Регистры v0, v1 (r2, r3) используются для возврата значений (почему два?)

  • Регистры t0 - t9 (r8-r15, r24,r25) можно использовать без ограничений

  • Регистры s0 - s8 (r16 - r23, r30) по договорённости необходимо восстанавливать в исходные значения перед выходом из подпрограммы. При этом даже если они используются вне подпрограммы, код сохранения и восстановления обязан присутствовать.

  • Регистры k0, k1 (r26, r27) используются для взаимодействия с ядром

  • Регистр sp (r29) содержит ссылку на вершину стека (stack pointer)

  • Регистр gp (r28) хранит адрес области глобальных данных (global pointer). Нужен, например, для хранения «глобальных переменных», доступных в том числе и из подпрограмм (конвенция!), или для передачи данных со стороны операционной системы

  • Регистр s8 (r30) в некоторых конвенциях организации подпрограмм используется для хранения ссылки на область данных текущей подпрограммы, поэтому он носит ещё одно название — fp (frame pointer)

Ещё два регистра — HI и LO — используются в командах деления и умножения

Плоская модель памяти

0xffffffff

highest address in kernel (and memory)

Память устройств

Последний адрес, доступный ядру

0xffffffff

memory map limit address

Конец памяти устройств

0xffff0000

MMIO base address

Начало памяти устройств

0xfffeffff

kernel data segment limit address

Область данных ядра

Конец данных ядра

0x90000000

.kdata base address

Начало данных ядра

0x8ffffffc

kernel text limit address

Область кода ядра

Предел кода ядра

0x80000180

exception handler address

Обработчик прерываний

0x80000000

.ktext base address

Начало кода ядра

0x80000000

lowest address in kernel space

Начало памяти ядра

0x7fffffff

highest address in user space

Область данных

Последняя ячейка, доступная пользователю

data segment limit address

Последняя ячейка области данных

0x7ffffffc

stack base address

Адрес исчерпания стека

0x7fffeffc

stack pointer $sp

Сюда указывает регистр стека (растёт вниз)

0x10040000

stack limit address

Стек может расти досюда

heap base address

Начало кучи (растёт вверх)

0x10010000

.data base Address

Начало статических данных

0x10008000

Global Pointer $gp)

Сюда указывает регистр глобальных данных

0x10000000

.extern Base Address

Область глобальных данных

Data Segment base address

Начало области данных

0x0ffffffc

text limit address

Область программного кода

Последняя ячейка области программного кода

0x00400000

.text Base Address

Начало программы

0x00000000

Зарезервированная область

  • Резервированная память (до 0x400000) может быть использована операционной системой для различных нужд. Например, в MARS директивы .text и .data приводят к заполнению памяти непосредственно по указанным адресам. На самом деле чаще всего результат трансляции записывается в исполняемый файл, который имеет довольно сложный формат, а при необходимости загружается в память в соответствии со специальными таблицами размещения, динамической компоновкой и т. п. Некоторые из этих данных нужны для работы программы под управлением ОС, они-то и размещаются в младших адресах памяти. Чтение и запись в эту область запрещены.

  • Text base — область для инструкций программы. Теоретически никто не мешает иметь несколько директив .text, размещающих код по различным адресам в пределах 0x400000 - 0x1000000. Обычно после загрузки программы, когда она начала работать, запись по адресам 0x400000 - 0xffffff запрещена.

  • Extern — область для внешних данных (нужна для взаимодействия с ОС). Кроме того, метки в этой области оказываются «видны» при сборке программ из нескольких файлов.
  • Data base — область, в которую обычно раскладываются данные директивами .data. Именно там лежат переменные, объявленные массивы и прочее. Традиционно имеется зазор между началом области данных (0x10000000) и непосредственно статическими данными (0x10010000 - 0x10040000). Обычно в процессе работы программы нельзя переходить по адресам из области данных и декодировать их как инструкции.

  • Heap (Куча) — область данных, в которую принято помещать динамические данные. Идея в повторном использовании одних и тех же областей памяти для различных нужд. Для этого служат процедуры выделения памяти, в которых запоминается размер и адрес запрошенного фрагмента, и освобождения, в которых эти данные объявляются устаревшими (можно совсем забыть, а можно область пометить как свободную), после чего очередная процедура выделения вполне может выдать ту же самую область. Механизмы выделения/освобождения памяти (т. н. memory managment) обычно довольно непросты, и соответствующие функции предоставляет ОС. Добавление и освобождение данных в куче обычно происходит в сторону увеличения адреса.

  • Stack — область динамических данных особого вида, реализующая абстракцию «стек» и используемая при вызове подпрограмм и передачи им параметров. Добавление и освобождение данных в стеке обычно происходит в сторону уменьшения адреса. Бесконтрольное снятие данных со стека может привести к тому, что регистр стека начнёт указывать за пределы пользовательской памяти, поэтому (и по каким-то ещё соображениям) изначально $sp указывает не на самое «дно» стека, а существенно ниже (под 0x7ffff000). Стек и куча растут навстречу друг другу, и могут занимать друг дружкину память, лишь бы не пересекались.

  • KText и KData. Начиная с адреса 0x80000000 идёт область, недоступная программе пользователя. Это область кода и данных ядра. Безотносительно к тому, запущена программа под управлением ОС или «на голом железе», для исполнения кода и доступа к памяти требуется особый режим работы процессора. Чтение, запись и переход с использованием адресов ядра пользовательской программе запрещены.

  • Среди прочего программного кода ядра выделяется адрес обработчика прерываний (0x80000180). По этому адресу передаётся управление при возникновении исключительной ситуации (наподобие переполнения или обращение к «не своей» памяти)
  • Область MMIO служит для адресации ячеек, вообще не принадлежащих оперативной памяти. Обращение по этим адресам приведёт к взаимодействию с данными на внешних устройствах (обычно с регистрами ввода-вывода или собственной памятью устройств)

Задание: проверить, можно ли в MARS прочитать байт из раздела .text по нечётному адресу? (Почему :) ?)

Директивы размещения данных в памяти

В программе на языке ассемблера возникает необходимость описать содержимое сегмента памяти. Для этого код программы помечается .text, а данные — .data . При трансляции в MARS код размещается с адреса 0x400000 (если не сказано иное), а данные — с адреса 0x10010000 (опять-таки, если не сказано иное).

  • В секции .data помещают директивы (указания ассемблеру) по размещению данных в памяти.

    • .word ­число — одно или несколько 4-байтовых чисел

  • .half число — одно или несколько 2-байтовых чисел

  • .byte число — одно или несколько однобайтовых чисел

  • .ascii "строка" — последовательность символов в кодировке ASCII

  • .asciiz "строка" — то же, только после последнего символа обязательно записывается нулевой байт (конец строки, договорённость, например, для языка Си) Пример размещения данных различного размера:

    .data
            .word   0xbad0feed
            .half   0x1234, 0x5678
            .byte   12, 13, 14, 15
            .half   0x3344
            .byte   0x66, 0x77
    Результат трансляции пословно (секция .data начинается по умолчанию с адреса 0x10010000). Обратим внимание на big endian: младший байт в слове имеет меньший адрес!
    10010000: bad0feed 56781234 0f0e0d0c 77663344

    Для того, чтобы обращаться к соответствующим ячейкам памяти, можно использовать и адреса. и метки. Метка — символическое имя, оно заменяет адрес в программе на языке ассемблера и транслируется в соответствующий адрес в машинных кодах.

    .data
            .word   0x12345
    var:    .word   0x6789A
            .word   0xBCDEF
    
    .text
            lw      $t1, 0x10010000
            lw      $t2, var
            lw      $t3, var+4
    Обратите внимание на то, что все псевдоинструкции раскрываются в одинаковые команды (разное только смещение):
     Address    Code        Basic               Source
    
    00400000: 3c011001  lui $1,0x00001001       lw  $t1, 0x10010000
    00400004: 8c290000  lw $9,0x00000000($1)
    00400008: 3c011001  lui $1,0x00001001       lw  $t2, var
    0040000c: 8c2a0004  lw $10,0x00000004($1)
    00400010: 3c011001  lui $1,0x00001001       lw  $t3, var+4
    00400014: 8c2b0008  lw $11,0x00000008($1)

    Загружать во временный регистр $at (он же $1) адреса начала секции данных, а прибавлять смещение относительно её начала — типичное поведение ассемблера MIPS. получается чуть более читаемый машинный код, а такой «круглый» адрес, как 0x10010000 (с нулевым младшим полусловом) можно загружать одной командой загрузки старшего полуслова lui. Начиная со значения, указанного в директиве .data (или с адреса по умолчанию), ассемблер высчитывает адрес, который будет соответствовать метке, каждый раз прибавляя размер очередной отведённой ячейки. Обращение к ячейке памяти размером N байтов в архитектуре MIPS возможно только при условии, что адрес этой ячейки кратен N (следствие big endian). Поэтому при размещении ассемблером ячеек разного размера происходит выравнивание: если очередная ячейка имеет размер, которому не кратен текущий предполагаемый адрес её размещения, к этому адресу дополнительно прибавляется от одного до трёх байтов, чтобы обеспечить кратность. Пример разнообразных данных в памяти с автоматическим и ручным выравниванием.

    .data
            .word   0x76543210
            .half   0x2468
            .word   0x76543210
            .byte   1
            .word   0x76543210
            .byte   3
            .half   0x2468, 0x0ac4
            .byte   5, 7, 9
            .align  2
            .byte   1, 3, 5
            .align  3
            .byte   7
            .align  1
            .byte   9

    Выравнивание можно вызвать дирекитвно, с помощью .align номер (где номер 0,1,2 и 3 соответствует байту, полуслову, слову и двойному слову соответственно). Вот во что превращается пример выше. Цветом выделены байты, добавленные для выравнивания; не забываем про big endian: для выравнивания на границу двойного слова к адресу 0x10010023 пришлось добавить пять байтов!

    • align.png

    10010000: 76543210 00002468 76543210 00000001 76543210 24680003 07050ac4 00000009
    10010020: 00050301 00000000 00090007 00000000 00000000 00000000 00000000 00000000

    Директива .data автоматически заполняет область данных, начиная с 0x10010000, причём последующие директивы .data продолжают заполнение с последнего незанятого адреса. Однако можно указывать адрес размещения данных явно в виде параметра .data. Директивы .data можно перемежать с директивами .text, это никак не повлияет на размещение данных:

    .data
            .word 0x123456
            .word 1
    .text
            move    $t1, $zero
    .data
            .word 0x7890a
            .word 2
    
    .data   0x10010100
            .word 0x334455
    Память в результате:
    10010000: 00123456 00000001 0007890a 00000002 00000000 00000000 00000000 00000000
    10010020: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
    . . .
    100100e0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
    10010100: 00334455 00000000 00000000 00000000 00000000 00000000 00000000 00000000

Адресация в секции кода

Размер ячейки команды в MIPS всегда 4 байта, поэтому

  • Попытка перехода на адрес,не кратный четырём, вызывает ошибку
  • Непосредственный адрес, используемый в инструкциях I и J типов, хранится без двух последних битов, которые и так всегда равны 0. Можно условно считать, что адресация происходит «по номерам ячеек», это удобно для относительных переходов
  • Адресация в командах «b» (branch) — относительная, адрес представляет собой смещение относительно счётчика команд, которое надо к нему прибавить для перехода. Переход назад — отрицательное число в дополнительном коде (напоминаем, используется младшие 16 битов ячейки). Это позволяет адресовать ±128 килобайтов кода, что вполне достаточно для повседневного программирования

  • Адресация в командах «j» (jump) — абсолютная (используется младшие 26 битов ячейки). Этого достаточно для адресации четверти всей секции пользовательского кода Вопрос: а точно ли четверти? Проверьте! Пример (в меру бессмысленного) кода с различными переходами:

    .text
            li      $t0, 20
    start:  li      $t1, 5
            move    $t2, $t0
    loop:   beqz    $t2, fin
            subu    $t2, $t2, $t1
            b       loop
    fin:    sra     $t0, $t0, 1
            bnez    $t0, end
            j       start
    end:    nop

    Результат трансляции (li ассемблер, как обычно, заменил на сложение с нулём):

    00400000: 24080014  addiu $8,$0,0x00000014          li    $t0, 20
    00400004: 24090005  addiu $9,$0,0x00000005  start:  li    $t1, 5
    00400008: 00085021  addu $10,$0,$8                  move  $t2, $t0
    0040000c: 11400002  beq $10,$0,0x00000002   loop:   beqz  $t2, fin
    00400010: 01495023  subu $10,$10,$9                 subu  $t2, $t2, $t1
    00400014: 0401fffd  bgez $0,0xfffffffd              b     loop
    00400018: 00084043  sra $8,$8,0x00000001    fin:    sra   $t0, $t0, 1
    0040001c: 15000001  bne $8,$0,0x00000001            bnez  $t0, end
    00400020: 08100001  j 0x00400004                    j     start
    00400024: 00000000  nop                     end:    nop

    Заметим, что относительная адресация высчитывается не от текущей, а от следующей ячейки (т. е. сначала к счётчику команд автоматически прибавляется 4, а затем вступает в силу адресная арифметика b-команды). Количество секций кода может быть также любым, только необходимость этого неочевидна :). Основное назначение — сборка программы из нескольких файлов на языке ассемблера, например, основной код и заранее написанные функции. Возможно, удобнее перемежать секции данных кодом, работающим именно с этими данными.

LecturesCMC/ArchitectureAssembler2019/03_RegistersMemory (последним исправлял пользователь FrBrGeorge 2019-05-17 15:13:12)