Просмотр полной версии : ЭТЮДЫ
Извините, что прерывайю вашу ̶и̶с̶т̶ риторическую беседку, но объясните мне, чяднт?
вот код, для Pent 512\1024 (Unreal) :
device zxspectrum128
;--------------------------------
ORG #6200
Start:
ld bc,#EFF7 ;pentagon on
ld a,%00001000 ;D3 1 = slot 0 page0
out (c),a
ld hl, screens
ld de,#0000
ld bc,6912
ldir
ld hl, #0000
ld de,16384
ld bc,6912
ldir
ld bc,#EFF7 ;pentagon off
ld a,%00000000 ;D3 0 = slot 0 ROM
out (c),a
jp $
org #8000
screens: incbin "pent.scr"
savesna "test_7ffd.sna" ,Start
savetap "test_7ffd.tap" ,Start
Код как код, для Ужасм-а, если точнее, но не суть же??
Вобщем .SNA работает так, как задумал аффтор, а TAP-ка соответственно не работает :(
Я так себе понимаю, что ужасм должен в обоих случАях генерить валидный код??
Что не так?? чиво добавить\убрать, чтобы и тап-ка работала как задумано, или это я много хочу от трех строчек?
zebest, а ты вообще что хочешь от этой программы?
- - - Добавлено - - -
У тебя прерывания разрешены в момент
LDIR
а ты вообще что хочешь
Вообще-то порт #EFF7 отладить хотел на своей железке, и нужен был простейший тест. Но хотелось бы тап-ку. Но это вторично. Первично хочу понять - почему SNA вариант работает правильно, а ТАР - нет.
В SNA так же прерывания не запрещены. Или? Или тап-ка в куда то себе заносит в кэш адрес запуска, а потом успешно все теряет??
zebest,
после запуска СНА у тебя прерывания ЗАПРЕШЕНЫ
после загрузки ТАП у тебя прерывания РАЗРЕШЕНЫ
потому в определенный момент твоя программа оказывается по адресу #00ff где у тебя уже частично твой экран и ВСЁ!
поставь в эмуляторе на #6200 брякпойнт и посмотри что у тебя там и как.
- - - Добавлено - - -
поставь первой строкой DI и все станет хорошо
после запуска СНА у тебя прерывания ЗАПРЕШЕНЫ
после загрузки ТАП у тебя прерывания РАЗРЕШЕНЫ
ну не знал я таких ньюансов, не знал
поставь первой строкой DI и все станет хорошо
Вот это и спрашивал. Ну и какая разница между tap и sna/
Спасибо!!
ах да, а что, при LDIR надо обязательно запрещать прерывания, или желательно?? При прерывании сё сбиваеЦЦа?
zebest, если у тебя обработчик прерывания есть то ничего запрещать не надо
если у тебя обработчика прерывания нет (как в данном случае, ты отключаешь ПЗУ где по адресу #0038 находится штатный обработчик) то лучше запретить.
Можно ли переделать эту прогу так, чтоб она видела одновременно три нажатые клавиши?
KEY_1 EQU 0FEF7H
KEY_2 EQU 0FDF7H
KEY_3 EQU 0FBF7H
KEY_4 EQU 0F7F7H
KEY_5 EQU 0EFF7H
KEY_0 EQU 0FEEFH
KEY_9 EQU 0FDEFH
KEY_8 EQU 0FBEFH
KEY_7 EQU 0F7EFH
KEY_6 EQU 0EFEFH
KEY_Q EQU 0FEFBH
KEY_W EQU 0FDFBH
KEY_E EQU 0FBFBH
KEY_R EQU 0F7FBH
KEY_T EQU 0EFFBH
KEY_P EQU 0FEDFH
KEY_O EQU 0FDDFH
KEY_I EQU 0FBDFH
KEY_U EQU 0F7DFH
KEY_Y EQU 0EFDFH
KEY_A EQU 0FEFDH
KEY_S EQU 0FDFDH
KEY_D EQU 0FBFDH
KEY_F EQU 0F7FDH
KEY_G EQU 0EFFDH
KEY_EN EQU 0FEBFH
KEY_L EQU 0FDBFH
KEY_K EQU 0FBBFH
KEY_J EQU 0F7BFH
KEY_H EQU 0EFBFH
KEY_CS EQU 0FEFEH
KEY_Z EQU 0FDFEH
KEY_X EQU 0FBFEH
KEY_C EQU 0F7FEH
KEY_V EQU 0EFFEH
KEY_SP EQU 0FE7FH
KEY_SS EQU 0FD7FH
KEY_M EQU 0FB7FH
KEY_N EQU 0F77FH
KEY_B EQU 0EF7FH
KEYTEST
pop hl
inc hl
ld a,(hl)
inc hl
in a,(0feh)
or (hl)
inc hl
inc a
RLCA
AND 1
ld (_KEYPRESSED),a
jp (hl)
_KEYPRESSED defw 0
Можно ли переделать эту прогу так, чтоб она видела одновременно три нажатые клавиши?
Что-то оно не работает...
Дружище, ты конечно известен своими панковскими манерами, но вроде есть более цивилизованные способы опрашивать клавиатуру, к тому-же учитывая задачи (например опрос клавы в игре - совсем не то-же самое что в текстовом редакторе например...)
Dart Alver
20.06.2020, 21:26
Можно ли переделать эту прогу так, чтоб она видела одновременно три нажатые клавиши?
Код по мне обрезок какой-то невнятной дичи :v2_dizzy_facepalm:
Конкретизируйте задачу. Нужно 3 произвольных клавиши, 3 определённых клавиши, 3 сигнатуры нажатых клавиш ?
NEO SPECTRUMAN
20.06.2020, 23:40
in a,(0feh)
0FEh-еть какое извращение :)
Да, надо опросить одновременно три клавиши.
A+K+J
И вторая комбинация
D+K+J
А эта прога не детектит, если A (D) +K нажаты, то нажатие J игнорит.
Представьте, как в кастловании, пришлось бы нажимать прыжок, резко его отпускать, чтоб воспользоваться хлыстом в прыжке. Или в контре во время прыжка, отпускать кнопку отвечающую за прыжок, чтоб воспользоваться кнопкой стрельбы.
А эта прога не детектит, если A (D) +K нажаты, то нажатие J игнорит.
На реале всегда так, если зажать некоторые комбинации клавиш - будет читатся что зажат например весь полуряд.
Это из-за буферизации что-ли (или её отсутствия).
Электронщики объясняли как-то почему такой баг и даже как-то пытались фиксить.
Но как всегда говорили непонятно простому смерду, так что лезь в раздел "Железо" и там где про клавиатуры - поройся или запости вопрос.
Dart Alver
21.06.2020, 15:54
На реале всегда так, если зажать некоторые комбинации клавиш - будет читатся что зажат например весь полуряд.
Это из-за буферизации что-ли (или её отсутствия).
Электронщики объясняли как-то почему такой баг и даже как-то пытались фиксить.
SpeccyWiki:
Аппаратно кнопки клавиатуры представляют собой матрицу восемь на пять линий — восемь полурядов по пять кнопок. В зависимости от адреса, использованного при обращении к порту клавиатуры, в младших пяти битах возвращается состояние клавиш одного из восьми полурядов. Такая схема создаёт "эффект матрицы" (matrix effect) — при нажатии двух клавиш (одного полуряда) одновременно с третьей клавишей (в одном столбце с первой) возникает паразитное нажатие в полуряду третьей клавиши, соответствующее столбцу второй. Этот эффект можно ликвидировать размещением диода на каждой клавише.
Да, надо опросить одновременно три клавиши.
A+K+J
И вторая комбинация
D+K+J
А эта прога не детектит, если A (D) +K нажаты, то нажатие J игнорит.
Понять как работает прога по приведённому огрызку вообще нереально. Я удивлён что вообще как-то работает ))
Прежде всего неясно почему таблица берётся со стека, но это ладно может там всё продумано, а дальше ...
По (hl) вначале читается старший байт порта, затем читается порт, а следующий байт по (hl) инверсное выделение бита клавиатуры. Затем вы кидаете в (_KEYPRESSED) : 0 - не нажата, 1 - нажата, и уходите на адрес обработки который тоже берётся из таблицы.
Что и как у вас прописано по таблице фиг его знает. Показанный элемент может работать корректно только с одной кнопкой, как вы обрабатываете несколько этим куском кода не описывается ))
- - - Добавлено - - -
А пардон ступил, не из таблицы, у вас вызов проги с указанием за вызовом старшего байта порта и выделения бита )))
Не понятно только почему используется паразитный inc hl за съёмом адреса со стека
Но то же самое, это только для одной клавиши
- - - Добавлено - - -
Наваял на вскидку, за отсутствие ляпов не поручусь ))
KEYTEST3 pop hl ; проверка 3-х портов клавиатуры;
call KEYTEST_
jr z,1F
inc hl
inc hl
jr 2F
KEYTEST2 pop hl ; проверка 2-х портов клавиатуры
1 call KEYTEST_
jr z,1F
2 inc hl
inc hl
jr 2F
KEYTEST pop hl ; проверка порта клавиатуры
1 call KEYTEST_
2 ld (_KEYPRESSED),a
jp (hl)
KEYTEST_
ld a,(hl) ; старший байт порта
inc hl
in a,(#FE)
or #E0
cp (hl) ; проверка нажатия одной или нескольких клавиш в одном полуряде
inc hl
ld a,1
ret z
xor a
ret
_KEYPRESSED defw 0
Применение:
call KEYTEST3
DB #XX , # YY ; #XX - старший байт адреса порта, #YY - код одной или нескольких клавиш в полуряде ( бит 0 - клавиша нажата бит 1 -нет )
DB #XX , # YY
DB #XX , # YY
; дальнейшие инструкции
call KEYTEST2
DB #XX , # YY
DB #XX , # YY
; дальнейшие инструкции
call KEYTEST
DB #XX , # YY
; дальнейшие инструкции
Для вашего случая KEYTEST3 не нужен, у вас две клавиши в одном полуряде, только соответственный параметр нужен.
Да ещё момент, при опросе не стоит в старшем байте порта использовать больше одного нуля. Есть контроллеры PC клавы такие (по крайней мере у меня был) которые понимают либо 0 во всех разрядах сразу, либо только в одном. Поэтому у меня на профике некоторые игры не слушались управления ))
mloop
ld bc,49150 ; keyboard row H, J, K, L, Enter
in a,(c) ; see what keys are pressed.
and 31
ld (storage),a
ld bc,65022 ; keyboard row G, F, D, S, A
in a,(c) ; see what keys are pressed.
and 31
ld (storage+1),a
...
jp mloop
storage
defb 1,2
чисто три строчки, для примера. В цикле сканируем полурядЫ и результат в буфер заносим, а хоть все 40 кнопок. Потом парсим буфер, че туда упало. Не ?
NEO SPECTRUMAN
21.06.2020, 17:31
Потом парсим буфер, че туда упало. Не ?
для сипаскальщиков это разрыв шаблона
не понимаю что мешает открыть
https://worldofspectrum.org/faq/reference/48kreference.htm
и делать тупое in, and, jp z
не понимаю что мешает ... делать тупое in, and, jp z
Кстати да, в конце-концов если от пп не требуется особой универсальности то достаточно:
LD A,#FD
IN A,(#FE)
AND 1 ; A
RET NZ
LD A,#BF
IN A,(#FE)
AND %1100 ; K и J
RET
На выходе - флаг Z будет установлен если нажаты AKJ
shurik-ua
21.06.2020, 19:05
asm rulezz )
NEO SPECTRUMAN
22.06.2020, 00:28
asm rulezz )
именно так
NEO SPECTRUMAN
04.07.2020, 15:21
Йпн"№К№"Кй пипец
нашел у себя ошибку
которая успешно скопипастилась раз 30
если не больше по одноподобному коду
которая выглядела вот так
and $00000010
вместо
and %00000010
ИЧСХ изза каких то уродов которые по привыкали писать цифры в виде 0х000000000000000000000000000000001
компилятору совершенно пофиг что в 1 байт пытаются засунуть 4 :v2_dizzy_facepalm:
и он молча молчит
а код не работает...
а нашлось чисто случайно
не совсем этюды конечно...
но как напоминание возможных граблей вполне может кому нить пригодитсо
писать цифры в виде 0х000000000000000000000000000000001
Эти ноги растут из Си вроде как. Постоянно натыкаюсь, даже в более-менее внятных сорцах.
Время было такое, все программеры волей-неволей были такими уродами, только не совсем ясно откуда такой синтаксис взялся, это (как и всё на сях) обусловлено какими-то дремучими аппаратными фичами, которые давным-давно уже не используются (а в то время казалось что навечно и во веки веков будут основой, ну как например двоичная система - давно уже 1 ячейка памяти может хранить не только 0 или 1, а до четырёх состояний, в зависимости от заряда на затворе полевика, но всё равно всё это преобразуется сперва в двоичный вид и уж потом по возрастающей - вплоть до чисел с плавающей точкой...)
Намедни возникла нужда сделать вывод большого (более 65535) числа в десятичном виде, в Килобайтах и с одним знаком после запятой.
Исходное число хранится в трёх байтах, т.о. оно может принимать значения от 0 до 16777216.
Соответственно, при выводе требуется "разбить" исходное число как бы на две составляющие: целую часть и остаток.
Тут всё просто, целая часть это старшие биты начиная с десятого и выше, а остаток это десять младших бит.
Собственно задачка - преобразовать число остатка (0..1023) в соответствующие значения десятых (0..9).
Тобишь значение 512 должно нам давать ",5" и т.д.
Чисто математически решение сводится к делению значения остатка на константу 102,4.
Для гуру это конечно же "не вопрос", а я традиционно люблю изобретать велосипед, поэтому на решение был убит очень нескучный вечер :)
Но результатом я дико доволен, в связи с чем решил поделиться.
Ранее у меня был исследован вопрос быстрого деления на 10, 100, 1000, и в хозяйстве были заготовки такого кода,
который я и стал пытаться приспособить для решения данной задачи. Вроде бы 102 с хвостиком и 100 - это примерно одно и то же...
Но не тут-то было, ошибка набегала столь фатальная, что такой вариант оказался абсолютно неприемлем.
Были попытки предварительно "чуть подкрутить" делимое (а также результат), чтобы хотя бы явные значения типа 512->0,5 были некривые. Но в итоге получалось громоздко и некрасиво.
В какой-то момент уже даже хотел сдаться, плюнуть, и сделать втупую сравнениями с переходными значениями и выдачей константы...
Но вдруг осенило! Давно не получал такого удовлетворения от неожиданно найденного решения, если честно.
Суть пока не рассказываю, просто готовый код ;)
; в [HL] остаток (0..1023)
MOV D,H
MOV E,L
DAD H
DAD H
DAD H
DAD D
DAD D
MOV A,H
RRC
RRC
ANI 0Fh
; в [A] точное значение десятых (0..9)
П.С. может кому-то даже пригодится)
; в [HL] остаток (0..1023)
MOV D,H
MOV E,L
DAD H
DAD H
DAD D
MOV A,H
RAR
; в [A] точное значение десятых (0..9)
Суть пока не рассказываю, просто готовый код
[точное значение десятых] = [остаток] * 10 / 1024
либо:
[точное значение десятых] = [остаток] * 5 / 512
Надо-же, нашёл в загашниках (ещё на дискете, на ZEUS писано было, потом уже под sjasm адаптировал)
; Проявлялка экрана
ORG #8000
LD SP,#8000
DI
XOR A:OUT (#FE),A
CALL WAITKEY
CALL O_INIT
START EI:HALT:DI
CALL POPOLZ
XOR A:OUT (#FE),A
LD HL,TAKT:INC (HL)
JR START
POPOLZ ; 6883
EXX:LD H,#5A,BC,#0358,DE,32:EXX
XOR A
POP2 LD E,A
EXA
LD D,0,HL,O_BUFF
ADD HL,DE
LD D,(HL)
LD A,E:ADD A,#C0:LD L,A,H,#57
LD A,(TAKT),C,A
ADD A,D:LD C,A
LD A,24:SUB C:JR C,POP0,Z,POP0
LD B,A
LD C,8
POP1 LD A,L:ADD A,32:LD E,A:SBC A,A:AND C:ADD A,H:LD D,A
LD A,L:EXX:LD L,A:EXX
LD A,H:EXX:RRCA:RRCA:RRCA:AND B:OR C:LD H,A
LD A,(HL),(HL),0
ADD HL,DE
LD (HL),A
EXX
REPT 8
LD A,(HL),(DE),A:DEC H,D
ENDR
LD A,L:ADD A,-32:LD L,A:SBC A,A:AND C:ADD A,H:LD H,A
DJNZ POP1
XOR A
REPT 8
INC D:LD (DE),A
ENDR
POP0 EXA
INC A:CP 32:JR C,POP2
RET
RND_BYTE
LD A,53
ADD A,A
JR NC,$+4
XOR 57
LD (RND_BYTE+1),A
RET
TAKT DB 0
O_BUFF DS 32
O_INIT LD A,R:ADD A,50:LD (RND_BYTE+1),A
LD HL,O_BUFF,B,32
O_I1 CALL RND_BYTE
AND 7:NEG
DEC HL:CP (HL):INC HL:JR Z,O_I1
LD (HL),A
INC HL,DE
DJNZ O_I1
RET
WAITKEY XOR A:IN A,(#FE):CPL:AND #1F:JR Z,WAITKEY
WK XOR A:IN A,(#FE):CPL:AND #1F:JR NZ,WK
RET
ORG #4000:INCBIN "1.scr";,0,#1800
Это в молодости когда DOOM-2 увидел (первого вообще в наших краях никто в глаза не видал) - захотелось на спеке такой-же FADE-OUT - кривенько, но ностальжи (эх какой там год - уже не вспомнить, что-то дремучее 9х )
:)
Я бы сказал - очистка экрана. А так да, работает
Конечно очистка!
Я просто оставил комменты те, старые!
(вообще-то там должно было не чистый скрин оставлять, а новый типа, ну как в DOOM)
Граждане, а киньте, кому не жалко, каких-то процедурок печати 42 символов. Можно и 64, но, в первую очередь, 42 интересует.
Oleg N. Cher
01.09.2021, 22:35
https://github.com/Oleg-N-Cher/XDev/blob/master/ZXDev/Lib/C/Out6x8.c
Не везде затирает фон под выводимыми буквами, нужно предварительно чистить какой-то другой процедурой.
В комплекте процедура генерации шрифта 6x8 из ПЗУшного.
На картинке кастомный шрифт.
MODULE TestOut6x8; (*$MAIN*)(*$866*)
IMPORT SYSTEM, C := Out6x8, GrFonts, B := Basic;
VAR
n: INT8;
BEGIN
B.Init; B.CLS;
C.font := SYSTEM.ADR(GrFonts.HalfSpaceRus6x8);
C.At(0, 23); n := 1; REPEAT C.Ch(CHR(n)); INC(n) UNTIL n = -3;
FOR n := 0 TO 16 DO C.At(n, n); C.Str("Привет, Савелий !!!") END;
B.PAUSE(0);
B.Quit
END TestOut6x8.
http://i.piccy_.info/i9/cd0e626aac7296b6d926053d06630980/1630524863/97716/1320134/6x8.png
Граждане, а киньте, кому не жалко, каких-то процедурок печати 42 символов. Можно и 64, но, в первую очередь, 42 интересует.
http://zxpress.ru/article.php?id=12845
http://zxpress.ru/article.php?id=4800
Видел я это...
Что такое, например, "LD В,'FONT42"? Видел в жизни всего два ассемблера - GENS и Sjasm. что за 'FONT42, который грузится в 8-бит регистр, не пойму.
Старший байт адреса шрифта.
0xDEAD, как вариант...
S42_PRN LD L,A
LD H,FONT42
LD DE,(SCP_ADR)
PRN_MOV LD A,0
INC A
AND 3
LD (PRN_MOV+1),A
AND A
JP Z,S42_1
DEC A
JP Z,S42_2
DEC A
JP Z,S42_3
S42_4 PUSH HL
PUSH DE
DUP 7
LD A,(DE)
LD C,(HL)
SRA C
SRA C
OR C
LD (DE),A
INC D
INC H
EDUP
LD A,(DE)
LD C,(HL)
SRA C
SRA C
OR C
LD (DE),A
POP DE
POP HL
INC E
DUP 7
LD A,(HL)
DUP 6
ADD A,A
EDUP
LD (DE),A
INC D
INC H
EDUP
LD A,(HL)
DUP 6
ADD A,A
EDUP
LD (DE),A
LD HL,SCP_ADR
INC (HL)
RET
S42_1 DUP 7
LD A,(DE)
OR (HL)
LD (DE),A
INC D
INC H
EDUP
LD A,(DE)
OR (HL)
LD (DE),A
LD HL,SCP_ADR
INC (HL)
RET
S42_2 DUP 7
LD A,(HL)
ADD A,A
ADD A,A
LD (DE),A
INC D
INC H
EDUP
LD A,(HL)
ADD A,A
ADD A,A
LD (DE),A
RET
S42_3 PUSH HL
PUSH DE
DUP 7
LD A,(DE)
LD C,(HL)
DUP 4
SRA C
EDUP
OR C
LD (DE),A
INC D
INC H
EDUP
LD A,(DE)
LD C,(HL)
DUP 4
SRA C
EDUP
OR C
LD (DE),A
POP DE
POP HL
INC E
DUP 7
LD A,(HL)
DUP 4
ADD A,A
EDUP
LD (DE),A
INC D
INC H
EDUP
LD A,(HL)
DUP 4
ADD A,A
EDUP
LD (DE),A
LD HL,SCP_ADR
INC (HL)
RET
Обрати внимание, что поскольку шрифт содержит 256 символов и занимает 2048 байт, то мы слегка меняем его формат чтобы выкинуть умножение при расчёте адреса символа. т.е. в шрифте идут начала первые строки всех 256 символов, затем вторые, итд... переход на следующую строку символа делается просто INC H :) Переменная SCP_ADR это адрес на экране.
0xDEAD, как вариант...
PRN_MOV LD A,0
INC A
AND 3
LD (PRN_MOV+1),A
AND A - лишняя команда
JP Z,S42_1
DEC A
JP Z,S42_2
DEC A
JP Z,S42_3
!
можно сократить еще на два байта =)
PRN_MOV LD A,#88
RLCA
LD (PRN_MOV+1),A
JP C,S42_1
RLCA
JP C,S42_2
RLCA
JP C,S42_3
Т.к. счетчик в аккуме кратен четырем - достаточно циклической прокрутки байта и проверки флага C. Таким же образом можно делать счетчики на 2 и 8.
Ну и, если хватает расстояния, можно позаменять JP на JR.
Спасибо, но уже недели две как сам написал. :v2_dizzy_roll: Даже практически то же самое получилось.
Посмотрю, конечно.
Почему add a, a, а не rla/rlca? По таймингам одинаково.
Почему add a, a, а не rla/rlca? По таймингам одинаково.
В данном случае без разницы, но вообще rlca не трогает флаги кроме С, а add наоборот - выставляет все флаги как положено.
Если на них нужно реагировать после сдвига то add, а если сохранить флаги "как было" то rlca
Если поменять функции HL и DE, то вместо
LD С,(DE)
~ROLL С
LD A,(HL)
OR C
LD (HL),A
можно писать
LD A,(DE)
~ROLL A
OR (HL)
LD (HL),A
Так чуть короче, чуть быстрее, и регистр BC полностью свободен.
Когда литеру шлёпаем в два знакоместа, то можно первое знакоместо проходить вниз, а второе вверх. Убираются PUSH POP, и сразу получаем адрес для следующего символа.
Шесть сдвигов можно заменить двумя циклическими сдвигами в другую сторону с одним AND.
Если размер важнее скорости, то можно свернуть все DUP-ы. :)
А если скорость важнее размера, то можно как нибудь свести эти четыре варианта печати, чтобы они друг друга по очереди вызывали. Или если печатать не символ, а сразу строку, то вообще шли друг за другом по кольцу.
Или может быть печатали сразу с двух литер в одно знакоместо.
Или даже имели сразу сдвинутые шрифты.
Когда литеру шлёпаем в два знакоместа, то можно первое знакоместо проходить вниз, а второе вверх.
О! И как бы просто, и попробуй догадайся.
Или если печатать не символ, а сразу строку, то вообще шли друг за другом по кольцу
Так я и сделал, собственно.
Или может быть печатали сразу с двух литер в одно знакоместо.
Если вторая литера будет символом конца строки или CRLF, то неудобно получится.
Собственно, что у меня получилось:
@FONT equ #d000
OUT43
call coords.BYTE_RC ; вычислили адрес в экранной памяти по координатам знакоместа
ex de, hl
push de
PRINT
ld a, (bc)
cp "\N" ; перевод строки?
jr NZ, .noCRLF1
pop hl
inc h
inc bc ; take next symbol after \N
jp OUT43
.noCRLF1 or a ; EOL?
jp Z, exitSub
push hl
.fnt1 ld d, FONT/256
ld e, a
;================================================= =================================
; 1st char
DUP 8
ld a, (de)
ld (hl), a
inc h
inc d
EDUP
pop hl
inc bc
;================================================= =================================
; 2nd char
ld a, (bc)
cp "\N"
jr NZ, .noCRLF2
pop hl
inc h
inc bc
jp OUT43
.noCRLF2 or a
jp Z, exitSub
push hl
.fnt2 ld d, FONT/256
ld e, a
DUP 8
ld a, (de)
rlca
rlca
push af
and %00000011
or (hl)
ld (hl), a
pop af
and %11110000
inc hl
ld (hl), a
dec hl
inc h
inc d
EDUP
pop hl
inc bc
;================================================= =================================
; 3rd char
ld a, (bc)
cp "\N"
jr NZ, .noCRLF3
pop hl
inc h
inc bc
jp OUT43
.noCRLF3 or a
jp Z, exitSub
inc hl
push hl
.fnt3 ld d, FONT/256
ld e, a
DUP 8
ld a, (de)
DUP 4
rlca
EDUP
push af
and %00001111
or (hl)
ld (hl), a
pop af
and %11000000
inc hl
ld (hl), a
dec hl
inc h
inc d
EDUP
pop hl
inc bc
;================================================= =================================
; last (4th) char
ld a, (bc)
cp "\N"
jr NZ, .noCRLF4
pop hl
inc h
inc bc
jp OUT43
.noCRLF4 or a
jp Z, exitSub
inc hl
push hl
.fnt4 ld d, FONT/256
ld e, a
DUP 8
ld a, (de)
rrca
rrca
and %00111111
or (hl)
ld (hl), a
inc h
inc d
EDUP
pop hl
inc bc
inc hl
jp PRINT
exitSub
pop hl
ret
Font1251 incbin "CP1251_42_B_converted.fnt",,2048
На входе в HL координаты знакоместа, в BC - адрес первого символа строки.
Пока ещё в состоянии proof of concept, надо оптимизировать, но печатает же ж. И явно быстрее того жуткого ужаса, который я написал в 1996 году, который, ну, в принципе, тоже ведь печатал... :v2_lol:
Взываю к коллективному разуму.
Постановка задачи следующая:
Необходимо трехбайтное число (#000000..#7FFFFF) умножить на дробь формата #FFnn/#10000
старший байт числителя дроби всегда равен #FF
Допустима небольшая погрешность при вычислении.
Основной критерий скорость вычисления, но размер п/п не должен превышать примерно килобайта. Доступна таблица квадратов.
SAM style
16.12.2021, 11:58
Код писать сейчас некогда, но вот идея: умножить исходное на ~#FFnn (aka #00xx), целую часть результата умножения - старшие 2 байта - вычесть из исходного.
Да код и не нужен.
О таком преобразовании я что-то и не подумал.
цель задачи
(Number)=(Number)-(Number)*c/#10000
;трехбайтное число
Number
low db 0
med db 0
high db 0
;определение множителя
ld hl,Tabl
and #7F
jr z,.gtm01
add a,l
ld l,a
ld c,(hl)
;c - множитель
;high
ld a,(high)
and #7F
ld b,a
; умножение hl=c*b
sub c
jr nc,.gtm02
ld a,c
sub b
.gtm02 rra
ld h,TableSqr/#100
ld l,a
ld e,(hl)
inc h
ld d,(hl)
ld a,b
add a,c
rra
ld l,a
ld a,(hl)
dec h
ld l,(hl)
ld h,a
and a
sbc hl,de
;med
push hl
ld a,(med)
; умножение hl=a*c
ld b,a
sub c
jr nc,.gtm03
ld a,c
sub b
.gtm03 rra
ld h,TableSqr/#100
ld l,a
ld a,b
add a,c
rra
ld e,(hl)
inc h
ld d,(hl)
ld l,a
ld a,(hl)
dec h
ld l,(hl)
ld h,a
and a
sbc hl,de
pop de
ld a,l
ld l,h
ld h,#00
;hla = med*c
;de = high*c
;сложим
add hl,de
ld b,a
ex de,hl
;low
ld a,(low)
ld h,a
sub c
jr nc,.gtm04
ld a,c
sub h
.gtm04 rra
ld l,a
ld a,h
add a,c
rra
ld h,TableSqr/#100+1
ld c,(hl)
ld l,a
ld a,(hl)
sub c
add a,b
jr nc,.gtm05
inc de
.gtm05 ld hl,(low)
ld a,(high)
or a
sbc hl,de
sbc a,0
ld (low),hl
ld (high),a
.gtm01
оптимизацию по скорости может кто предложить?
выровнять Tabl на параграф не предлагать
Вместо длинного вычисление множителя через таблицу, не подойдёт-ли чтонибудь простое, вроде CPL или NEG?
Умножать MED однобайтово, а LOW вообще не умножать.
- - - Добавлено - - -
(Number)=(Number)-(Number)*c/#10000
изменить так:
(Number)=(Number)-(Number)*#100/#10000+(Number)*nn/#10000
то есть вернуться обратно к nn
Вместо длинного вычисление множителя через таблицу, не подойдёт-ли чтонибудь простое, вроде CPL или NEG?
не вариант.
на входе A=[+/0..#F]
таблица #00,#01,#03,#05,#09,#0D,#12,#19,#20,#29,#33,#3D,#4 9,#55,#63,#71
Умножать MED однобайтово, а LOW вообще не умножать.
погрешность слишком большая будет набегать
погрешность слишком большая будет набегать
А погрешность проанализировать, возможно её можно как-то по простому компенсировать.
- - - Добавлено - - -
таблица #00,#01,#03,#05,#09,#0D,#12,#19,#20,#29,#33,#3D,#4 9,#55,#63,#71
Да это же практически (x^2+1)>>1. Таблица квадратов есть. Может можно как нибудь приспособить?
Вы полагаете расчет, даже с использование таблицы квадратов, будет быстрее табличной выборки?
Да, предполагал, что если TableSqr выравнена, а Tabl не выровнена, то такое возможно. Но раз нет, то нет.
Хотя вариант имеет право на жизнь
Есть такие замечательные команды как adc и sbc - используют флаг CY
Например типовая задача уменьшить значение аккумулятора не менее какого то (например 23).
обычно делают:
dec a
cp 23
jr nc,$+3
inc a
а можно вот так аккуратно:
sub 23
adc a,22
работает много элегантнее и что очень важно имеет всегда одну и ту же длительность (что особенно интересно демописателям где каждый раз приходиться пересчитывать такты).
Или обратная задача - увеличить значение но не более какого то (например 75)
В этом случае делаем так:
add a,-75
sbc a,-1-75
В этом случае выше 75 ну никак не поднимется с теми же преимуществами.
Прошло всего-то 14 лет - а кто-то проверял, как это работает? Я проверил (понадобилось). Немного не так работает, как заявлено.
ld a, 23
sub 23
adc a, 22
оставляет в А значение 22, хотя должно быть 23.
То есть, формула должна быть:
sub x+1
adc a, x
а не
sub x
adc a, x-1
К этому:
add a,-75
sbc a,-1-75
претензий нет.
sub x+1
adc a, x
При этом
1. Если исходно Aисх>x, то на выходе A=Aисх-1
2. Если исходно x>=Aисх>=0, то на выходе A=Aисх
Так и я о чём. В этом и был смысл поста 14-летней давности, но оно так не работает, как написано.
К этому:
Код:
add a,-75
sbc a,-1-75
претензий нет.
Тут похожая ситуация:
1. Если исходно Aисх<x (в примере x=75), то на выходе A=Aисх+1
2. Если исходно FF>=Aисх>=x, то на выходе A=Aисх
- - - Добавлено - - -
оно так не работает, как написано.
Автор того поста не упомянул один момент - чтобы эти конструкции работали, надо чтобы исходное значение было "корректным" (>=x в случае декремента и <=x в случае инкремента).
Я понимаю.
Описанный алгоритм с декрементом работает не так, как написано. В приведённом примере, аккумулятор уменьшается до 22, хотя, как написано, он не должен уменьшаться меньше 23. Инкремент работает правильно, к нему претензий нет.
Инкремент работает правильно, к нему претензий нет.
У инкремента тоже есть "мертвая зона", если он правильный, то и декремент тоже.
- - - Добавлено - - -
Если претензия к исходному описанию не в "мертвой зоне", а в не совсем правильном описании порога - могу только согласиться.
ld a, 30
err sub 23
adc a, 22
jp err
После 23446557465 итерации оставляет в аккумуляторе 22.
ld a, 30
err sub 24
adc a, 23
jp err
После 23446557465 итерации оставляет в аккумуляторе 23.
а что мешает сперва проверять, потом делать инкремент/декремент?
CHECK_DN CP 23+1 ; A=23 min
JR C,$+3
DEC A
PROFIT ... ; уменьшаем до 23
CHECK_UP CP 55 ; A=55 max
ADC A,0
PROFIT ... ; увеличиваем до 55
Весь смысл описанного кода как раз в отсутствии проверок и ветвлений.
Весь смысл описанного кода как раз в отсутствии проверок и ветвлений
Ну, в случае увеличения в моем варианте ветвлений нету, достаточно проверки. И по тактам, если кому критично, без расхождения.
С уменьшением простого решения нет. Либо раздувать код, либо ветвление.
Была бы у Z80 проверка сразу нескольких флагов, как на PDP - тогда, думаю, задача бы решилась.
а что мешает сперва проверять, потом делать инкремент/декремент?
Чем эти варианты лучше? "Мертвая зона" есть, и такой декремент на байт длиннее.
Варианты без "мертвой зоны":
1. Декремент
sub x+1
jr nc,$+3
xor a
adc a,x
2. Инкремент
add a,-x
jr nc,$+4
ld a,0
sbc a,-x-1
Инкремент скорее всего можно сократить, надо подумать.
Эти варианты имеют разную длительность. В первоначальном, 14-летней давности посте, это подчёркивалось.
Что вы все имеете в виду под мертвой зоной? Когда число изначально "неправильное" (выходит за нужный предел) ? Это уже проблемы программиста =)
Эти варианты имеют разную длительность. В первоначальном, 14-летней давности посте, это подчёркивалось.
Выровнять не сложно, но получается громоздко. Для примера декремент (инкремент можно сделать аналогично)
sub x+1
jr c,$+6
add a,x
jr $+6
ld a,x
jr nc,$
В общем, чтобы избежать пресловутой "мертвой зоны" (если я правильно вас понял), когда число вне диапазона, то вариант от ivagor самое то.
Если же число задано корректно и постепенно приближается к порогу, то выравнивание по тактам делается весьма просто:
увеличение:
CHECK_UP CP x
ADC A,0
PROFIT ...
уменьшение:
CHECK_DN CP x+1
CCF
SBC A,0
PROFIT ...
Единственное ограничение для декремента - не получится задать число 255 (255+1=0), флаг всегда будет сброшен. Но такой счетчик сам по себе лишен смысла.
выравнивание по тактам делается весьма просто
В CHECK_DN можно заменить ccf/ sbc a,0 на adc a,-1 и тогда получатся полные аналоги исходных вариантов (https://zx-pk.ru/threads/9031-etyudy.html?p=1154886&viewfull=1#post1154886) по размеру и скорости.
Нужна подпрограмма опроса клавиатуры. Всё, что я везде встречал - процедуры опроса для игр, где опрашивается 1-2-5 клавиш, игнорируются SS и CS, нет задержки перед автоповтором, и вообще - не предназначены для текстового редактора или хотя бы для текстового поля ввода.
Стоит ли изобретать велосипед, или не заморачиваться, и использовать в процедуре обработки прерываний jp 56 , и не парить мозг?
Думаё, её будет проще написать, чем приспосабливать что-то чужое :)
Нужна подпрограмма опроса клавиатуры. Всё, что я везде встречал - процедуры опроса для игр, где опрашивается 1-2-5 клавиш, игнорируются SS и CS, нет задержки перед автоповтором, и вообще - не предназначены для текстового редактора или хотя бы для текстового поля ввода.
Стоит ли изобретать велосипед, или не заморачиваться, и использовать в процедуре обработки прерываний jp 56 , и не парить мозг?
Это уже не Этюды. Это серьезное поделие.
Думаё, её будет проще написать, чем приспосабливать что-то чужое :)
Не, ну она ж уже написана, и уже сидит в ПЗУ. Может просто, где-то есть покороче/побыстрее/пооптимальнее - процедуры ПЗУ ж и бейсиком используются, следовательно, можно было бы многое выбросить и/или упростить. А может, это и не критично.
- - - Добавлено - - -
Это уже не Этюды. Это серьезное поделие.
Тоже так думаю. Следовательно - пусть себе jp 56 живёт, и процветает.
В общем, чтобы избежать пресловутой "мертвой зоны" (если я правильно вас понял), когда число вне диапазона, то вариант от ivagor самое то.
Если же число задано корректно и постепенно приближается к порогу, то выравнивание по тактам делается весьма просто:
увеличение:
CHECK_UP CP x
ADC A,0
PROFIT ...
уменьшение:
CHECK_DN CP x+1
CCF
SBC A,0
PROFIT ...
Единственное ограничение для декремента - не получится задать число 255 (255+1=0), флаг всегда будет сброшен. Но такой счетчик сам по себе лишен смысла.
sub n+1
adc a,n
не?
https://worldofspectrum.org/forums/discussion/60525/console-framework#latest
Какой-то гражданин решил написать консоль для Спектрума. Ну и заодно написал свою процедуру опроса клавиатуры.
Нужна подпрограмма опроса клавиатуры. Всё, что я везде встречал - процедуры опроса для игр, где опрашивается 1-2-5 клавиш, игнорируются SS и CS, нет задержки перед автоповтором, и вообще - не предназначены для текстового редактора или хотя бы для текстового поля ввода.
Стоит ли изобретать велосипед, или не заморачиваться, и использовать в процедуре обработки прерываний jp 56 , и не парить мозг?
https://github.com/salextpuru/sdcc-noinit/tree/master/libsrc/libzxkbd
Там все на асме в сишной обертке.
Две раскладки.
Там koi8r стоит и ascii. Но можно таблицы поменять.
Альты, шрифты отдельно слушаются.
Barmaley_m
17.09.2022, 20:21
Нужна подпрограмма опроса клавиатуры. Всё, что я везде встречал - процедуры опроса для игр, где опрашивается 1-2-5 клавиш, игнорируются SS и CS, нет задержки перед автоповтором, и вообще - не предназначены для текстового редактора или хотя бы для текстового поля ввода.
Это не подпрограмма, это тебе нужен целый драйвер клавиатуры.
Стоит ли изобретать велосипед,
Смотря куда это все пойдет. Какие есть еще варианты:
1) Поизучать дизассемблер ПЗУ ZX-Spectrum от Яна Логана и Френка О-хары, на предмет драйвера клавиатуры. Изучить его, взять из него нужные части и приспособить под свои нужды;
2) Взять драйвер AZKEYB.MAC (от ASC CP/M)7779077791
3) Найти какой-нибудь другой драйвер клавиатуры или написать свой на основе изучения существующих;
Встречал где-то на просторах гитхаба процедурку, которая зажигает нужный бит в 16-разрядном регистре, соответственно значению аккумулятора.
На входе A = 5
На выходе HL = %0000000000100000
Не могу найти.
Ну, не в цикле ж это делать.
Ну, не в цикле ж это делать.
Цикл = самое универсальное (любые регистры), можно таблицу = самое быстрое, ну а можно самомод-код:
; SET A,HL
LD HL,0
RLA
RLA
CP #20
RLA
OR #C4
LD ($+4),A
DW #CBCB
Или чуть медленней, но уже с игнором старшего полубайта А
; SET A,HL
LD HL,0
SLI A
SLA A
CP #20
RLA
OR #C4
LD ($+4),A
DW #CBCB
У кого есть СТРЕЛОЧКА? Курсор мыши 8х8 px, бегающий по экрану. Без лишних наворотов - такое, как определение хот-спотов и прочие хитрости сам сделаю.
Сюда (http://zxdn.narod.ru/coding/asp7iarw.txt) можно не отправлять - оно неработоспособно, нужно пилить и пилить, если оно вообще когда-нибудь работало.
out_sprite_xy_32px_2scr.zip от Dr.Bars - это для стрелочки перебор и оверкилл. Хотя, работает замечательно.
У кого есть СТРЕЛОЧКА
Да стрелочек куча, раз никто не откликнулся = неверный запрос.
1. От мыши (формат коор, куда что как? Интерфейс какой? Живой или эмуль? Или клон? Или вообще AY?)
2. Спрайт как кодировать? (маской, тупо XOR, или может вообще в буфер?)
3. На два экрана или only #4000?
4. Ориентируемся на скорость или объём?
5. По прерываниям (чтоб плавно) или пофик, но чтоб было?
В общем потому и молчание - уж больно похоже "Дедушка Мороз, я хочу смартфон! - ОК, держи IBM Simon (https://ru.wikipedia.org/wiki/IBM_Simon)! -Дедушка, да я не это имел в виду!!!"
:)
NEO SPECTRUMAN
04.11.2023, 19:02
ОК, держи IBM Simon! -Дедушка, да я не это имел в виду!!!"
походу кто то просто не понел
что ибмысимона можно потом выгодно толкнуть &)
раз никто не откликнулся = неверный запрос
Сомневаюсь, что поэтому.
Сомневаюсь, что поэтому.
Ещё есть такой вариант стрелочки - https://zxpress.ru/book_articles.php?id=668
Здравствуйте.
Нужна короткая процедура вычисления по формуле:
C = (A / B) * 8 ,
где A, B, C - байты и A в диапазоне от 1 до (B - 1).
Здравствуйте.
Нужна короткая процедура вычисления по формуле:
C = (A / B) * 8 ,
где A, B, C - байты и A в диапазоне от 1 до (B - 1).
А разрядность А В С какая? 8 или 16?
Dart Alver
28.01.2024, 13:09
А разрядность А В С какая? 8 или 16?
Он же написал байты. ))
Тут больше вопрос просто байты или и регистры нужны им соответствующие ?
C = (A / B) * 8 ,
где A, B, C - байты и A в диапазоне от 1 до (B - 1).
По делению видел процедуры https://zxpress.ru/article.php?id=11746&ysclid=lrxb1u4v3h388674283 , не знаю правда насколько верные.
А здесь по сути надо C=A*8/B , а с учётом условия: ' A в диапазоне от 1 до (B - 1) ' - результат будет не больше 7
Дальше мозги не соображают ))
Нужна короткая процедура вычисления по формуле:
C = (A / B) * 8 ,
где A, B, C - байты и A в диапазоне от 1 до (B - 1).
Вот, есть такая:
ld c,7
sla a ; rlca
jr c,skip1
cp b
jr nc,skip1
res 2,c
jr skip2
skip1:
sub b
skip2:
sla a ; rlca
jr c,skip3
cp b
jr nc,skip3
res 1,c
jr skip4
skip3:
sub b
skip4:
sla a ; rlca
jr c,skip5
cp b
jr nc,skip5
res 0,c
skip5:
35 байт 97 тактов.
Можно уменьшить, за счёт точности, заменив "sla a" на "rlca".
Можно уменьшить за счёт замедления, свернув повторение в цикл.
Возможно можно оптимизировать.
97 тактов это не точно. Можно сказать, примерно 100 тактов.
C = (A / B) * 8 , где A, B, C - регистры Z80
Кстати, если делать по-уму - то заменятся должны не просто случайные пиксели, но ещё и так чтобы дважды не заменялись те что уже заменены.
Решается толково написаным RND. Тогда количество итераций можно выяснить исходя из знания площади картинки и количество заменяемых пикселей за раз...
Это как-бы настоящий подход, а то в большинстве процедур - костыли, прогнали несколько замен - а потом окончательная - весь кусок сразу...
Не прошло и 4 года, как на спектумкомпьютинг подняли схожую тему: https://spectrumcomputing.co.uk/forums/viewtopic.php?t=11240
Очень интересует подобный генератор, но знаний в тервере и комбинаторике не хватает, чтобы что-нибудь тут понять.
Что нужно: генератор, позволяющий настраивать период в заданных пределах, например, имеется массив
db 1
db 2
db 3
db 4
db 5
Хочу запустить генератор с периодом 5, который позволил бы мне из этого массива выбрать за 5 итераций все значения в случайном порядке - "3, 1, 4, 5, 2".
Или - с периодом 10000 для другого массива, из 10000 элементов.
Позволяет ли так сделать эта LCG-функция, которая рассматривается в посте?
генератор, позволяющий настраивать период
Ну в общем-то вроде можно взять любой RND (например однобайтовый), который даёт 0...255 и умножать результат на N+1 - получится что в старшем байте и будет нечто 0...N
Оно-то будет. А что будет? Любой RND может выдать последовательность "3", "3", "3", "1", "4"? Думаю, может. А зачем мне "3", "3", "3"?
Оно-то будет. А что будет? Любой RND может выдать последовательность "3", "3", "3", "1", "4"? Думаю, может. А зачем мне "3", "3", "3"?
Ну можно заюзать готовые таблички размером 256 где расставлены все эти 0...N и считывать элементы индексом RND.
Можно и без таблиц, но возня со всякими там делениями будет замедлять...
Можно, конечно. Особенно удобно, когда хочешь побитово, "по точкам" зажечь или погасить изображение на экране. Всего делов-то - сделать табличку в 49152 байта.
По ссылке как раз вроде бы, как я понял, описывается генератор неповторяющихся последовательностей с настраиваемым периодом. Даже не генератор последовательностей, а, скорее, shuffle-процедура.
https://spectrumcomputing.co.uk/forums/viewtopic.php?p=140188&sid=d6093b83d47b2065f92602d7700ce7aa#p140188
https://worldofspectrum.org/forums/discussion/comment/1013871/#Comment_1013871
когда хочешь побитово, "по точкам" зажечь или погасить изображение на экране
Ну возми 16-битный RND (в том-же ZX-Ревю) и вперёд!
Вызываешь, получаешь двухбайтное "случайное" значение, один байт это X второй Y, смотришь что там у тебя в картинке - если бит включен - соотв. включаешь его на экране. Повторить 65536 раз и в результате переберутся все возможные координаты и картинка проявится. Конечно если Y>192 то просто пропускается эта точка. Все нормальные генераторы пробегаются по всем коордам достаточно рандомно и попадают туда где уже были только спустя 65536 итераций (ну если 16-ти битный).
Я на таком делал:
; zx-review 3-4 1997
; http://zxpress.ru/article.php?id=1010
RND_32 LD HL,(SEED)
CALL RND
RND LD A,H:ADD HL,HL:XOR H
ADD HL,HL,HL,HL,HL,HL:XOR H
ADD HL,HL,HL,HL:XOR H:ADD HL,HL,HL,HL
LD L,A:LD (SEED),HL
RET
SEED DEFW #FFFF ; НЕ НОЛЬ!
Barmaley_m
08.04.2024, 19:03
Что нужно: генератор, позволяющий настраивать период в заданных пределах, ... Хочу запустить генератор с периодом 5, который позволил бы мне из этого массива выбрать за 5 итераций все значения в случайном порядке - "3, 1, 4, 5, 2". Или - с периодом 10000 для другого массива, из 10000 элементов.
Позволяет ли так сделать эта LCG-функция, которая рассматривается в посте?
Про период линейного конгруэтного генератора не подскажу, но подскажу по другим генераторам: LFSR или Xorshift. Период этих генераторов в точности равен 2^N-1, где N - количество бит состояния. При итеративном исполнении процедуры генератора его состояние меняется на новое; каждое состояние возникает ровно один раз за период. Так что такие генераторы можно и нужно использовать для вещей вроде попиксельной замены экрана. Кстати, в игре Batman The Movie используется именно LFSR.
Если N=16, то период будет 65535 (на 1 меньше, так как состояние "все нули" не возникает при работе генератора). Если N=4 - то период будет 15.
Можно зайти и из общих соображений. Любой псевдослучайный генератор имеет некое состояние, которое сохраняется между вызовами. Если для хранения состояния используется N бит - то период повторения не может быть больше 2^N. Может быть меньше (как в случае LFSR, на 1 меньше), или даже намного меньше (при использовании "плохих" LFSR или констант для линейного конгруэнтного). Если период меньше 2^N - то некоторые комбинации бит состояния за период не возникают. Но те, которые возникают, появляются один и только один раз.
В случае линейного конгруэнтного генератора, так как его состояние является остатком от деления на некоторое число M - то период не может быть больше, чем M. Если правильно подобрать константы - то наверное, можно сделать, чтобы он был строго равен M, что тебе и требуется.
Bedazzle
09.04.2024, 15:12
Что нужно: генератор, позволяющий настраивать период в заданных пределах, например, имеется массив
а массив можно менять в процессе выборки?
некоторое время назад решал похожую проблему в лоадере:
https://www.dropbox.com/scl/fi/0l0cg1ybhs1hd028xt4vs/rene_loader.tzx?rlkey=fi0w4r6jw85ojs3o9o4ocm0rq&dl=0
а массив можно менять в процессе выборки?
Ну, пускай будет можно. Если в массиве 5-50-500-5000 элементов - то можно и копию сделать, если нужно.
Но "зачеркивать" уже однажды выбранные элементы - не вариант.
Здравствуйте.
Как вычислить (Байт Mod 7) ?
Результат: 0 - 6.
поделить на 7 сдвигами
;A=A mod 7
ld c,a
xor a
ld b,#08
label2 rl c
adc a,a
sbc a,7
jr nc,label1
add a,7
label1 djnz label2
поделить на 7 сдвигами
;A=A mod 7
...
Как вы такое вообще придумываете?
Я серьёзно, на каких примерах можно научиться подобным вывертам?
это частный случай восьмибитного деления сдвигами
это частный случай восьмибитного деления сдвигами
Из Левенталя что-ли?
Но всё равно, это ведь как-то придумали
"Взять всё и поделить!"(с)
https://chilliant.blogspot.com/2010/12/modulo-arithmetic-in-z80-assembler.html
Задумался по поводу придумывания вывертов.
Вспомнил, что был специальный калькулятор для программистов на ассемблере - hp 16c, на нём даже программировать можно, чтоб придуманные выверты обкатывать.
Нашёл эмулятор https://www.hpcalc.org/details/9513
Сейчас осваиваю, надеюсь поможет.
Задумался по поводу придумывания вывертов.
Вспомнил, что был специальный калькулятор для программистов на ассемблере - hp 16c, на нём даже программировать можно, чтоб придуманные выверты обкатывать.
Нашёл эмулятор https://www.hpcalc.org/details/9513
Сейчас осваиваю, надеюсь поможет.
на 4-битном HP Saturn? Ну да, конечно.
на 4-битном HP Saturn? Ну да, конечно.
А что не так?
А что не так?
Как можно применить калькулятор к программированию на Спектруме ?
Как можно применить калькулятор к программированию на Спектруме ?
Так .это необычный калькулятор, специализированный для программистов на ассемблере. У него есть логические команды, команды сдвига, в том числе с переносом. При этом он программируемый, то есть можно составить некое подобие этюда и отладить его.
Теперь ясно. Если не ошибаюсь, у проца нет команды xor и она заменяется иначе. Вот будет весело.
Теперь ясно. Если не ошибаюсь, у проца нет команды xor и она заменяется иначе. Вот будет весело.
A or B - даёт нули в тех разрядах, которые при XOR остаются нуля
A and B - даёт единицы в тех разрядах, которые при XOR остаются нулями, а её инверсия - соответственно - нули.
То есть A xor B = (A or B) and not (A and B) = (not A and B) or (A and not B)
В самом деле:
0 xor 0 = (0 or 0) and not (0 and 0) = 0
0 xor 1 = (0 or 1) and not (0 and 1) = 1
1 xor 0 = (1 or 0) and not (1 and 0) = 1
1 xor 1 = (1 or 1) and not (1 and 1) = 0
Ну а как реализовать на конкретном проце без ХOR - это уж как удобнее
В старое время использовали отладчик STS2.6 и новее. За неимением реала используется эмуль с отладчиком. Если калькулятор поможет разработке, то это хорошо. (стикер скепсиса)
Хороший калькулятор действительно редкость, для программирования под Linux помоему самый удобный gnome-calculator, кнопки в нем неудобно расположены, но можно сразу вводить текст выражения в область индикатора.
https://g0blinish.ucoz.ru/forblog_zx/koch.png
Снежинка Коха прокатит за этюд?
https://g0blinish.ucoz.ru/forblog_zx/koch.zip
Всех с наступающим! Пусть Спектрум принесет много интересных игр, хороших демок и разных программ.
Понадобилось знаковое 8*8=16. Взял для начала "классику"
MULS ; HL=H*E (+-)
LD L,0 ; 2b, 7t \
LD D,L ; 1b, 4t |
LD A,H ; 1b, 4t |
XOR E ; 1b, 4t |
EXA ; 1b, 4t |
LD A,H ; 1b, 4t |
NEG ; 2b, 8t > = 20b, 75t
JP M,$+4 ; 3b, 10t |
LD H,A ; 1b, 4t |
LD A,E ; 1b, 4t |
NEG ; 2b, 8t |
JP M,$+4 ; 3b, 10t |
LD E,A ; 1b, 4t /
DUP 8
ADD HL,HL ; 1b, 11t \
JP NC,$+4 ; 3b, 10t > = 40b, 256t
ADD HL,DE ; 1b, 11t /
EDUP
EXA ; 1b, 4t \
JP P,$+7 ; 3b, 10t |
EX DE,HL ; 1b, 4t > = 9b, 47t
LD L,H ; 1b, 4t |
SBC HL,DE ; 2b, 15t |
RET ; 1b, 10t /
Итого: 67 байт, 378 тактов
Где-то на форуме я уже кидал клич как её ускорить, но ничего путного не вышло.
Казалось-бы раз знаковые биты предварительно обнуляются то достаточно семи DUP вместо восьми, но оказалось что это не так,
ибо -128 (#80) после NEG становится +128 (те-же #80), т.е. всё-таки НАДО ОБРАБАТЫВАТЬ ВСЕ 8 БИТ.
А ускорить всё-таки хотелось... И в результате-таки получилось
MULS ;[ HL=H*E (+-)
LD A,H ; 1b, 4t \
XOR E ; 1b, 4t |
EXA ; 1b, 4t |
XOR A ; 1b, 4t |
LD L,A ; 1b, 4t |
LD D,A ; 1b, 4t > = 17b, 64t
SUB H ; 1b, 4t |
JP M,$+4 ; 3b, 10t |
LD H,A ; 1b, 4t |
XOR A ; 1b, 4t |
SUB E ; 1b, 4t |
JP M,$+4 ; 3b, 10t |
LD E,A ; 1b, 4t /
DUP 8
ADD HL,HL ; 1b, 11t \
JP NC,$+4 ; 3b, 10t | = 40b, 256t
ADD HL,DE ; 1b, 11t /
EDUP
EXA ; 1b, 4t \
JP P,$+7 ; 3b, 10t |
EX DE,HL ; 1b, 4t > = 9b, 47t
LD L,H ; 1b, 4t |
SBC HL,DE ; 2b, 15t |
RET ; 1b, 10t /
Итого: 66 байт, 367 тактов
Ускорение за счёт замены JP на JR, и JP P, на RET P возможно, но количество тактов перестанет быть константой, так что это опционально...
P.S. Обе процедуры дают правильный (одинаковый) результат на тестовой проверке которая перемножала весь диапазон H (0..255) на весь Е (0..255).
Всё сошлось.
как насчет умножения с использованием таблицы квадратов?
https://map.grauw.nl/sources/external/z80bits.html
как насчет умножения с использованием таблицы квадратов?
https://map.grauw.nl/sources/external/z80bits.html
Ну таблицы - это другая история, тут речь именно об умножении "в столбик".
Ты про Square Table 8-bit * 8-bit Signed что-ли?
Вроде она и так заявлена как знаковая (хотя там что-то пишут про потерю точности)
Гоба на соседнем ресурсе поднял очень актуальную и нетривиальную проблему сравнения 2 знаковых 8 битных числа. Собственно надо сообразить максимально компактно следующее
if x>y then x=-x
сходу получилось
org 0x8000
ld c, -0x70 ;y
ld e, 0x11 ;x
call comp
; x in A
jr $
comp
ld a, c
add a,0x80
ld c, a
ld a, e
add a,0x80
cp c
ld a, e
ret c
ret z
cpl
inc a
ret
Но есть не покидающее чувство что можно красиво заюзать мистический P/V или S флаги.
Например так
SignedComp:
;Input:
;A=X
;C=Y
;Output:
;if X<=Y THEN A=X
;if X>Y THEN A=-X
cp c
ret z
jp po,$+5
ret p
db 0FEh ;cp ..
ret m
neg
ret
и не портит С (Y)
Красавчик, ivagor лучший!
недавно столкнулся с необходимостью лока, это когда процедура запрещает повторный вызов самой себя или работу с областью памяти. например в прерывании.
я не знаю как данная задача решается на современном железе но на z80 не придумал ничего лучше inc (rp)
inc (ix+stHead.lock)
ld b, (ix+stHead.lock)
dec b
jr z, .cont
.exit
dec (ix+stHead.lock)
ret
.cont
можно заменить на djnz по обстоятельствам
хотел понять есть ли варианты лока?
А вдруг будет целый шторм запусков, и inc-ов будет выполнено постоянно больше dec-ов?
Может изначально в ячейке ложить единицу, и перед декрементом её проверять?
ld b, (ix+stHead.lock)
dec b
ret nz
dec (ix+stHead.lock)
jr z, .cont
.exit
inc (ix+stHead.lock)
ret
.
Теперь вроде, даже в сильный шторм, проскакивать выполнение должно.
А вдруг будет целый шторм запусков, и inc-ов будет выполнено постоянно больше dec-ов?
Может изначально в ячейке ложить единицу, и перед декрементом её проверять?
ld b, (ix+stHead.lock)
dec b
ret nz
dec (ix+stHead.lock)
jr z, .cont
.exit
inc (ix+stHead.lock)
ret
.
Теперь вроде, даже в сильный шторм, проскакивать выполнение должно.
а теперь посмотри что будет если прерывание придет между 1 и 2 командой. в моем случае сколько бы не было инков на континуе перейдет только первый
- - - Добавлено - - -
хотя тупанул, плохого ничего не случится, но и первая проверка будет бесполезной, как защита от гипотетического шторма nmi тоже не спасет но вообще имеет смысл. даже скорее для локов когда лочится в одной процедуре а снимается лок в другой, подход с предварительной проверкой обязателен. спасибо полезно.
- - - Добавлено - - -
хотя еще прикинул, а потом еще, ну для устройств далеких от спектрума и со специфическим софтом наверное и надо доп проверку, у нас не подобрал условий для переполнения 255, даже больше 3 не могу придумать.
Dart Alver
08.09.2025, 11:49
недавно столкнулся с необходимостью лока, это когда процедура запрещает повторный вызов самой себя или работу с областью памяти.
Для однократного вызова процедуры можно глушить ретурном.
proc_ push af
ld a,#C9 ; 'ret'
ld (proc_),a
pop af ; 7 байтов с push , 5 без них
...
proc_ ld hl,proc_ ; или если например hl не жалко
ld (hl),#C9
...
или можно встроить семафор в тело программы
proc_ ld a,1
or a
ret z ; jr z, exit
xor a
ld (proc_+1),a
...
- - - Добавлено - - -
Ещё пришло на ум . Если однократных вызовов надо много, то можно сделать процедуру залочивания.
; процедура лока
locker_ ex (sp),hl ; после первого применения поставит заглушку 'ret'
push hl ; по вызывавшему адресу
dec hl
dec hl
dec hl
ld (hl),#C9
pop hl
ex (sp),hl
ret
; применение
proc_ call locker_ ; 3 байта на вызов
...
Для однократного вызова процедуры можно глушить ретурном.
Это все не рабочее, просто проверяй предположением что допустим прерывание пришло после первой комманды. Эти локи с комбинациями с di такое себе.
Может изначально в ячейке ложить единицу
а вот это я точно использую, уменьшает начальную проверку. и b грузить не надо
- - - Добавлено - - -
на самом деле все варианты норм, потому что у нас проц не может одновременно выполнять несколько потоков и к локу требования намного меньше, выполнение кода все равно будет завершено полностью перед другой обработкой потому как выполнение после проверки флага и изменения флага, необходимости одновременной проверки и изменения нет.
спасибо всем, пересмотрю вообще требования.
Dart Alver
08.09.2025, 13:10
Это все не рабочее, просто проверяй предположением что допустим прерывание пришло после первой комманды. Эти локи с комбинациями с di такое себе.
Пардон не понял, что процедуру планируется применять ещё и в обслуживании прерывания.
тут даже речь не совсем про процедуру, с одними данными могут работать много процедур и лочить их должна одна пока не отпустит. анлок может вообще вызываться не из нее.
еще немного прикинул, требования к локу становятся изначальными если кто то решит сделать систему с многозадачностью, в этом случае строгий лок необходим и вариант с inc (rp) наверное единственный.
у меня потребность возникла при работе со списками, не хотелось бы чтобы прерывание что то удаляло в списке когда основной код берет из него данные. хотя опять же повторюсь, надо просмотреть все варианты, возможно мне это не нужно будет
Bedazzle
08.09.2025, 13:25
А если в начале процедуры модифицировать первый байт как ret, и в конце - возвращать в nop?
Тогда всегда сможет работать только одна.
А если в начале процедуры модифицировать первый байт как ret, и в конце - возвращать в nop?
Тогда всегда сможет работать только одна.
в общем случае лок отдельной процедуры это пол задачи, чаще нужно лочить данные. В этом случае все становится сложнее, выделение области для переменных на стеке или вообще без переменных в памяти. грубо говоря есть процедура удаления элемента списка и добавления, если удалять в основном цикле а добавлять в прерывании (дизайн не очень согласен, но списки разные бывают, допустим список с нажатыми клавишами) возможны нарушения данных. как то так. Возможно пересмотрю подход, я просто вернулся к https://vtrd.in/release.php?r=f60879576ade55b3f12e1f79f01055fc и мне понадобились некоторые базовые вещи, по классике не охота, а по другому пока не умею.
Может неправильно понял задачу, но если правильно - то делал разделённым счётом.
Var1 - где-то там в основном цикле инкреминтируется
Var2 - в контрольной процедуре (которую нужно ограничивать) держит в себе последнее значение Var1 которое "видело" и сравниваю с разностью (ну допустим уже больше N вызовов) - разрешает дальнейший код, если пока не прошло достаточно тиков - отменяется.
(костыльно, но на связке S300-Weintek не придумалось ничего лучше, зато работает замечательно, 10 лет в разных вариациях)
Покритикуйте такую идею для реализации критической секции. На emuverse.ru (https://emuverse.ru/wiki/Zilog_Z80/%D0%A1%D0%B8%D1%81%D1%82%D0%B5%D0%BC%D0%B0_%D0%BA% D0%BE%D0%BC%D0%B0%D0%BD%D0%B4) упоминается недокументированная команда LD DDD, rot [IX+d], которая производит битовый сдвиг байта в памяти и запись результата в регистр. Т.е. имеем две операции в рамках одной атомарной. Идея такова. Нулевой байт обозначает свободную секцию. Используем LD A, SLI [IX+d] (или SCF: LD A, RL [IX+d]). Поток, которые первый выполняет эту атомарную операцию занесет в секцию значение 1, последующие потоки будут заносить 3, 7, 15 и т.д., но никогда не 1. Т.е. наличие единицы в регистре A обозначает, что мы захватили секцию. Освобождение секции выполняется простым занесением нуля в секцию.
;; если флаг Z при выходе функции включен, то зашли в секцию
TryEnterCriticalSection:
LD A, SLI [IX]
DEC A
RET
EnterCriticalSection:
LD A, SLI [IX]
DEC A
JR NZ, $ - 5
RET
LeaveCriticalSection:
LD [IX], 0
RET
Update.
Что-то я слишком все усложнил. Можно и на документированных командах это сделать. 0x80 - значение свободной секции.
;; если флаг С при выходе функции включен, то зашли в секцию
TryEnterCriticalSection:
SLA (IX)
RET
EnterCriticalSection:
SLA (IX)
JR NC, $ - 4
RET
LeaveCriticalSection:
LD [IX], 0x80
RET
Что-то я слишком все усложнил. Можно и на документированных командах это сделать. 0x80 - значение свободной секции.
ролами тоже нормально, я думал как вариант, просто не нашел преимуществ перед inc/dec. а так все верно, рабочая идея
- - - Добавлено - - -
Может неправильно понял задачу, но если правильно - то делал разделённым счётом.
Var1 - где-то там в основном цикле инкреминтируется
Var2 - в контрольной процедуре (которую нужно ограничивать) держит в себе последнее значение Var1 которое "видело" и сравниваю с разностью (ну допустим уже больше N вызовов) - разрешает дальнейший код, если пока не прошло достаточно тиков - отменяется.
(костыльно, но на связке S300-Weintek не придумалось ничего лучше, зато работает замечательно, 10 лет в разных вариациях)
ну для каких вариантов приемлемо конечно, но тут как бы все в лоб, у меня потребность: есть набор процедур работы со списками и есть несколько списков, вот список с которым работаем надо блочить
- - - Добавлено - - -
Вообщем посмотрел на все еще раз и решил оставить 0>inc>sub>dec это позволяет помимо прочего по флагу z определять блок в вызывающей процедуре, все остальное требует отдельного кода для этого. rrc (rp) rlc (rp) получается даже лучше всего и флаг С однозначно говорит что отработало или нет (только при выходе без снятия лока не забыть сделать scf) и меньше занимает чем проверка 0>inc
Powered by vBulletin® Version 4.2.5 Copyright © 2026 vBulletin Solutions, Inc. All rights reserved. Перевод: zCarot