В систему команд внесены изменения.
Убраны отдельные команды безусловного и дальнего переходов, вместо них добавлены две команды беззнакового условного перехода LTU, GEU.
Безусловный переход эмулируется беззнаковым сравнением GEU с нулем (R0=0), дальний переход соответствует коду "запрещенной" команды "ST R0".
R0 всегда 0 при прямой адресации, при косвенной адресации R0 содержит указатель GP, который нельзя изменить командой ST R0.
EQ src, imm -- if(A == src) PC += imm8<<1 ;signed
NE src, imm -- if(A != src) PC += imm8<<1 ;signed
LT src, imm -- if(A < src) PC += imm8<<1 ;signed
GE src, imm -- if(A >= src) PC += imm8<<1 ;signed
GT src, imm -- if(A > src) PC += imm8<<1 ;signed
LE src, imm -- if(A <= src) PC += imm8<<1 ;signed
LTU src, imm -- if(A < src) PC += imm8<<1 ;unsigned
GEU src, imm -- if(A >= src) PC += imm8<<1 ;unsigned
LD src -- A = src
ST dst -- dst = A
ADD src -- A += src
SUB src -- A -= src
AND src -- A &= src
OR src -- A |= src
XOR src -- A ^= src
ROT src -- A = rot(A, src[3:0]) ;эмуляция LSH, RSH
ADD 0 -- A += 0 ;эмуляция NOP
GEU 0 -- PC += imm8<<1 ;эмуляция безусловного перехода
ST 0 -- A = PC, PC = ALU ;эмуляция дальних переходов JMP, CALL, RET
Источник src=R0 всегда подразумевает константу 0.
Переходы всегда на четный адрес, для выравнивания используется NOP.
Программная модель памяти минималистичного ядра.
Принята в предположении, что в практических программах основной объем памяти расходуется на код и массивы, но не на одиночные константы, аргументы, переменные и указатели.
imm8(GP) подразумевает адресацию одиночных глобальных констант, функций, переменных, указателей.
imm8+FP(GP) подразумевает адресацию аргументов функций.
imm8+LP(GP) подразумевает адресацию одиночных локальных перменных, указателей, LP=FP+FSIZE.
imm8+LP+LSIZE(GP) подразумевает адрес возврата, если необходим. Функции нерекурсивные.
Если GSIZE не превышает 512 байт, то в качестве GP используется только R0.
Cверх 512 байт в качестве GP используется R1=R0+512, сверх 1024 байт - R2=R1+512, и тд.
Все эти адреса лежат внутри сегмента GSIZE, массивы в общем случае распределяются вне этого сегмента.
Для адресации массивов в общем случае используется imm8(Rn), Rn != GP, n<8.
Регистры R0..R7 лежат в памяти, начиная с абсолютного адреса 0.
Пример:
int out;
void outstr(char *s){ while(out=*s)s++; }
void main(){ outstr("Hello, world!"); }GSIZE=12байт (+ на системные нужды):
2 переменная out
2 адрес функции outstr()
2 аргумент *s
2 адрес возврата из outstr()
2 адрес функции main()
2 указатель на строку "Hello, world!"
Ассемблер, переделан из вывода -target=symbolic:
outstr:
geu 0, L3 ;безусловный переход на L3:
L2:
ld s(0)
add _1(0) ;s+=1
st s(0)
L3:
ld s(0) ;загружаем адрес текущего символа
st loadchar+4(0) ;пишем в аргумент функции
ld loadchar(0) ;загружаем адрес функции
st 0 ;переход на функцию loadchar()
ld loadchar+2(0) ;загружаем результат функции
st out(0)
bne 0, L2 ;условный переход на L2:
ld outstr+4(0) ;загружаем адрес возврата
st 0 ;возврат из outstr
main:
ld "Hello, world!\000"(0) ;загружаем указатель на строку
st outstr+2(0) ;аргумент outstr()
ld outstr(0) ;адрес outstr()
st 0 ;переход
...