Найти тему
Лучнег Х.

Динамическая память на ассемблере?!! Или даешь хардкод!!!

И так в продолжение статьи перехожу к коду.

Что потребуется знать:

  • Ядро имеет набор инструкций THUMB-2 в него входят 16битные инструкции и 32битные расширенные;
  • Слово (WORD) - 32 бита, полуслово (HWORD) - 16 бит, байт (BYTE) - 8 бит;
  • Регистровый файл состоит из регистров R0-R15, где R0-R12 - регистры общего назначения. R0-R7 - младшие регистры, R8-R12 - старшие регистры. С младшими регистрами будут работать как правило 16битные инструкции со старшими только 32битные. R13 (SP) - регистр указателя вершины стека, R14 (LR) - регистр связи (в нормальном режиме содержит адрес возврата, в режиме обработки прерываний предыдущее состояние ядра и позицию вложенности), R15 (PC) - регистр указателя текущей команды;
  • Объем оперативной памяти (SRAM) 20кБ, расположена начиная с адреса 0х20000000;
  • PUSH {список_регистров} (псевдооператор) пушит на стек один или список регистров через запятую, LDR - записывает данные в регистр из адреса, расположенного в другом регистре. MOV - может копировать из регистра в регистр или записать 8битное непосредственное значение. STR - записывает из регистра данные по адресу, расположенному в другом регистре. Подробнее об инструкциях можно посмотреть в "PM0056 Programming manual".
  • Команды являются трехадресными, то есть записываются в стиле КОМ [приемник], операнд1, операнд2, если приемник не указан явно, то приемником будет операнд1.

Итак для начала потребуется в файле link.ld в области определения секций следующее:

SECTIONS
{
.code : {
. = ALIGN(4);
*(.vectors); /* Указатели векторов прерываний */
*(.text); /* */
*(.asmcode); /* Текст программы */
*(.rodata); /* Константы */
} > FLASH
.bss : {
. = ALIGN(4);
PROVIDE(_BSS_START = .);
*(.bss); /* Переменные в SRAM */
PROVIDE(_BSS_END = .);
} > SRAM
}
"FREE_SRAM_START"=.;

Строка "FREE_SRAM_START"=.; создает константу линковщика с именем FREE_SRAM_START, которая указывает на начало свободного участка оперативной памяти после области глобальных переменных.

Далее в файле с кодом менеджера памяти пишу пролог и секцию с глобальными переменными.

.syntax unified
.thumb
.cpu cortex-m3
.section .bss
.align (2)
TOTAL_FREE_RAM_MEMORY: .word 0x0 //здесь хранится количество байт оставшей свободной памяти
FREE_RAM: .word 0X0 //а здесь указатель на начало свободного участка

Далее нужно инициировать указатель на свободный участок:

.section .asmcode

.global MemoryInit
MemoryInit:
ldr r0, =FREE_SRAM_START //вот здесь и применится константа линковщика
ldr r1, =FREE_RAM
str r0, [r1] //вот тут указатель и инициируется

Теперь неплохо было бы уметь определять количество оставшейся памяти:

.global GetFreeMemAmount
GetFreeMemAmount: //out - в R2 количество свободной памяти
PUSH {r0,r1}
LDR r1, =FREE_RAM //в r1 адрес указателя на свободную память
LDR r1, [r1] //в r1 вобственно сам адрес начала свободной памяти
MOV R0, SP // в r0 адрес вершины стека
SUB R0, #200 // задается буфер на 50 записей
RSB R1, R0 // r1 - объем свободной памяти
MOV R2, R1
POP {r0,r1}
BX LR

И собственно выделение массива:

.global GetArray
//IN - r0, размер массива, байт, r1 - размер элемента 0 - байт, 1 - полуслово, 2 - слово
//OUT - r5, указатель на массив (адрес)
GetArray:
push {r0-r4,lr}
MVN r5, #1
ADD r5, #1 // r5 = 0xFFFFFFFF
LSL R4, r0, r1 // byte_array_size{r4} = size * data_size
LSL R2, R1, #16 // размер данных распологается в старшем полуслове
ORR R0, R0, R2 // создается дескриптора [размер_элемента + количество]
ADD r4, #4 // byte_array_size += 4 - размер массива с учетом дескриптора
BL GetFreeMemAmount // проверка наличия свободной памяти
cmp r2, r4 // if (TOTAL_FREE_RAM_MEMORY < array_size)
bmi GetArray_exit // then return -1
LDR r3, =FREE_RAM // r3 = ppFREE_RAM
LDR r5, [r3] // array{r5} = *ppFREE_RAM
ADD r2, r5, r4 // r2 = *ppFREE_RAM{r5} + array_size
STR r2, [r3] // *ppFREE_RAM{[r3]} = r2
str r0, [r5] // *array{[r5]} = array_size
add r5, #4 // array += 4 (shift addr by word)
GetArray_exit: // return array{r5}
pop {r0-r4,lr}
BX LR

Выделение с заполнением:

.global GetFilledArray
GetFilledArray:
PUSH {r0-r4,LR}
BL GetArray // выделить массив размера R0 элементов размера R1
MOV r3, #0 // счетчик циклов
GetFilledArray_Fill_Loop:
LSL r4, r3, r1 //вычисление смещения r4 = r3*2^r1
CMP r1, #0 //switch(r1)
BEQ GFA_BYTE
CMP r1, #1
BEQ GFA_HALF_WORD
CMP r1, #2
BEQ GFA_WORD
GFA_BYTE: //case(0):
STRB r2, [r5,r4] //запись байта
B GFA_Exit //break;
GFA_HALF_WORD: //case(1):
STRH r2, [r5,r4] //запись полуслова
B GFA_Exit //break;
GFA_WORD: //case(2)
STR r2, [r5,r4] //запись слова
GFA_Exit: //break;
ADD r3, #1
CMP r3, r0
BNE GetFilledArray_Fill_Loop
POP {r0-r4,LR}
BX LR

Освобождение памяти:

.global ReleaseArray
// IN: r0 - указатель на массив
ReleaseArray:
push {r0,r2}
SUB r0, #4 @ array{r0} -= 4 -> *array{[r0]} == array_size - array_start_address
LDR r2, =FREE_RAM @ r2 - адрес указателя на свободную память
str r0, [r2] @ запись адреса начала освобождаемого массива в указатель на начало свободной памяти
pop {r0,r2}
BX LR