Строки 20 и 30 поменять местами и убрать строку 40, она бессмысленна.
То бишь сперва заносим число в SEED, а потом уже вызываем код.
Возможны ещё баги в кодовом блоке, уже забыл, что делают те две процедуры.
Вид для печати
Строки 20 и 30 поменять местами и убрать строку 40, она бессмысленна.
То бишь сперва заносим число в SEED, а потом уже вызываем код.
Возможны ещё баги в кодовом блоке, уже забыл, что делают те две процедуры.
А работать оно всё равно должно. Если сбрасывается, то дело техническое, испортился бейсиком машкод, не та версия бейсика или ещё что-то такое.
И ещё, вместо:
LD DE,1
SBC HL,DE
лучше писать
LD DE,-1
ADD HL,DE
Так короче, быстрее, и не влияет флаг C
А разве не портит калькулятор HL'? Может, потому и глючит на выходе в Барсик.
Господа ))) Не нужно столько всего. Просто тихо мирно готовится ролик про математику Z80 без флагов. Вот прям совсем для таких как я. И вот предположим, в целом я действие флагов понимаю (уже успел почитать) но вот человек который первый раз пытается кодить на ассемблере, вы думаете он будет разбираться? Вспоминая себя лет 7 назад могу точно сказать что нет. Новичку нужно дать пару новых инструкций и как то заставить выучить их "на зубок", а потом уже расширять базу знаний. То бишь материал надо подать с минимально необходимым количеством новой информации - ровно столько, чтобы хватило понять следующий материал. А вы мне тут и про флаги, и про JP и вообще в дебри математики ушли. )) Я вам весьма признателен за помощь, даже не сомневайтесь, но сейчас её слишком много ) Флаги и условные/безусловные переходы это планируемая тема отдельного выпуска. А вот конкретно сейчас минимум - INC DEC ADD, SUB SBC (ну просто для 16-битного числа опкода SUB нет, насколько я понял). Отсюда и все сложности с регистрами и работой с калькулятором и процедурами ПЗУ. Это то, что запомнить можно ну вот прям в один присест. Если человек готов разбираться в этом, он, без сомнения, раскопает все, что вы сказали. Но если ковыряет только в порядке эксперимента, то все это лишнее. Главное стабильность работы.
Еще раз спасибо за помощь нубу, который делает ролики для еще больших нубов чем он сам )
Кстати есть подозрение что глючило как раз из-за пары команд LET x=USR 30000: RANDOMIZE x. Как то так.
Вот на это надо обратить внимание. Именно на данном этапе, пока нет понимания флагов. SBC не замена SUB! Если очень хочется SBC, как замену SUB, то перед ней нужно сбросить флаг переноса. Командой AND A или OR A или CP A.
LD DE,1
AND A ; сбрасываем перенос
SBC HL,DE ; отнимаем
бейсик компилятор думает так-же
a=x
b=y
c=x-y
в этом примере кстати хорошо видно почему применение всяких ЯВУ компиляторов даёт такой унылый результатКод:LD HL,(40074)
PUSH HL
LD HL,(40078)
PUSH HL
POP HL
POP DE
AND A
EX DE,HL
SBC HL,DE
LD (40082),HL
(лишние команды жрут память/такты)
Ну эт уже конкретно прогу надо смотреть. Именно в таком виде она неправильная: сперва надо записать значение командой RANDOMIZE, а уже потом запускать код командой USR.
Просто в Спектрум-Бейсике запуск кодов обычно идет конструкцией RANDOMIZE USR, отсюда и путаница. Первым может быть почти любой оператор, требующий параметры.
Но запускается машкод именно через USR - это функция, передающая через пару BC данные, причем в оба направления. На входе BC будет равно адресу старта кодов.
- - - Добавлено - - -
Логичней было бы объявлять тип переменных, как в Паскале или где еще, по типу "это целочисленный var до 65535 макс"
Что дало бы возможность привязать переменную к регистровой паре в большинстве случаев, и можно прямо в тексте.
Исходя из моих знаний о работе компиляторов Basic-кода и был сделан вывод о необходимости разбираться в ассемблере "ручками", а не полагаться на инструменты. И я весьма признателен местным гуру за терпеливую помощь и ускоряющие пинки в нужном направлении.
mcoder 1983
........
HiSoft ColtCompiler (от тех-же авторов, но 1985) сделал чуть более оптимальный код
Код:LD HL,(51569)
PUSH HL
LD HL,(51575)
POP DE
AND A
EX DE,HL
SBC HL,DE
Потому что забыли (поленились) сделать оптимизации кода, сокращение push/pop. Ради смеха прогнал в ZX Like Pascal:
Выдало:Код:program abc;
var
a,b,c,x,y: byte;
begin
a:=x;
b:=y;
c:=x-y;
end.
Если все переменные типа word (а не byte), то еще лучше:Код:ld a,(_X)
ld (_A),a
ld a,(_Y)
ld (_B),a
ld a,(_X)
ld l,a
ld h,0
ld a,(_Y)
ld e,a
ld d,0
and a
sbc hl,de
ld a,l
ld (_C),a
Код:ld hl,(_X)
ld (_A),hl
ld hl,(_Y)
ld (_B),hl
ld hl,(_X)
ld de,(_Y)
and a
sbc hl,de
ld (_C),hl
все равно весьма далеко от чисто ассемблерного кода. примерно так это будет в асме:
LD HL,X
LD BC,Y
PUSH HL
AND A
SBC HL,BC
EX DE,HL ;DE=C=X-Y
POP HL ; восстанавливаем X
если же нужно просто арифметическое действие без сохранения исходных значений, все еще проще.
goodboy и reddie, а где в ваших примерах присвоение a=x, b=y и c=x-y? Я вижу только взятие x и y и их вычитание
Посчитал очки или координаты спрайта и будешь пол-игры в регистрах хранить? Так регистров не напасёшься :)
- - - Добавлено - - -
это бы да. Только если выражения посложнее написать (к примеру, три операнда или скобки ввести), то уже куча частных случаев возникает. Всё не предусмотришь.
наверно речь о том что
(varA+1) это и будет адрес переменнойКод:varA ld hl,#xxxx
За сколько напишешь на голом ассемблере такую процедуру вывода окна с динамическим списком опций из разных массивов и выбора одной из них? И стОит ли оно столько времени? Я на Паскале за один день написал и отладил:
Вложение 77644Код:procedure window_dialog_sub1;
begin
case w_items[w_dialog_i,1] of
view_science_1: write(s_text[w_items[w_dialog_i,2]]);
view_civ_1: write(civ_text[w_items[w_dialog_i,2]]);
view_city_1: write(c_text[w_items[w_dialog_i,2]]);
view_unit_1: write(u_text[w_items[w_dialog_i,2]],' ',u[w_items[w_dialog_i,2],u_at],'/',
u[w_items[w_dialog_i,2],u_def],'/',u[w_items[w_dialog_i,2],u_speed]);
view_building_1: write(b_text[w_items[w_dialog_i,2]]);
view_tax_1:
begin
tax_view:=(w_items[w_dialog_i,2]-1)*10;
write(tax_view,'%');
end;
end;
end;
procedure window_clear_put;
{
w_dialog_center - окно по центру
w_dialog_x - x левого верхнего угла окна, если w_dialog_center=0
w_dialog_y - y левого верхнего угла окна, если w_dialog_center=0
w_dialog_w - ширина окна
w_dialog_h - высота окна
w_dialog_color1 - цвета рамки
w_dialog_color2 - цвета окна
}
begin
if w_dialog_center=1 then
begin
w_dialog_x:=(32-w_dialog_w)/2;
w_dialog_y:=(24-w_dialog_h)/2;
end;
color(w_dialog_color2);
WindowSet(w_dialog_x,w_dialog_y,w_dialog_w,w_dialog_h);
WindowClear;
for i2:=0 to w_dialog_w-1 do
begin
SpritePutClear(FRAME_H,w_dialog_color1,i2+w_dialog_x,w_dialog_y);
SpritePutClear(FRAME_H,w_dialog_color1,i2+w_dialog_x,w_dialog_y+w_dialog_h-1);
end;
for i2:=0 to w_dialog_h-1 do
begin
SpritePutClear(FRAME_V,w_dialog_color1,w_dialog_x,i2+w_dialog_y);
SpritePutClear(FRAME_V,w_dialog_color1,w_dialog_x+w_dialog_w-1,i2+w_dialog_y);
end;
SpritePutClear(FRAME_LU,w_dialog_color1,w_dialog_x,w_dialog_y);
SpritePutClear(FRAME_RU,w_dialog_color1,w_dialog_x+w_dialog_w-1,w_dialog_y);
SpritePutClear(FRAME_LD,w_dialog_color1,w_dialog_x,w_dialog_y+w_dialog_h-1);
SpritePutClear(FRAME_RD,w_dialog_color1,w_dialog_x+w_dialog_w-1,w_dialog_y+w_dialog_h-1);
end;
procedure window_dialog;
{
Параметры процедуры window_dialog:
вход:
w_dialog_center - окно по центру
w_dialog_x - x левого верхнего угла окна, если w_dialog_center=0
w_dialog_y - y левого верхнего угла окна, если w_dialog_center=0
w_dialog_w - ширина окна
w_dialog_h - высота окна
w_dialog_items - количество элементов
w_items[] - параметры элементов: номер таблицы, номер элемента
w_dialog_text1 - заголовок 1 окна
w_dialog_text2 - заголовок 2 окна
w_dialog_color1 - цвета рамки
w_dialog_color2 - цвета окна
w_dialog_color3 - цвета окна и элементов
w_dialog_color4 - цвета окна и курсора
w_dialog_cancel - возможность отказа от выбора (1=да/0=нет)
выход:
w_dialog_case - выбранный элемент (0, если отказ от выбора)
промежуточные:
w_dialog_i1 - первый видимый элемент
w_dialog_i2 - последний видимый элемент
w_dialog_cursor - вертик.координата курсора
}
begin
w_dialog_case:=1;
w_dialog_cursor:=1;
w_dialog_i1:=1;
if (w_dialog_h-4)>w_dialog_items
then w_dialog_i2:=w_dialog_items
else w_dialog_i2:=w_dialog_h-4;
window_clear_put;
color(w_dialog_color2);
gotoxy((w_dialog_x+1)*2,w_dialog_y+1);
writeln(w_dialog_text1);
gotoxy((w_dialog_x+1)*2,w_dialog_y+2);
writeln(w_dialog_text2);
repeat
color(w_dialog_color3);
WindowSet(w_dialog_x+1,w_dialog_y+3,w_dialog_w-2,w_dialog_h-4);
WindowClear;
for w_dialog_i:=w_dialog_i1 to w_dialog_i2 do
begin
gotoxy((w_dialog_x+1)*2,w_dialog_y+3+w_dialog_i-w_dialog_i1);
window_dialog_sub1;
end;
color(w_dialog_color4);
gotoxy((w_dialog_x+1)*2,w_dialog_y+2+w_dialog_cursor);
for w_dialog_i:=1 to (w_dialog_w-2)*2 do
write(' ');
gotoxy((w_dialog_x+1)*2,w_dialog_y+2+w_dialog_cursor);
w_dialog_i:=w_dialog_case;
window_dialog_sub1;
repeat
readkey(key1,key2,key3);
if key1=81 then
if w_dialog_case>1 then
begin
w_dialog_case:=w_dialog_case-1;
w_dialog_cursor:=w_dialog_cursor-1;
if w_dialog_case<w_dialog_i1 then
begin
w_dialog_i1:=w_dialog_i1-1;
w_dialog_i2:=w_dialog_i2-1;
w_dialog_cursor:=w_dialog_cursor+1;
end;
end;
if key1=65 then
if w_dialog_case<w_dialog_items then
begin
w_dialog_case:=w_dialog_case+1;
w_dialog_cursor:=w_dialog_cursor+1;
if w_dialog_case>w_dialog_i2 then
begin
w_dialog_i1:=w_dialog_i1+1;
w_dialog_i2:=w_dialog_i2+1;
w_dialog_cursor:=w_dialog_cursor-1;
end;
end;
if key1<>0 then delay(10);
until key1<>0;
if key1=32 and w_dialog_cancel=1 then w_dialog_case:=0;
until key1=13 or w_dialog_case=0;
color(0);
clrscr;
end;
а на голом и не будут писать такое, макросы + шитый или байт-код
То есть word по сравнению с byte использует более короткий код при математических операциях и присвоении результата, довольно неожиданно. Затраты на хранение переменных покрываются размером кода даже при одиночном использовании, при последующих использованиях X,Y уже не надо тратиться на word вместо byte, а код для word опять будет короче.
Эксперимент не прекращается. И вот с последней частью вышла запарка. Хотя я разобрался. Оказалось, что при вызове подпрограмм ПЗУ для вывода 16-битного значения на экран через стек калькулятора (11563 и 11747) они вычищают рег. пару BC. Ну и в итоге по возврату в бейсик мы видим в ней 0. А вот если их убрать, то все нормально. Всем спасибо за помощь, я еще зайду ). Просто для вас, возможно, такие действия со стороны процессора очевидны, а вот я в ступор минут на 20 впал )
Доброго времени суток. Снова вопрос нуба. Циклы на ассемблере организуются только через DJNZ? Ну я имею ввиду, что для организации цикла длиной более 255 итераций нужно создавать вложенные циклы? Или можно, к примеру, грузить в регистровую пару DE число итераций, затем грузить содержимое регистра D в аккумулятор и сравнивать его с регистром E. Пока сравнение ложно, флаг Z равен 0. И тогда JR NZ,nn снова запускаем выполнение блока кода? Или где-то я ошибся?
Обычно так и делают, только не сравнение, а OR - LD BC/DE/HL,NN: DEC Rx: LD A,R_high: OR R_low: JR NZ,REPEAT
Можно и без использования аккумулятора, но тогда значение нужно пересчитать вручную/средствами ассемблера, на выходе будет что-то типа
LD DE,NN
....
DEC E
JR NZ,REPEAT
DEC D
JR NZ,REPEAT
в общем, вложенный цикл, где счетчик рассчитан так, чтобы работал корректно, т.к. регистры уменьшаются независимо друг от друга
Так ещё можно:
О, вот тебе и раз - опоздал. Ну и ладно. :DКод:ld de, 1000 ; будем делать 1000 итераций
ld b,e ; подготовка регистров
dec de ; подготовка регистров
inc d ; подготовка регистров
ld hl,0 ; после выполнения цикла, в hl должно быть значение 1000
Loop:
inc hl
djnz Loop
dec d
jp nz,Loop
В общем решил так:
Если использовать CP C, то выполняется почему то 253 итерации. И все. А вот с OR пошло как надо. Всем спасибо )Цитата:
LD BC,nn ;количество итераций, 2 байта
LD HL,nn ;число с которым выполняются операции
LOOP
LD A,n ;константа в регистр
; Код программы
DEC BC
LD A,B
OR C
JR NZ,LOOP
А еще можно так =)) при условии, что итераций меньше 32768
LD DE,NN
....
DEC DE
BIT 7,D
JR Z,REPEAT
значение флага будет равно значению бита, т.е. когда бит 7=0 (число>=0 но меньше 32768) - сработает переход по Z
ну и счетчик надо заранее уменьшать на 1, поскольку при нулевом значении DE цикл все еще будет крутиться
да, нужно использовать именно OR - нулевой результат будет тогда, когда оба регистра равны нулю
LD A,n не имеет смысла, т.к. чуть ниже идёт LD A,B
Ну и попробуем сравнить:
или такКод:ld bc, 1000
loop:
dec bc ; 6t
ld a,b ; 4t
or c ; 4t
jr nz, loop ; 12t
; итого 26 тактов
или такКод:ld bc, 1000
loop:
djnz loop ; 13/8t
dec c ; 4t
jr nz, loop ; 12/7t
; итого 29 тактов (чуть меньше, т.к. последний виток быстрее переход)
и предложенное вышеКод:ld bc, 1000
loop:
dec c ; 4t
jr nz, loop ; 12/7t
dec b ; 4t
jr nz, loop ; 12/7t
; итого 32 такта (чуть меньше, т.к. последний виток быстрее переход)
Код:ld de, 1000
loop:
dec de ; 6t
bit 7,d ; 8t
jr z, loop ; 12t
; итого 26 тактов
Первый вариант быстрее, и чуть короче, но использует регистр А.
Третий вариант медленнее второго, но зато не привязан к B, можно использовать два любых регистра.
Четвёртый не привязан к А, но имеет ограничение на 32765 значений.
Что лучше - выбирать нужно по конкретной задаче.
Очень даже имеет, так как в начале программы используется константа которая не меняется. Собственно в моей программе туда грузим вполне себе конкретный байт цветности, а потом выгружаем его в ячейку памяти. Вообще, поскольку константа не меняется, можно было бы её напрямую грузить в вычисляемую ячейку памяти, но программа была уже написана и работала, так что... :))Код:LD A,n не имеет смысла, т.к. чуть ниже идёт LD A,B
Но все равно благодарен всем присутствующим за помощь. Она потом используется. Возможно даже по назначению. Только OBS Надо настроить нормально, а то в последний раз кошмар что было. Но удалять уже жаль.
Ну да, я в среднем и приблизительно написал. Если мы знаем сколько конкретно циклов будет, то можем конкретно посчитать такты.
Например 50 циклов. Считаем. 49 раз djnz завернёт за 13 тактов. Плюс один раз djnz 8t, dec 4t и jr 7t.Цитата:
Код:ld bc, 1000
loop:
djnz loop ; 13/8t
dec c ; 4t
jr nz, loop ; 12/7t
; итого 29 тактов (чуть меньше, т.к. последний виток быстрее переход)
49*13+8+4+7=50*13-13+8+4+7=650+6=656
За цикл будет 656/50=1312/100=13,12
Всё равно 13 с мелочью. Но не 29.