Тогда еще добавка из последнего:
Добавил поддержку локальных меток — начинаются с точки и по факту разворачиваются внутри парсера в lastGlobalLabel.thisLocalLabel таким образом можно обратится к метке из любой точки программы по полному имени, но в пределах одной процедуры можно обращаться по короткому имени. При этом создание символов через = не засчитывается как глобальная метка после которой локальные будут соединятся с ней — только прямые объявления меток.
Добавил ключевое слово ds x [ y ] которое создаёт массив размером x слов заполненных значением y (если не указано — 0).
Для краткости и понятности вызова процедур ввёл 4 псевдоинструкции:
call arg
; эквивалентно следующему:
[ sp ] =+2 pc
pc = arg
ret
; эквивалентно
pc = [ sp ]
; а так же для быстрых вызовов:
qcall arg
; эквивалентно
r4 =+2 pc
pc = arg
qret
; эквивалентно
pc = r4
В силу того как парсером обрабатываются коды условий типа @nz @z — их можно присовокуплять к этим инструкциям точно так же как к обычным. Однако надо помнить, что если адрес процедуры есть не прямая метка (addr16), а содержимое регистра, то call (как и qcall) неприменима, т.к. первой инструкцией в ней должна быть [ sp ] =+1 pc, поэтому косвенные переходы по крайней мере пока надо расписывать полностью.
Так же PORT_CONSOLE теперь еще работает на ввод возвращая или 0 или символ последней нажатой клавиши (пока по сути обёртка над kbhit/getch без учёта какой то виртуальной архитектуры).
Так же еще кучу багов вымел как в виртуальной машине так и в ассемблере.
В общем теперь возможно написать такую программу:
PORT_CONSOLE = $FFFF
sp = $FF00
pc = start
; string_input
; in: r0 - string buffer
; r1 - max buffer size
; out:
string_input r3 = r0 ; remember beginning
.loop r2 =? [ PORT_CONSOLE ]
pc = .loop @z
r2 <?> 13
pc = .end @z ; if CR
r2 <?> 8
pc = .backsp @z ; if BS
r1 =? r1
pc = .overfl @z ; if buffer overflow
; accept symbol
[ PORT_CONSOLE ] = r2
r1 =-1 r1
[ r0 ] = r2
r0 =+1 r0
pc = .loop ; continue input
; backspace
.backsp r0 <?> r3
pc = .loop @z ; ignore del at start of line
[ PORT_CONSOLE ] = r2
[ PORT_CONSOLE ] = 32 ; erase prev symbol at (windows) console...
[ PORT_CONSOLE ] = r2
r1 =+1 r1
r0 =-1 r0
pc = .loop
; overflow
.overfl pc = .loop ; just continue
; end
.end [ r0 ] = 0
ret
; string_print
; in: r0 - string buffer
string_print r1 =? [ r0 ]
ret @z
r0 =+1 r0
[ PORT_CONSOLE ] = r1
pc = string_print
; string_len
; in: r0 - string buffer
; out: r0 - length of the string
string_len r1 = 0
.loop r2 = [ r0 ]
pc = .end @z
r0 =+1 r0
r1 =+1 r1
pc = .loop
.end r0 = r1
ret
start
r0 = msg1
call string_print
r0 = buf
r1 = 10
call string_input
[ PORT_CONSOLE ] = 10
r0 = msg2
call string_print
r0 = buf
call string_print
r0 = CrLf
call string_print
dw 0
buf ds 12 $AAAA
msg1 dw "Enter command: " 0
msg2 dw "You entered this text: " 0
CrLf dw 13 10 0
Программа выведет приглашение ввести с клавиатуры текст в буфер ограниченный десятью символами и выведет потом введённый текст в консоль же.
В принципе это уже приближается к реальному машинописанию на реальном ассемблере, можно писать достаточно сложные программы и почувствовать отклик от них.
И ощущения от архитектуры двоякие.
С одной стороны сам ассемблерный код несмотря на сильно упрощенный синтаксис и крайнюю схожесть с человекочитаемыми операторами из сишечки всё равно выглядит как стена ассемблерного и плохосчитываемого кода. :D Какой то революции человекочитаемого ассемблера не случилось.
С другой стороны мозг реально разгружен когда _пишешь_ на этом ассемблере по сравнению с классикой — не нужно как в Z80 на том же спектруме постоянно задумываться над тем как и куда перекинуть результаты из аккумулятора или HL, во что развернуть проверку регистровой пары на достижение нуля, какие там есть двухбайтовые инструкции на которых можно сэкономить и т.п.
8<=============
А виртуальной машины пока еще нет чтобы эффектами меряться. Да и много чего нет — инструкции в АЛУ даже вводятся по мере того как появляются в них потребности. Это в свою очередь интересно тем, что как только видно что какая то инструкция часто нужна, то берешь и вводишь её — например move with flags update которая пишется в этом синтаксисе как =? и перемещает данное обновляя флаги S и Z обычно не встречается, но тут сразу попросилась разгружать циклы для asciiz-строк.
Если мне нужно программировать для Profi в CP/M? И при этом мне нужно запустить скомпилированную программу из командной строки с определенными параметрами? Это возможно?
круто, у меня тоже был Кворум 64к и я даже уже знал про то что он совместим с CP/M за счёт дополнительных 16Кб ОЗУ и особых режимов памяти, но техническую информацию об этом так и не смог найти в своё время. спасибо за ссылку!
Я не знаю английского. В русскоязычные играл, но под CP/M нет таких. Хотя можно попробовать перевести. Только вот построение фраз (команд) останется на английская манер.
Он и объявляет следующую игру!
И ждём диплома от nodeus !)
Добавил поддержку локальных меток — начинаются с точки и по факту разворачиваются внутри парсера в lastGlobalLabel.thisLocalLabel таким образом можно обратится к метке из любой точки программы по полному имени, но в пределах одной процедуры можно обращаться по короткому имени. При этом создание символов через = не засчитывается как глобальная метка после которой локальные будут соединятся с ней — только прямые объявления меток.
Добавил ключевое слово ds x [ y ] которое создаёт массив размером x слов заполненных значением y (если не указано — 0).
Для краткости и понятности вызова процедур ввёл 4 псевдоинструкции:
В силу того как парсером обрабатываются коды условий типа @nz @z — их можно присовокуплять к этим инструкциям точно так же как к обычным. Однако надо помнить, что если адрес процедуры есть не прямая метка (addr16), а содержимое регистра, то call (как и qcall) неприменима, т.к. первой инструкцией в ней должна быть [ sp ] =+1 pc, поэтому косвенные переходы по крайней мере пока надо расписывать полностью.
Так же PORT_CONSOLE теперь еще работает на ввод возвращая или 0 или символ последней нажатой клавиши (пока по сути обёртка над kbhit/getch без учёта какой то виртуальной архитектуры).
Так же еще кучу багов вымел как в виртуальной машине так и в ассемблере.
В общем теперь возможно написать такую программу:
Программа выведет приглашение ввести с клавиатуры текст в буфер ограниченный десятью символами и выведет потом введённый текст в консоль же.
В принципе это уже приближается к реальному машинописанию на реальном ассемблере, можно писать достаточно сложные программы и почувствовать отклик от них.
И ощущения от архитектуры двоякие.
С одной стороны сам ассемблерный код несмотря на сильно упрощенный синтаксис и крайнюю схожесть с человекочитаемыми операторами из сишечки всё равно выглядит как стена ассемблерного и плохосчитываемого кода. :D Какой то революции человекочитаемого ассемблера не случилось.
С другой стороны мозг реально разгружен когда _пишешь_ на этом ассемблере по сравнению с классикой — не нужно как в Z80 на том же спектруме постоянно задумываться над тем как и куда перекинуть результаты из аккумулятора или HL, во что развернуть проверку регистровой пары на достижение нуля, какие там есть двухбайтовые инструкции на которых можно сэкономить и т.п.
8<=============
А виртуальной машины пока еще нет чтобы эффектами меряться. Да и много чего нет — инструкции в АЛУ даже вводятся по мере того как появляются в них потребности. Это в свою очередь интересно тем, что как только видно что какая то инструкция часто нужна, то берешь и вводишь её — например move with flags update которая пишется в этом синтаксисе как =? и перемещает данное обновляя флаги S и Z обычно не встречается, но тут сразу попросилась разгружать циклы для asciiz-строк.
для наглядности взял бы ты сырцы покороче от какого-нибудь спековского эффекта и переписал на своём
румынский генштаб не разобрался )
ну вот так, уже руки отваливаются :)
Пробовал еще разок :)
ZORK 1 и 3 запущенные на Spectrum Profi :)