Второе предложение использовать LCC. Я использовал его в этом 16-битном проекте RISC: http://fpgacpu.org/xsoc/cc.html.
Я не думаю, что будет иметь большое значение, собираете ли вы 8-битный вариант и используете 3 add-with-carry для увеличения IP, или 24-битный вариант и делаете все это аппаратно. Вы можете скрыть разницу в вашем ассемблере.
Если вы посмотрите мою статью выше или еще более простой процессор здесь: http://fpgacpu.org/papers/soc-gr0040-paper.pdf, вы увидите, что вам действительно не нужно так много операторов / инструкций, чтобы охватить целое число C. Фактически, существует утилита lcc (ops), которая печатает набор операторов min для данного компьютера.
Для получения дополнительной информации см. Мою статью о переносе lcc на новую машину здесь: http://www.fpgacpu.org/usenet/lcc.html
Как только я портировал lcc, я написал ассемблер, и он синтезировал больший набор инструкций из основных. Например, у моей машины был load-byte-unsigned, но не load-byte-signed, поэтому я выпустил следующую последовательность:
lbs rd,imm(rs) ->
lbu rd,imm(rs)
lea r1,0x80
xor rd,r1
sub rd,r1
Так что я думаю, что вы можете обойтись этим минимальным прикрытием операций:
registers
load register with constant
load rd = *rs
store *rs1 = rs2
+ - (w/ w/o carry) // actually can to + with - and ^
>> 1 // << 1 is just +
& ^ // (synthesize ~ from ^, | from & and ^)
jump-and-link rd,rs // rd = pc, pc = rs
skip-z/nz/n/nn rs // skip next insn on rs==0, !=0, <0, >=0
Еще проще - не иметь регистров (или эквивалентно размывать регистры с памятью - все регистры имеют адрес памяти).
Отложите регистр для SP и напишите в компиляторе обработчик функции prolog / epilog, и вам не придется беспокоиться о командах стека. Есть только код для хранения каждого из регистров сохранения вызываемого абонента, настройки SP в соответствии с размером кадра и т. Д.
Прерывания (и возврат от прерываний) просты. Все, что вам нужно сделать, это ввести команду перехода и ссылки в регистр команд. Если вы выбрали для этого битовую комбинацию, равную 0, и поместили правильные адреса в регистр источника rs (особенно, если это r0), это можно сделать с помощью входа сброса триггера или дополнительного принудительного ввода-вывода. 0 и ворота. Я использую аналогичный трюк во второй статье выше.
Интересный проект. Я вижу, что в настоящее время проводится конкурс TTL / 7400, и я сам думал о том, как легко можно обойтись без машины, и будет ли обманом добавить асинхронную SRAM 32 КБ или 128 КБ на машину для хранения кода и данных.
В любом случае, счастливого взлома!
приписка
1) Вы захотите решить, насколько велик каждый целочисленный тип. Вы, конечно, можете сделать char, short, int, long, long long и т. Д. Одного размера, если хотите, одним 24-битным словом, хотя это не будет соответствовать минимальным диапазонам представления.
2) И хотя я сосредоточился здесь на lcc, вы спрашивали о C ++. Я рекомендую убедить C сначала. Как только вы разберетесь с C, включая операторы *, /,% в программном обеспечении и т. Д., Вам будет удобнее перейти на полноценный C ++, будь то в LLVM или GCC. Разница между C и C ++ заключается в том, что «только» лишние таблицы vtables и RTTI и кодовые последовательности (полностью построенные из повторения целочисленного оператора примитива C) необходимы для обработки вызовов виртуальных функций, указателя на разыменование члена, динамического приведения, статического конструктора, исключения обработка и т. д.