PDA

Просмотр полной версии : Использование стека и прерывания



drbars
09.12.2012, 16:40
Почитав замечательную статью (http://zxpress.ru/article.php?id=11231) в журнале DeJa Vu #0A "Программирование - быстрая процедура печати спрайтов через стек от WoodlandStudio." - решил попробовать реализовать. В итоге получаю видеоэффекты и зависание после секунд работы.

Суть моей задачи такова. Для примера на прерываниях висит музыка и кое какой вывод графики стеком. Всё работает.

Eсть необходимость "читать" через стек данные из памяти в регистр DE во время прерывания... И тут наступают грабли.

Для примера соорудил код:



INT_VECTOR=#BE00
INT_HANDLER=#BFBF
STACK_MAIN=#7FFF
STACK_IM2=INT_HANDLER-1

ORG #8000
START:
DI
LD SP,STACK_MAIN

LD HL,INT_VECTOR
LD BC,#00BF
LD (HL),C
INC HL
DJNZ $-2
LD (HL),C

LD A,INT_VECTOR/256
LD I,A
IM 2
EI

MAIN_LOOP:
; тут читаем стеком данные из памяти и кидаем на экран для примера
; EI:HALT тут нет, т.к прерывание должно приходить во время работы чтения стеком.
;Если LD SP,SPR1 закоментить зависать перестает, но ясно что выводит мусор :)

LD (STK1+1),SP
LD SP,SPR1
LD HL,#4000
DUP 16
POP DE
LD (HL),E
INC HL
LD (HL),D
INC HL
EDUP
STK1 LD SP,#0000
JP MAIN_LOOP

SPR1 DS #20,#FF

MAIN_ISR:
PUSH IX
PUSH HL
PUSH DE
PUSH BC
PUSH AF
EXX
EX AF,AF'
PUSH IY
PUSH HL
PUSH DE
PUSH BC
PUSH AF
; тут музыка и графика стеком выводится

POP AF
POP BC
POP DE
POP HL
POP IY
EX AF,AF'
EXX
POP AF
POP BC
POP DE
POP HL
POP IX
RET

; Обработчик прерываний
ORG INT_HANDLER
EX DE,HL
EX (SP),HL
LD (SaveRET+1),HL
EX DE,HL
POP DE
LD (SaveSP+1),sp
LD SP,STACK_IM2
CALL MAIN_ISR
SaveSP LD SP,#0000
EI
SaveRET JP #0000



В чём же может быть проблема? :( Что-то к вечеру совсем не соображу...

drbars
09.12.2012, 16:55
Вроде помогло...)) Шо это было?)))

TomCaT
09.12.2012, 16:59
Если прерывания разрешены (EI перед Main Loop), и SP не имеет свободной области под указываемым им адресом - то гайки. Если читаемый буфер SPR1 односторонний или постоянно обновляемый в тех же прерываниях, то достаточно освободить немного места перед его началом, как сказал krt17. Кстати, там точно нужны 2 команды EX DE,HL ?

drbars
09.12.2012, 17:04
Как я понял, у каждого спрайта, который будет выводиться таким способом первые два байта нужно резервировать. так?

drbars
09.12.2012, 17:12
Т.к. прерывание может подпортить любые его четыре байта во время вывода на экран.
В моём случае вроде ничего не портится, полоска выводится непрерывно. Глюков в ней нет. Два байта в начале запарываются, но там можно всегда нули держать на такой случай.



krt17,
Не догнал, что там это даст?

ld hl,spr1
ld e,(hl)
inc hl
ld d,(hl)
inc hl
ld sp,hl
ld hl,#4000

TomCaT
09.12.2012, 17:21
Да, я поправил свое сообщение, но написал все в общих словах, интересно все равно - не совсем понял задачу, которая была поставлена. Перечитал все еще раз - выводятся НЕ 32 байта #FF, а то, что при каждом прерывании обработчик записывает в буфер SPR1?

И нужны ли EX DE,HL ?

drbars
09.12.2012, 17:27
Да, я поправил свое сообщение, но написал все в общих словах, интересно все равно - не совсем понял задачу, которая была поставлена. Перечитал все еще раз - выводятся НЕ 32 байта #FF, а то, что при каждом прерывании обработчик записывает в буфер SPR1?

И нужны ли EX DE,HL ?

Пример в топике - отладка алгоритма. В Main_loop будет цикл задач. Одна из которых должна рисовать локации на экране таким способом.



И нужны ли EX DE,HL ?

Да, нужны. Без них не работает :)

Titus
09.12.2012, 17:29
При разрешенных прерываниях будут портится спрайты, если только не сделать синхронизацию с прерываниями так, чтобы на момент прихода прерывания, спрайты уже все были напечатаны.

А... все понятно, у вас как бы восстановление в обработчике INT'a )

drbars
09.12.2012, 17:30
для понимания процесса рекомендую разобраться самому, думать это же приятно :v2_dizzy_roll:.
Сколько уже напридумывал, сам в шоке :) Суть в общем-то ясна :v2_dizzy_tired2:

Чудненько работает:



ld hl,SPR1
ld e,(hl)
inc hl
ld d,(hl)
inc hl
ld sp,hl
ld hl,#4000

DUP 16
LD (HL),E
INC HL
LD (HL),D
INC HL
POP DE
EDUP



Этот способ хорош, чтобы дергать расчитанные адреса экрана из таблиц циклом)

jerri
09.12.2012, 20:14
drbars,


interrupt
di
ex (sp),hl ;забираем со стека адрес возврата
ld (imret),hl ;сохраняем адрес возврата
pop hl ;забираем hl
ld (imstek),sp ;сохраняем стек
push bc ;либо push de в зависимости от того каким регистром берем спрайты
ld sp,int_sp
call proc_int

ld sp,$
imstek equ $-2
ei
jp $
imret equ $-2


;процедура вывода спрайта

;hl откуда
;de куда
;в bc будут данные

ld (retsp),sp
;забираем первое слово
ld c,(hl)
inc hl
ld b,(hl)
inc hl
ld sp,hl
;---------------
;манипулируем
;---------------
ld sp,$
retsp equ $-2
ret


как то так

drbars
09.12.2012, 21:06
Я уже запилил как было в первом посте, как понял где баг. Мне с DE выгоднее работать.

jerri
09.12.2012, 21:30
нынче замедлять код модно? обработчик из 1 поста оптимальный, не надо его трогать, ну или по крайней мере так трогать

этот обработчик сочинил AlCo он более универсальный

все зависит от того для чего надо
у Медногова
после передачи данных их hl в sp
есть конструкция

ld h b
ld l c

что как бы тоже потери тактов

drbars
09.12.2012, 21:41
еще совет раз решил заморочится графикой через стек, inc hl в конкретной ситуации можно менять на inc l это 2 такта на байт в данном случае почти на 10 % быстрее
INC L не всегда реально использовать. У меня таблица адресов не кратна 256, немного больше... на 32 байта кажется.

jerri
09.12.2012, 22:04
drbars, ecли на четном адресе живет то можно один inc hl заменить на inc l

TomCaT
11.12.2012, 00:56
Нет, я вот что не понимаю:

Произошло прерывание, надо снять адрес возврата со стека, и сохранить его на будущее. Так как там будет JP по адресу, со стека адрес надо снять, но не испортив регистров.


EX DE,HL
EX (SP),HL
LD (SaveRET+1),HL
EX DE,HL
POP DE

Адрес сохранен, регистры на своих местах, SP как был до прерывания.
А если вот так:


EX (SP),HL
LD (SaveRET+1),HL
POP HL

Меньше на 2 команды и быстрее на 8 тактов. Разве не то же самое делает?..

drbars
11.12.2012, 06:34
Во втором случае ты восстанавливаешь HL ведь. Этот регистр не очень удобен в работе. Процедура обработки прерывания позволяет восстанавливать только один из регистров во время снятия данных стеком.

TomCaT
11.12.2012, 10:13
Во втором случае ты восстанавливаешь HL ведь.

Хм, я, наверное, не то курю, но на мой взгляд после обоих вариантов все регистры остаются на своих местах (кроме SP, который в обоих вариантах на 2 больше чем на входе, т.е. опять же такой, как был до сигнала прерывания). Поэтому, как кажется, неважно, что восстанавливать - главное, чтобы в итоге было восстановлен все. При то, что во втором варианте код короче и быстрее.


Процедура обработки прерывания позволяет восстанавливать только один из регистров во время снятия данных стеком.

Почему один? EX (SP),HL сохраняет HL на стеке, POP HL его восстанавливает. DE и все остальное, кроме SP, не изменяются на всем протяжении, потому что потом идет PUSH всех, а в конце POP всех и прямой JP на адрес возврата. Там ничего не должно теряться в обоих вариантах.

jerri
11.12.2012, 10:23
TomCaT, ты абсолютно прав

но тема здесь немного другая
необходимо после восстановления адреса возврата и стека
восстановить испорченную память что мы и делаем обновляя ее DE у медноногова
и push reg у AlCo



EX DE,HL
EX (SP),HL
LD (SaveRET+1),HL
EX DE,HL
POP DE
LD (SaveSP+1),sp

LD SP,STACK_IM2
CALL MAIN_ISR

SaveSP LD SP,#0000
EI
SaveRET JP #0000



ex (sp),hl ;забираем со стека адрес возврата
ld (imret),hl ;сохраняем адрес возврата
pop hl ;забираем hl
ld (imstek),sp ;сохраняем стек
push bc ;либо push de в зависимости от того каким регистром берем спрайты

ld sp,int_sp
call proc_int

ld sp,$
imstek equ $-2
ei
jp $
imret equ $-2

TomCaT
11.12.2012, 20:39
:v2_dizzy_facepalm:
Вот же, обработчик по сути рассчитан на то, что в DE то же, что было в (SP-2) до прихода прерывания...
Все, понял, спасибо. Хитро-о, однако.

drbars
12.12.2012, 08:05
:v2_dizzy_facepalm:
Хитро-о, однако.
Не то слово, но зато теперь со всем разобрались и можно применить в реальном проекте :v2_dizzy_roll:

ws_mason
12.12.2012, 17:36
Кстати, статья моя (потупив взгляд). Печать спрайта выковыряна из Черного Ворона, а работу с регистром ВГ сам ваял.
Вот пример использования печати спрайта http://zx.pk.ru/showpost.php?p=32815&postcount=60

drbars
16.12.2012, 12:47
Эх, мало толку от этого метода...

Придумал вывод только таким способом пока:



LD HL,TEST_SPR
LD E,(HL)
INC HL
LD D,(HL)
INC HL
LD (STK1+1),SP
LD SP,HL
LD HL,#C000
LD B,#10

LOOP1 LD A,L
DUP 16
LD (HL),E ; 7t
INC L ; 4t
LD (HL),D ; 7t
POP DE ; 10t
INC L ; 4t
EDUP
LD L,A
; на пересылку 2-х байт уходит 7+4+7+10+14=32 такта
; LDI — те же 16 таков, да и использовать LDI тут не получится :(

INC H
LD A,H
AND #07
JR NZ,LOOP2
LD A,L
SUB #E0
LD L,A
SBC A,A
AND #F8
ADD A,H
LD H,A

LOOP2 DJNZ LOOP1

STK1 LD SP,#0000


Вот такой способ уже не получается:

DE — восстанавливать нельзя!



POP DE
LD A,E
OR OFFSET
LD E,A

DUP 32
LDI
EDUP