Blame | Last modification | View Log | Download | RSS feed | ?url?
; контроллер PS2 клавиатуры/мыши для ZX-Spectrum
; by KingOfEvil, год 2oo7 от р.х.
;
; Тактовая частота микроконтроллера 8 МГц
; Назначение выводов микроконтроллера:
;
; PS2: PA4 - CLK
; PA3 - DATA
;
; PB7..PB0 - данные
;
; PA0 - если 0, то подключить клавиатуру к PS2 интерфейсу
; если 1, то подключена мышь
; PA1 - сброс регистров в ПЛИС
; PA2 - строб для приема информации с PB7..PB0 (прием по переднему фронту
; в сдедующей последовательности: клавиатура, мышь X, мышь Y, мышь кн.
; Через PB7..PB0 будет передано 8 байтов, которые нужно принимать в ПЛИС
; последовательно с каждым фронтом импульса на PA2)
; область векторов прерываний
jmp start0 ;<0> Начальный пуск программы
nop ;<1> Сторожевой таймер
jmp sterr ;<2> выход за границу стека
jmp sc_m ;<3> Таймер A
nop ;<4> не используется
nop ;<5> не используется
jmp sc_key ;<6> Порт A
nop ;<7> Порт B
nop ;<8> не используется
nop ;<9> не используется
nop ;<10> не используется
nop ;<11> не используется
nop ;<12> не используется
nop ;<13> не используется
nop ;<14> не используется
rst ;<15> Завершение записи в флэш
start0: ldr #a,0 ; Сегмент A - рабочие регистры портов
ldr #b,18h ; Сегмент B - регистры конфигурации портов
ldr #c,64 ; Сегмент С - переменные
ldr #d,80 ; Сегмент D - переменные
movl %b1,00111011b ; Установка конфигурации порта A
movl %b1,00000110b
movl %b1,00011111b
movl %b1,00000000b
movl %b1,00000000b
movl %b1,00000000b ; прерывание по отрицательному перепаду уровня
movl %b2,00011011b ; Установка конфигурации порта B
movl %b2,11111111b
movl %b2,11111111b
movl %b2,0
movl %b2,0
movl %b2,0
bisl %a1,0010b ; выдаем 1 на линию сброса регистров в ПЛИС
bicl %a1,0100b ; выдаем 0 на линию strobe
jsr keyini
bicl %a1,0010b
nop
nop
bisl %a1,0100b
jsr mousini
jsr rezet
jsr timer
start: ldr #a,0 ; Сегмент A - рабочие регистры портов
ldr #b,18h ; Сегмент B - регистры конфигурации портов
ldr #c,64 ; Сегмент С - переменные
ldr #d,80 ; Сегмент D - переменные
jsr init ; инициализируем порты
jsr keyini
jsr timer ; инициализируем прерывания от таймера
;movl %d0,0
jsr rdflsh
main: bicl %d5,0010b
bttl %d5,0001b
jnz clm
jmp main ; и есчо разок ...
clm: cmpl %d4,255
jz main ; если мышь не была расползнана при инициализации
movl %a4,0 ; тормозим счетчик и запрещаем прерывания от него
jsr wait ; ожидаем готовность мыши
movl %c3,11101011b ; код команды запроса состояния
jsr wr_sc ; засылаем в мышь
jsr more ; принимаем ответ
cmpl %c3,11111010b ; все о.к. ?
jnz clm ; видимо, нет
jsr more ; ну а если все о.к., то принимаем еще 3 байта
ldr #b,72
mov %b7,%c3 ; zz
jsr more
jsr mashtb
ldr #b,72
add %b5,%c3 ; xx
jsr more
jsr mashtb
ldr #b,72
add %b6,%c3 ; yy
bish %b7,1111b ; обрабатываем данные о нажатых кнопках мыши
bicl %b7,1000b
btgl %b7,1111b
bicl %d5,0001b ; clear mouse interruption bit
jsr vyvod ; закачиваем данные в ПЛИС
bttl %d5,0010b
jnz exi1
exi: jsr timer
exi1: movl %d5,0
movl %a4,00000011b
jmp main
mashtb: mov %d1,d0 ; грузим в d1 коэффициент замедления мышки
cmpl %d1,0
jz vjopu
cmpl %c3,0
jz vjopu
cmpl %c3,128
js msh1 ; если меньше 128
movl %d2,10000000b;
msh0: shr %c3
shra %d2
dec %d1
jnz msh0
shl %d2 ; нужно установить столько старших разрядов, сколько
; сдвигов было проделано (иначе стрелка будет
; дергаться из-за того, что вместо уменьшения координат
; произойдет их увеличение). В d2 теперь как раз и будет
; установлено нужное количество старших разрядов.
or %c3,%d2
rts
msh1: mov %d1,%d0
msh2: shr %c3
dec %d1
jnz msh2
cmpl %c3,0
jnz vjopu
movl %c3,1
vjopu: rts
vyvod: ; закачка данных в ПЛИС
bicl %a1,0010b ; сбрасываем регистры в ПЛИС
nop
nop
bisl %a1,0010b ; снимаем сигнал сброса
ldr #b,72
mov %a2,%b0 ; понеслась :-)
jsr strobe
mov %a2,%b1
jsr strobe
mov %a2,%b2
jsr strobe
mov %a2,%b3
jsr strobe
mov %a2,%b4
jsr strobe
or %b5,%d4
mov %a2,%b5
jsr strobe
or %b6,%d4
mov %a2,%b6
jsr strobe
or %b7,%d4
mov %a2,%b7
jsr strobe
movl %a2,0
ldr #b,18h
rts
strobe: bisl %a1,0100b ; даем строб
nop
nop
bicl %a1,0100b ; снимаем строб
rts
sc_m: clie
mov %c3,%a4
bisl %d5,0001b
stie
rti
sc_key: clie ; запрещаем прерывания
mov %c3,%a1 ; снимаеи сигнал запроса прерывания +
; mov %c3,%a4
movl %a4,00000000b ; тормозим счетчик и запрещаем прерывания от него
; movl %a4,00000110b
; movl %a5,150
; movl %a4,0
push #b
ldr #b,18h
movl %b1,00101011b
movl %b1,00000111b ; разрешаем запись в PA0
bicl %a1,0001b ; захватываем сигнал переключения на клавиатуру
bisl %d5,0010b ; устанавливаем флаг прерывания от клавиатуры
jsr key
jsr vyvod
ldr #b,18h
movl %b1,00101011b
movl %b1,00000110b ; запрещаем запись в PA0
movl %a4,00000110b ; подключаем регистр интервала (hi) к адресу 5
movl %a5,150
movl %a4,00000011b ; запускаем счетчик и разрешаем прерывания от него
pop #b
stie
rti
; ****
key: movl %c4,0
movl %c5,0
jsr rd_sc ; Ура! Начинаем принимать скан-код!
mov %c4,%c3
cmpl %c4,11100000b ; принимать второй байт скан-кода?
jz key1
cmpl %c4,11100001b
jz key1
jmp conttt
key1: bisl %c5,0010b ; устанавливаем флажок
jsr more
mov %c4,%c3
conttt: cmpl %c4,7 ; если нажата кнопка f12, то даем reset
jz rezt
cmpl %c4,078h
jnz conttz
jsr nmi
conttz: cmpl %c4,0 ; если была ошибка, то считаем, что был
; принят код отжатия. Понимаю, что лажа, но
; почему-то в 99% случаев ошибки происходят
; именно при приеме кода отжатия. Уж не знаю,
; почему. Спишем на ламерство изобретателей
; PS2 интерфейса (ну не признавать же свою
; криворукость ;-) )
jz podgon
cmpl %c4,11110000b ; это был код отжатой клавиши?
jz podgon
cmpl %c4,11110001b
jz podgon
jmp contt0
podgon: bisl %c5,0001b ; устанавливаем флажок
jsr more ; принимаем еще один скан-код - код отжатой
mov %c4,%c3 ; клавиши
contt0: bttl %c5,0010b
jz contta
contt1: cmpl %c4,00010001b ; правый alt = левый alt = symbol shift
jz contt2
ex5: cmpl %c4,74h
jnz ex6
jsr shift
movl %c4,3eh
ex6: cmpl %c4,6bh
jnz ex7
jsr shift
movl %c4,2eh
ex7: cmpl %c4,75h ; up
jnz ex8
jsr shift
movl %c4,3dh
ex8: cmpl %c4,72h ; down
jnz ex9
jsr shift
movl %c4,36h
ex9: cmpl %c4,71h ; backspace = del
jnz contt2
jsr backsp
jmp contt2
contta: cmpl %c4,66h ; это клавиша backspace?
jnz conttd
jsr backsp ; если да, то ставим в соответсвие caps+0
conttd: cmpl %c4,59h ; правый shift = левый shift = caps shift
jnz conttw
movl %c4,12h
conttw: cmpl %c4,0dh ; TAB = EDIT = caps + 0
jnz conte
jsr shift
movl %c4,16h
conte: cmpl %c4,58h ; Caps lock = caps + 2
jnz conte1
jsr shift
movl %c4,1eh
conte1: cmpl %c4,049h ; точка
jnz conte2
jsr sshift
movl %c4,03ah
conte2: cmpl %c4,041h ; запятая
jnz conte3
jsr sshift
movl %c4,031h
conte3: cmpl %c4,055h ; =
jnz conte4
jsr sshift
movl %c4,004bh
conte4: cmpl %c4,04eh ; -
jnz conte5
jsr sshift
movl %c4,03bh
conte5: cmpl %c4,6
jnz conte6
bttl %c5,0001b
jnz conte6
inc %d0 ; f2 - увеличения коэффициента замедления мышки
conte6: cmpl %c4,4
jnz conte7
bttl %c5,0001b
jnz conte7
dec %d0 ; f3 - уменьшение коэффициента замедления мышки
cmpl %d0,255
jnz conte7
movl %d0,0
conte7: cmpl %c4,5
jnz contt2
jsr wrflsh ; - f1 - сохранение коэффициента замедления во flash - память
contt2: mdal %c1,scode
mdah %c2,scode ; Грузим в регистр косвенной адресации адрес
mtpr #6,%c1 ; таблицы скан-кодов в памяти команд
mtpr #7,%c2 ; (вся хрень с автоинкрементом, доступ через d7)
ccd: movl %c1,72
cyck0: movl %c6,8 ; 8 бит
movl %c2,11111110b ; стартовое значение
cyck1: mov %c3,d7 ; берем скан-код из таблицы
cmp %c3,%c4 ; совпал с прочитанным?
jz skey ; если совпал то идем на skey
sst 0001b ; устанавливаем флаг c
rlc %c2 ; смотрим следующий вариант
dec %c6
jnz cyck1
inc %c1
cmpl %c1,77 ; проверили все 5 наборов по 8 клавиш
jnz cyck0
movl %c4,0
rts
ppodgon:jsr keyini
jmp podgon
skey: mtpr #4,%c1
movl %c1,01000000b
mtpr #5,%c1
bttl %c5,0001b
jnz reskey
and %d6,%c2 ; фиксируем нажатие клавиши
movl %c4,0
rts
reskey: not %c2
or %d6,%c2 ; фиксируем отжатие клавиши
movl %c4,0
rts ; Усё
more: movl %c0,222 ; будем ждать (3*45+3)*222 команд
wt0: movl %c1,45 ; если за это время не поступит новый байт
wt1: mov %c2,%a1 ; данных, то выходим по ошибке
btth %c2,0001b;
jz wt2
dec %c1
jnz wt1
dec %c0
jnz wt0;
rts
wt2: jsr rd_sc
rts
backsp: jsr shift ; del = shift+0 обрабатываем shift
movl %c4,45h ; подсовываем скан-код нуля
rts
shift: ldr #b,72
bttl %c5,0001b
jnz rshift
bicl %b0,0001b ; фиксируем нажатие клавиши
ldr #b,18h
rts
rshift: bisl %b0,0001b ; фиксируем отжатие клавиши
ldr #b,18h
rts
sshift: ldr #b,72
bttl %c5,0001b
jnz rsshift
bich %b1,1000b ; фиксируем нажатие клавиши
ldr #b,18h
rts
rsshift:bish %b1,1000b ; фиксируем отжатие клавиши
ldr #b,18h
rts
nmi: bicl %a1,0010b ; даем сигнал сброса
movl %a2,1 ; даем data0 = 1 (теперь на выходе nmi ПЛИС
; появится 0)
nop
nop
nop
nop
movl %a2,0 ; убираем 1 на data0
bisl %a1,0010b ; снимаем сигнал сброса
rts
; **************************************************************
rd_sc: ; Процедура чтения байта скан-кода
movl %c3,0 ; Будем читать в c3. Начальное значение 0
mov %c1,%a1
bttl %c1,1000b ; проверяем стартовый бит
; jnz error ; если не 0, то кладем на этот байт скан-кода, да и на весь
; скан-код
movl %c2,8 ; будем читать 8 бит
scan1: jsr wait
jsr wait1 ; идем на процедуру ожидания следующего такта
shr %c3
mov %c1,%a1
bttl %c1,1000b
jz scan2
bish %c3,1000b ; если data=1, то ставим эту 1 в c3
scan2: dec %c2
jnz scan1
jsr wait ; Байт скан-кода вроде бы прочитали, теперь надо принять бит
; четности и затем стоповый бит.
jsr wait1; На бит четности сразу же кладем, ибо нафиг он не нужен
jsr wait
jsr wait1
mov %c1,a1
bttl %c1,1000b ; Проверяем стоповый бит
; jz error ; если он =0, то кладем на этот скан-код. Видимо, была ошибка
jsr wait ; ждем прихода в исходное состояние
rts
; ******************************************************************
wr_sc: ; Процедура передачи байта скан-кода
; Чтобы перейти в режим передачи данных, нужно удерживать 0 на линии
; clk не менее 60 мкс. На всякий случай будем держать 0 80 мкс.
; 80 это 320 команд (640 тактов) при F=8 МГц
movl %b1,00101011b ; будем писать в подрегистр типа вывода порта A
movl %b1,00011110b
bich %a1,0001b ; выдаем 0 в clk и data
bicl %a1,1000b
movl %c0,64 ; Ждем 64*5=320 команд
repl: nop
nop
nop
nop ; было 3
nop
dec %c0
jnz repl;
bish %a1,0001b ; снимаем 0 clk
movl %b1,00101011b ; будем писать в подрегистр типа вывода порта A
movl %b1,00001110b ; переводим clk на чтение
bicl %a1,1000b
movl %c0,1 ; это будет счетчик единичных битов для формирования
; бита четности
w1: movl %c7,255 ; ждем 0, т.е. начала тактирования процесса
w2: btth %a1,0001b
jz e1
nop
nop
nop
nop
nop
dec %c7
jnz w2
e1:
; передачи данных девайсом
movl %c2,8 ; будем передавать 8 бит
wr_0: ; передаем бит
wr1: shr %c3
jc wr2
bicl %a1,1000b
jmp wr3
wr2: bisl %a1,1000b
inc %c0 ; добавляем 1 к счетчику
wr3: btth %a1,0001b ; ждем 1 на clk
jz wr3;
; ожидаем защелкивания бита данных девайсом и его
; готовности к приему следующего бита (0 на clk)
movl %c7,255
ww2: btth %a1,0001b
jz ee1
nop
nop
nop
nop
nop
dec %c7
jnz ww2
ee1: dec %c2
jnz wr_0
; Байт скан-кода вроде бы передали, теперь надо передать бит
; четности и затем принять стоповый бит. Мля, ну какие же
; ламеры придумали этот ps2 интерфейс :-E
movl %c3,0
shr %c0 ; Значение бита четности берем из 0-го разряда %c0
jnc bcnz ; Если бит четности =0
bisl %c3,1 ; если бит четности =1
bcnz: ; передаем
shr %c3
jc wrr2
bicl %a1,1000b
jmp wrr3
wrr2: bisl %a1,1000b
inc %c0 ; добавляем 1 к счетчику
wrr3: ; ждем 1
btth %a1,0001b
jz wrr3;
; ожидаем защелкивания бита данных девайсом и его
; готовности к приему следующего бита
movl %c7,255 ; ждем 0
wwa1: btth %a1,0001b
jz eex
nop
nop
nop
nop
nop
dec %c7
jnz wwa1
eex: movl %b1,00101011b ; будем писать в подрегистр типа вывода порта A
movl %b1,00000110b ; переводим data на чтение
it: ; ждем 1
btth %a1,0001b
jz it;
jsr wait1 ; ожидаем приход стопового бита
movl %c7,255 ; ждем 0
awa1: btth %a1,0001b
jz wex
nop
nop
nop
nop
nop
dec %c7
jnz awa1
wex: mov %c1,%a1
bttl %c1,1000b ; проверяем стоповый бит
; только непонятно, зачем ;)
ht: ; ждем 1
btth %a1,0001b
jz ht;
rts
error: movl %c3,0
wait: mov %c1,%a1 ; ждем 1
btth %c1,0001b
jz wait;
rts
wait1: movl %c7,255 ; ждем 0
wai1: mov %c1,%a1
btth %c1,0001b
jz exit1
nop
nop
nop
nop
nop
dec %c7
jnz wai1
jmp error
exit1: rts
; ********************
wrflsh: ldr #b,56 ; адрес регистра управления блока ЭСППЗУ данных
movl %b1,0 ; адрес ячейки = 0 (используем только один байт)
mov %b7,%d0
movl %b0,00000001b
wrf1: bttl %b0,0001b
jnz wrf1 ; ждем, пока происходит запись
ldr #b,18h
rts
; ********************
rdflsh: ldr #b,56 ; адрес регистра управления блока ЭСППЗУ данных
movl %b1,0 ; адрес ячейки = 0 (используем только один байт)
movl %b0,00000010b
rdf1: bttl %b0,0010b
jnz rdf1 ; ждем, пока происходит чтение
mov %d0,%b7
ldr #b,18h
rts
; ********************
init: movl %b1,00111011b ; Установка конфигурации порта A
movl %b1,00000110b
movl %b1,00011111b
movl %b1,00000000b
movl %b1,00000000b
movl %b1,00000001b ; прерывание по отрицательному перепаду уровня
; на линии clk_key для опроса клавиатуры
stie
movl %b2,00011011b ; Установка конфигурации порта B
movl %b2,11111111b
movl %b2,11111111b
movl %b2,0
movl %b2,0
movl %b2,0
bisl %a1,0010b ; выдаем 1 на линию сброса регистров в ПЛИС
bicl %a1,0100b ; выдаем 0 на линию strobe
movl %d5,0 ; флажок (потом пригодится)
rts
keyini: ; инициализация матрицы клавиатуры
ldr #b,72 ; весь буфер из 5-и байтов заполняем
movl %b0,11111111b ; значениями 255
movl %b1,11111111b
movl %b2,11111111b
movl %b3,11111111b
movl %b4,11111111b
ldr #b,18h
jsr vyvod
rts
; здесь надо бы отключать клавиатуру - потом поправлю
mousini:clie ; di
ldr #b,72
movl %b5,125
movl %b6,127
movl %b7,255
ldr #b,18h
jsr rmz00
jsr wait
; мышь подключили. Теперь ожидаем ее готовность
; Теперь нужно передать в мышь команду запроса
; ее состояния (здесь неудобно использовать
; потоковый режим, хотя, в принципе, можно)
movl %d4,3
mr:
movl %c3,0ffh
jsr wr_sc
jsr rmz00
jsr more
jsr more
mrr: movl %c3,0f4h
jsr wr_sc
jsr more
movl %c3,11110000b ; код команды перехода в Remote mode
jsr wr_sc
jsr more ; принимаем код подтверждения
cmpl %c3,11111010b
jz mrr1
dec %d4
jnz mr
jmp mousoff ;mousoff ; error
mrr1: movl %d4,0
; movl %c3,11110011b
; jsr wr_sc
; jsr more
; movl %c3,80
; jsr wr_sc
; jsr more
movl %c3,11101000b
jsr wr_sc
jsr more
movl %c3,0
jsr wr_sc
jsr more
rts
rezet: bicl %a1,0010b
nop
nop
bisl %a1,0100b
movl %c3,50
jsr rmz01
bicl %a1,0100b
nop
nop
bisl %a1,0010b
rts
rezt: jsr rezet
rst
jmp start
mousoff:movl %d4,255
ldr #b,72
movl %b5,255
movl %b6,255
movl %b7,255
ldr #b,18h
rts
timer: ; мышку будем опрашивать по прерываниям от таймера
movl %a4,00010010b ; подключаем регистр конфигурации к адресу 5
movl %a5,01001111b ; задаем коэффициент деления тактовой частоты 1/256
movl %a4,00000010b ; подключаем регистр интервала (low) к адресу 5
movl %a5,70
movl %a4,00000110b ; подключаем регистр интервала (hi) к адресу 5
movl %a5,0 ; итак, получили полный коэффициент
; деления 128*(2?4?*256+113)=80000, прерывания от
; таймера будут идти с частотой Fтакт/80000=100 Гц
movl %a4,00000011b ; пускаем таймер на счет
stie ; разрешаем прерывания
rts
sterr: rst
jmp main
scode: .byte 12h,1ch,15h,16h,45h,4dh,5ah,29h
.even; байт 0: cs,a,q,1,0,p,ent,space
.byte 1ah,1bh,1dh,1eh,46h,44h,4bh,11h
.even; байт 1: z,s,w,2,9,o,l,ss
.byte 22h,23h,24h,26h,3eh,43h,42h,3ah
.even; байт 2: x,d,e,3,8,i,k,m
.byte 21h,2bh,2dh,25h,3dh,3ch,3bh,31h
.even; байт 3: c,f,r,4,7,u,j,n
.byte 2ah,34h,2ch,2eh,36h,35h,33h,32h
.even; байт 4: v,g,t,5,6,y,h,b
rmz00: movl %c3,155
rmz01: jsr rmz0
dec %c3
jnz rmz01
rts
rmz0: movl %c4,255
rmz1: movl %c5,45
rmz2: dec %c5
jnz rmz2
dec %c4
jnz rmz1
rts
.end;