Код:
;=======================================================================
include "8085.inc"
;=======================================================================
ORG 100h
START:
DI ; Запретить прерывания (т.к. пока не ясно какой вектор прерывания по адресу 0038h)
LXI SP,0BFFFh ; установка стека
MVI A,0C3h ; установить код команды JMP в
STA 0000h ; нулевой адрес и
STA 0038h ; адрес вызова прерывания.
LXI H,PROGRM ; Установить адрес перехода по "БЛК","СБРОС"
SHLD 0001h ; (сразу после кода команды JMP)
LXI H,INIT ; Установить адрес перехода на обработку прерывания
SHLD 0039h ; (также после JMP)
XRA A ; Этой командой проще всего записать в аккумультор ноль
OUT 10h ; и пересылкой кода 00h в порт 10h отключить квазидиск
EI ; Резрешить прерывания, т.к. переходы установлены
HLT ; Ждем вызова INIT
JMP PROGRM ; Перейти на начало программы
;Блок начальной установки
;выполняется один раз
INIT:
DI
MVI A,88h ;Установить режимы портов: 02 - вывод, 03 - вывод
OUT 00 ;мл.4 бита порта 01 - вывод, старшие 4 бита - ввод
LXI H,COLR15 ;в пару "HL" - адрес последнего цвета таблицы цветов
MVI D,16 ;в "D" - счетчик колич. устанавл. цветов (размер табл)
MVI E,15 ;в "E" - адрес для записи в ОЗУ цветогенератора
INCOL_LOOP:
MOV A,E ;установить адрес ОЗУ цвета через
OUT 02 ;порт 02
MOV A,M ;взять в "A" байт (физический код) цвета из таблицы цветов
OUT 0Ch ;записать этот байт в ОЗУ цветогенератора и повторить
OUT 0Ch ;несколько раз для надежной записи по одному адресу,
OUT 0Ch ;т.к. в некоторых "Векорах" наблюдается очень плохая
OUT 0Ch ;запись в ОЗУ цветогенератора и лучше подстраховаться.
OUT 0Ch ;
DCX H ;уменьшить адрес-указатель в таблице цветов
OUT 0Ch ;
DCR E ;уменьшить адрес для ОЗУ цветогенератора
OUT 0Ch ;
DCR D ;уменьшить счетчик байт в таблице цветов
OUT 0Ch ;
JNZ INCOL_LOOP
MVI A,88h ;установить режимы портов 02-вывод, 03-вывод
OUT 00 ;мл. 4 бита порта 01 - вывод, старшие 4 бита - ввод
LDA Border ;взять цвет бордюра ячейки "Border" (матем. код) и
OUT 02 ;установить цвет бордюра на 4-х младших битах порта 02
MVI A,0ffh ;вертикальное положение экрана
OUT 03 ;и установить в порт 03
LXI H,VBLANK ;Установить новый адрес перехода на обработку прерывания
SHLD 0039h ;(также после JMP)
EI ;разрешить прерывание
RET
;Обработчик кадрового прерывания
VBLANK:
DI
PUSH H ;Сохранить все регистры в стеке
PUSH B ;
PUSH D ;
PUSH PSW ;
MVI A,8Ah ;установить режимы портов 02-ввод, 03-вывод
OUT 00 ;мл. 4 бита порта 01 - вывод, старшие 4 бита - ввод
MVI A,0FEh ;установить в младшем разряде порта 03 нулевой потенциал
OUT 03 ;чтобы опросить 1-ую линейку клавиатуры (курсоры)
IN 02 ;взять из порта 02 физич. код нажатия клавиш линейки
CMA ;Инвертируем значение, чтобы биты нажатых клавиш
;соответствовали единицам
ANI 0f0h ;Отсекаем маской ненужное
STA control_state ;поместить этот код в ячейку "control_state"
MVI A,88h ;установить режимы портов 02-вывод, 03-вывод
OUT 00 ;мл. 4 бита порта 01 - вывод, старшие 4 бита - ввод
LDA Border ;взять цвет бордюра ячейки "Border" (матем. код) и
OUT 02 ;установить цвет бордюра на 4-х младших битах порта 02
MVI A,0ffh ;вертикальное положение экрана
OUT 03 ;и установить в порт 03
POP PSW ;вернуть все регистры из стека
POP D ;
POP B ;
POP H ;
EI ;разрешить прерывание (иначе оно будет только один раз)
RET
;=======================================================================
;Переменные и константы
xmax equ 30 ;Максимум для переменной xpos
ymax equ 30 ;Максимум для переменной ypos
left_key_mask equ 10h ;Маски бит клавиш курсора
right_key_mask equ 40h
up_key_mask equ 20h
down_key_mask equ 80h
left_key equ 1 ;Логические коды клавиш курсора
right_key equ 2
up_key equ 3
down_key equ 4
no_key equ 0 ;Логический код "клавиша не нажата"
control_state: ;Хранит инверсию линейки битов для клавиш курсора
db 0
hkey: ;Хранит логический код клавиш курсора горизонтальных стрелок,
db 0 ;либо no_key
vkey: ;Хранит логический код клавиш курсора вертикальных стрелок,
db 0 ;либо no_key
xpos: ;Горизонтальная координата спрайта
db 14
ypos: ;Вертикальная координата спрайта
db 14
oxpos: ;Копия старого значения xpos
db 14
oypos: ;Копия старого значения ypos
db 14
PROGRM: ;Начало программы
CALL CLEAR_SCREEN ;Очищаем экран
PR_CYCLE: ;Главный цикл
lda control_state ;Получаем линейку бит клавиш
ora a ;Если линейка пуста, то
jz RENDER ;переход на отображение спрайта
mov b,a ;Сохраним значение линейки в регистре b
mvi a, no_key ;Предварительно сбросим переменные hkey и vkey
sta hkey ;
sta vkey
H_CHECK: ;Проверка нажатия клавиш курсора горизонтальных стрелок
LEFT: ;Проверка кл. "Влево"
mov a,b ;Загружаем линейку бит клавиш
ani left_key_mask ;Проверяем линейку на бит соответствующий кл. "Влево"
jz RIGHT ;Если бит не установлен, то переход на проверку кл. "Вправо",
mvi a, left_key ;иначе сохраняем логический код клавиши "Стрелка влево" в
sta hkey ;переменную hkey
jmp V_CHECK ;Переход на проверку нажатия клавиш курсора вертикальных стрелок
RIGHT: ;Проверка кл. "Вправо". Делаем аналогично проверке кл. "Влево"
mov a,b
ani right_key_mask
jz V_CHECK
mvi a, right_key
sta hkey
V_CHECK: ;Проверка нажатия клавиш курсора вертикальных стрелок
UP: ;Проверку кл. "Вверх" делаем аналогично предыдущим проверкам
mov a,b
ani up_key_mask
jz DOWN ;Переход на проверку кл. "Вниз"
mvi a, up_key
sta vkey
jmp EXIT_FROM_CHECK ;Выход из проверок нажатий клавиш
DOWN: ;Проверку кл. "Вниз" делаем аналогично предыдущим проверкам
mov a,b
ani down_key_mask
jz EXIT_FROM_CHECK ;Выход из проверок нажатий клавиш
mvi a, down_key
sta vkey
EXIT_FROM_CHECK:
;Блок обновления позиции спрайта
H_POS: ;Обновление координаты x( переменная xpos)
LEFT_DIRECTION_CHECK: ;Проверяем движение влево по нажатию кл. "Влево"
lda hkey ;Загружаем значение переменной hkey
cpi left_key ;Если клавиша не нажата,то
jnz RIGHT_DIRECTION_CHECK ;то переход на проверку движения вправо по нажатию кл. "Вправо"
lda xpos ;Загружаем значение переменной xpos
ora a ;Если значение переменной равно нулю (минимальное значение), то
jz V_POS ;переменную не изменяем, и уходим на обновление координаты y (переменная ypos),
dcr a ;иначе уменшаем значение переменной xpos
sta xpos ;и обновляем переменную xpos
jmp V_POS ;Уходим на обновление координаты y (переменная ypos)
RIGHT_DIRECTION_CHECK: ;Проверяем движение вправо по нажатию кл. "Вправо"
lda hkey ;Загружаем значение переменной hkey
cpi right_key ;Если клавиша не нажата,то
jnz V_POS ;уходим на обновление координаты y (переменная ypos),
lda xpos ;Загружаем значение переменной xpos
cpi xmax ;Если значение переменной равна максимуму, то
jz V_POS ;переменную не изменяем, и уходим на обновление координаты y (переменная ypos),
inr a ;иначе увеличим значение переменной xpos
sta xpos ;и обновляем переменную xpos
V_POS: ;Обновление координаты y (переменная ypos),
UP_DIRECTION_CHECK: ;Проверяем движение вверх по нажатию кл. "Вверх"
;Делаем аналогично проверке двежения вправо
lda vkey
cpi up_key
jnz DOWN_DIRECTION_CHECK ;Переход на проверку движения вниз по нажатию кл. "Вниз"
lda ypos
cpi ymax
jz EXIT_FROM_DIRECTION_CHECK ;Выход из блока обновления
inr a
sta ypos
DOWN_DIRECTION_CHECK: ;Проверяем движение вниз по нажатию кл. "Вниз"
;Делаем аналогично проверке двежения влево
lda vkey
cpi down_key
jnz EXIT_FROM_DIRECTION_CHECK ;Выход из блока обновления
lda ypos
ora a
jz EXIT_FROM_DIRECTION_CHECK ;Выход из блока обновления
dcr a
sta ypos
EXIT_FROM_DIRECTION_CHECK:
RENDER: ;Блок отображения спрайта на экране
lda ypos ;Вычисляем экранный адрес вывода спрайта по координатам ypos и xpos
add a
add a
add a
mov l,a
lda xpos
adi 0e0h
mov h,a
push h ;Сохраняем экранный адрес вывода спрайта в стеке
lda oypos ;Вычисляем экранный адрес области очистки по oypos и oxpos
add a
add a
add a
mov l,a
lda oxpos
adi 0e0h
mov h,a
hlt ;Ждем начала нового кадра
call CLEAR_AREA ;Очищаем область на экране со старым изображением спрайта
pop h ;Восстанавливаем экранный адрес вывода спрайта из стека
lxi b,SPRITE ;Заносим адрес данных спрайта в пару BC
call SPR_OUT ;Выводим спрайт на экран
lda xpos ;Сохраняем координаты спрайта как устаревшие
sta oxpos
lda ypos
sta oypos
jmp PR_CYCLE ;Переход на начало главного цикла
PR_END: ;Конец программы
JMP PR_END
;===================================================================================
;ПРОЦЕДУРЫ
;===================================================================================
;Очистка экрана
CLEAR_SCREEN:
LXI H,0C000h ;Адрес начала экранного ОЗУ поместить в "HL"
Pro: MVI M,0 ;Записать нули по адресу "HL"
INX H ;Следующий адрес
MOV A,H ;Проверить конец экранного ОЗУ:
ORA A ;старший байт адреса = 00? - значит предыдущий
JNZ Pro ;адрес был FFFFh - т.е. конец экр.ОЗУ, если нет - продолжение
RET
;--------------------------------------------------------------------------------
;Заполнение плоскостей
SET_PLANE:
; вх a - номер плоскости 0..1
; вх b - значение заполнения
ora a ;Проверим номер плоскости
jnz SPL1 ;Если не нулевая - прыгаем дальше
LXI H,0E000h ;Заносим адрес нулевой плоскости в пару HL
SP_LOOP1:
mov a,b ;Записать значение заполнения в аккумулятор
MOV M,a ;Записать значение по адресу "HL"
INX H ;Следующий адрес
MOV A,H ;Проверить конец экранного ОЗУ:
ORA A ;старший байт адреса = 00? - значит предыдущий
JNZ SP_LOOP1 ;адрес был FFFFh - т.е. конец экр.ОЗУ и плоскости 0, если нет - продолжение
RET
SPL1:
cpi 1 ;Проверим номер плоскости
rnz ;Если не первая - выходим из процедуры
LXI H,0C000h ;Заносим адрес первой плоскости в пару HL
SP_LOOP2:
mov a,b ;Записать значение заполнения в аккумулятор
MOV M,a ;Записать значение по адресу "HL"
INX H ;Следующий адрес
MOV A,H ;Проверить конец первой страницы экранного ОЗУ:
cpi 0E0h ;старший байт адреса <> ef( старший байт адреса предыдущей страницы) - значит продолжение
jnz SP_LOOP2
ret
;------------------------------------------------
;Вывод спрайта
SPR_OUT:
; вх HL - адрес на экране( 0 пл.)
; вх BC - адрес спрайта
mov a,l
adi 15 ;Увеличваем вертикальную позицию спрайта на его высоту-1, так как спрайт выводится сверху вниз
mov l,a
push h ;Сохраняем адрес спрайта на экране (плоскость 0)
REPEAT 15 ;Выводим первый столбик для плоскости 0
ldax b
mov m,a
inx b
dcr l
END REPEAT
ldax b
mov m,a
inx b
pop h ;Востанавливаем адрес спрайта на экране (плоскость 0),
push h ;и снова сохраняем его
mov a,H ;Переходим в плоскость 1
sui 20h
mov h,a
REPEAT 15 ;Выводим первый столбик для плоскости 1
ldax b
mov m,a
inx b
dcr l
END REPEAT
ldax b
mov m,a
inx b
pop h ;Востанавливаем адрес спрайта на экране (плоскость 0)
inr h ;Переход на столбец вправо
push h ;Сохраняем новый адрес спрайта на экране (плоскость 0)
REPEAT 15 ;Выводим второй столбик для плоскости 0
ldax b
mov m,a
inx b
dcr l
END REPEAT
ldax b
mov m,a
inx b
pop h ;Востанавливаем адрес спрайта на экране (плоскость 0)
mov a,H ;Переходим в плоскость 1
sui 20h
mov h,a
REPEAT 15 ;Выводим второй столбик для плоскости 1
ldax b
mov m,a
inx b
dcr l
END REPEAT
ldax b
mov m,a
ret
;------------------------------------------------
;Очистка области 16 на 16 пикселей
CLEAR_AREA:
; вх HL - адрес области на экране( 0 пл.)
push h ;Сохраняем адрес области на экране (плоскость 0)
xra a ;Получаем ноль в аккумуляторе
REPEAT 7 ;Очищаем данные плоскости 0 "столбиком" по две строки за раз снизу вверх
mov m,a
inr h
mov m,a
inr l
mov m,a
dcr h
mov m,a
inr l
END REPEAT
mov m,a
inr h
mov m,a
inr l
mov m,a
dcr h
mov m,a
pop h ;Востанавливаем адрес области на экране (плоскость 0)
mov a,H ;Переходим в плоскость 1
sui 20h
mov h,a
xra a ;Получаем ноль в аккумуляторе
REPEAT 7 ;Очищаем данные плоскости 1 "столбиком" по две строки за раз снизу вверх
mov m,a
inr h
mov m,a
inr l
mov m,a
dcr h
mov m,a
inr l
END REPEAT
mov m,a
inr h
mov m,a
inr l
mov m,a
dcr h
mov m,a
ret
;----------------------------------------------------------------------------------
Border: DB 0 ;Цвет бордюра (сейчас соответствует цвету 0)
COLR0: DB 00h ;Палитра цветов
COLR1: DB 04h
COLR2: DB 18h
COLR3: DB 0c9h
COLR4: DB 00h ;Повтор цветов для отключения плоскостей 2 и 3
COLR5: DB 04h
COLR6: DB 18h
COLR7: DB 0c9h
COLR8: DB 00h
COLR9: DB 04h
COLR10: DB 18h
COLR11: DB 0c9h
COLR12: DB 00h
COLR13: DB 04h
COLR14: DB 18h
COLR15: DB 0c9h
;Данные спрайта
SPRITE:
;Первый столбик для плоскости 0
DB 00000001b
DB 00000001b
DB 00000001b
DB 00000000b
DB 00000000b
DB 00000000b
DB 00000101b
DB 00000010b
DB 00000100b
DB 00001001b
DB 00000010b
DB 00000100b
DB 00001001b
DB 00010010b
DB 00000100b
DB 00001001b
;Первый столбик для плоскости 1
DB 00000001b
DB 00000001b
DB 00000001b
DB 00000001b
DB 00000011b
DB 00000011b
DB 00000110b
DB 00000111b
DB 00000011b
DB 00000110b
DB 00001111b
DB 00001111b
DB 00010110b
DB 00011101b
DB 00111111b
DB 00010111b
;Второй столбик для плоскости 0
DB 00000000b
DB 00000000b
DB 00000000b
DB 00000000b
DB 00000000b
DB 10000000b
DB 00000000b
DB 01000000b
DB 10000000b
DB 00100000b
DB 01000000b
DB 10010000b
DB 00100000b
DB 01001000b
DB 10010000b
DB 00100000b
;Второй столбик для плоскости 1
DB 00000000b
DB 00000000b
DB 00000000b
DB 00000000b
DB 10000000b
DB 00000000b
DB 11000000b
DB 11000000b
DB 11000000b
DB 11000000b
DB 10100000b
DB 11110000b
DB 11110000b
DB 10110000b
DB 11101000b
DB 11110000b