Blame | Last modification | View Log | Download | RSS feed
; контроллер 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> Таймер ANOP ;<4> не используетсяNOP ;<5> не используетсяJMP SC_KEY ;<6> Порт ANOP ;<7> Порт BNOP ;<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 ; Установка конфигурации порта AMOVL %B1,00000110BMOVL %B1,00011111BMOVL %B1,00000000BMOVL %B1,00000000BMOVL %B1,00000000B ; прерывание по отрицательному перепаду уровняJMP RESTSTART: LDR #A,0 ; Сегмент A - рабочие регистры портовLDR #B,18H ; Сегмент B - регистры конфигурации портовLDR #C,64 ; Сегмент С - переменныеLDR #D,80 ; Сегмент D - переменныеJSR INIT ; инициализируем портыJSR KEYINI ; инициализируем матрицу ZX-клавишJSR MOUSINI ; инициализируем мышкуJSR TIMER ; инициализируем прерывания от таймера;MOVL %D0,0JSR RDFLSHMAIN: BTTL %D5,0001BJNZ CLMJMP MAIN ; и есчо разок ...CLM: CMPL %D4,255JZ MAIN ; если мышь не была расползнана при инициализацииMOVL %A4,0 ; тормозим счетчик и запрещаем прерывания от негоJSR WAIT ; ожидаем готовность мышиMOVL %C3,11101011B ; код команды запроса состоянияJSR WR_SC ; засылаем в мышьJSR MORE ; принимаем ответCMPL %C3,11111010B ; все о.к. ?JNZ EXI ; видимо, нетJSR MORE ; ну а если все о.к., то принимаем еще 3 байтаLDR #B,72MOV %B7,%C3 ; ZZJSR MOREJSR MASHTBLDR #B,72ADD %B5,%C3 ; XXJSR MOREJSR MASHTBLDR #B,72ADD %B6,%C3 ; YYBISH %B7,1111B ; обрабатываем данные о нажатых кнопках мышиBICL %B7,1000BBTGL %B7,1111BBICL %D5,0001B ; CLEAR MOUSE INTERRUPTION BITJSR VYVOD ; закачиваем данные в ПЛИСMOVL %A4,00000110B ; подключаем регистр интервала (HI) к адресу 5MOVL %A5,2 ;EXI: MOVL %A4,00000011B; запускаем счетчик и разрешаем прерывания от негоJMP MAINMASHTB: MOV %D1,D0 ; грузим в D1 коэффициент замедления мышкиCMPL %D1,0JZ VJOPUCMPL %C3,0JZ VJOPUCMPL %C3,128JS MSH1 ; если меньше 128MOVL %D2,10000000B;MSH0: SHR %C3SHRA %D2DEC %D1JNZ MSH0SHL %D2 ; нужно установить столько старших разрядов, сколько; сдвигов было проделано (иначе стрелка будет; дергаться из-за того, что вместо уменьшения координат; произойдет их увеличение). В D2 теперь как раз и будет; установлено нужное количество старших разрядов.OR %C3,%D2RTSMSH1: MOV %D1,%D0MSH2: SHR %C3DEC %D1JNZ MSH2CMPL %C3,0JNZ VJOPUMOVL %C3,1VJOPU: RTSVYVOD: ; закачка данных в ПЛИСBICL %A1,0010B ; сбрасываем регистры в ПЛИСNOPNOPBISL %A1,0010B ; снимаем сигнал сбросаLDR #B,72MOV %A2,%B0 ; понеслась :-)JSR STROBEMOV %A2,%B1JSR STROBEMOV %A2,%B2JSR STROBEMOV %A2,%B3JSR STROBEMOV %A2,%B4JSR STROBEOR %B5,%D4MOV %A2,%B5JSR STROBEOR %B6,%D4MOV %A2,%B6JSR STROBEOR %B7,%D4MOV %A2,%B7JSR STROBEMOVL %A2,0LDR #B,18HRTSSTROBE: BISL %A1,0100B ; даем стробNOPNOPBICL %A1,0100B ; снимаем стробRTSSC_M: CLIEMOV %C3,%A4BISL %D5,0001BSTIERTISC_KEY: CLIE ; запрещаем прерыванияMOV %C3,%A1 ; снимаеи сигнал запроса прерывания +MOVL %A4,00000000B ; тормозим счетчик и запрещаем прерывания от негоPUSH #BLDR #B,18HMOVL %B1,00101011BMOVL %B1,00000111B ; разрешаем запись в PA0BICL %A1,0001B ; захватываем сигнал переключения на клавиатуруBISL %D5,0010B ; устанавливаем флаг прерывания от клавиатурыJSR KEYJSR VYVODLDR #B,18HMOVL %B1,00101011BMOVL %B1,00000110B ; запрещаем запись в PA0MOVL %A4,00000110B ; подключаем регистр интервала (HI) к адресу 5MOVL %A5,150 ; итак, получили полный коэффициентMOVL %A4,00000011B ; запускаем счетчик и разрешаем прерывания от негоPOP #BSTIERTI; ****KEY: MOVL %C4,0MOVL %C5,0JSR RD_SC ; Ура! Начинаем принимать скан-код!MOV %C4,%C3CMPL %C4,11100000B ; принимать второй байт скан-кода?JZ KEY1CMPL %C4,11100001BJZ KEY1JMP CONTTTKEY1: BISL %C5,0010B ; устанавливаем флажокJSR MOREMOV %C4,%C3CONTTT: CMPL %C4,7 ; если нажата кнопка F12, то даем RESETJZ RESTCMPL %C4,078HJNZ CONTTZJSR NMICONTTZ: CMPL %C4,0 ; если была ошибка, то считаем, что был; принят код отжатия. Понимаю, что лажа, но; почему-то в 99% случаев ошибки происходят; именно при приеме кода отжатия. Уж не знаю,; почему. Спишем на ламерство изобретателей; PS2 интерфейса (ну не признавать же свою; криворукостьJZ PODGONCMPL %C4,11110000B ; это был код отжатой клавиши?JZ PODGONCMPL %C4,11110001BJZ PODGONJMP CONTT0PODGON: BISL %C5,0001B ; устанавливаем флажокJSR MORE ; принимаем еще один скан-код - код отжатойMOV %C4,%C3 ; клавишиCONTT0: BTTL %C5,0010BJZ CONTTACONTT1: CMPL %C4,00010001B ; правый ALT = левый ALT = SYMBOL SHIFTJZ CONTT2EX5: CMPL %C4,74HJNZ EX6JSR SHIFTMOVL %C4,3EHEX6: CMPL %C4,6BHJNZ EX7JSR SHIFTMOVL %C4,2EHEX7: CMPL %C4,75H ; UPJNZ EX8JSR SHIFTMOVL %C4,3DHEX8: CMPL %C4,72H ; DOWNJNZ EX9JSR SHIFTMOVL %C4,36HEX9: CMPL %C4,71H ; BACKSPACE = DELJNZ CONTT2JSR BACKSPJMP CONTT2CONTTA: CMPL %C4,66H ; это клавиша BACKSPACE?JNZ CONTTDJSR BACKSP ; если да, то ставим в соответсвие CAPS+0CONTTD: CMPL %C4,59H ; правый SHIFT = левый SHIFT = CAPS SHIFTJNZ CONTTWMOVL %C4,12HCONTTW: CMPL %C4,0DH ; TAB = EDIT = CAPS + 0JNZ CONTEJSR SHIFTMOVL %C4,16HCONTE: CMPL %C4,58H ; CAPS LOCK = CAPS + 2JNZ CONTE1JSR SHIFTMOVL %C4,1EHCONTE1: CMPL %C4,049H ; точкаJNZ CONTE2JSR SSHIFTMOVL %C4,03AHCONTE2: CMPL %C4,041H ; запятаяJNZ CONTE3JSR SSHIFTMOVL %C4,031HCONTE3: CMPL %C4,055H ; =JNZ CONTE4JSR SSHIFTMOVL %C4,004BHCONTE4: CMPL %C4,04EH ; -JNZ CONTE5JSR SSHIFTMOVL %C4,03BHCONTE5: CMPL %C4,6JNZ CONTE6BTTL %C5,0001BJNZ CONTE6INC %D0 ; F2 - увеличения коэффициента замедления мышкиCONTE6: CMPL %C4,4JNZ CONTE7BTTL %C5,0001BJNZ CONTE7DEC %D0 ; F3 - уменьшение коэффициента замедления мышкиCMPL %D0,255JNZ CONTE7MOVL %D0,0CONTE7: CMPL %C4,5JNZ CONTT2JSR WRFLSH ; - F1 - сохранение коэффициента замедления во FLASH - памятьCONTT2: MDAL %C1,SCODEMDAH %C2,SCODE ; Грузим в регистр косвенной адресации адресMTPR #6,%C1 ; таблицы скан-кодов в памяти командMTPR #7,%C2 ; (вся хрень с автоинкрементом, доступ через D7)CCD: MOVL %C1,72CYCK0: MOVL %C6,8 ; 8 битMOVL %C2,11111110B ; стартовое значениеCYCK1: MOV %C3,D7 ; берем скан-код из таблицыCMP %C3,%C4 ; совпал с прочитанным?JZ SKEY ; если совпал то идем на SKEYSST 0001B ; устанавливаем флаг CRLC %C2 ; смотрим следующий вариантDEC %C6JNZ CYCK1INC %C1CMPL %C1,77 ; проверили все 5 наборов по 8 клавишJNZ CYCK0MOVL %C4,0RTSPPODGON:JSR KEYINIJMP PODGONSKEY: MTPR #4,%C1MOVL %C1,01000000BMTPR #5,%C1BTTL %C5,0001BJNZ RESKEYAND %D6,%C2 ; фиксируем нажатие клавишиMOVL %C4,0RTSRESKEY: NOT %C2OR %D6,%C2 ; фиксируем отжатие клавишиMOVL %C4,0RTS ; УсёMORE: MOVL %C0,222 ; будем ждать (3*45+3)*222 командWT0: MOVL %C1,45 ; если за это время не поступит новый байтWT1: MOV %C2,%A1 ; данных, то выходим по ошибкеBTTH %C2,0001B;JZ WT2DEC %C1JNZ WT1DEC %C0JNZ WT0;RTSWT2: JSR RD_SCRTSBACKSP: JSR SHIFT ; DEL = SHIFT+0 обрабатываем SHIFTMOVL %C4,45H ; подсовываем скан-код нуляRTSSHIFT: LDR #B,72BTTL %C5,0001BJNZ RSHIFTBICL %B0,0001B ; фиксируем нажатие клавишиLDR #B,18HRTSRSHIFT: BISL %B0,0001B ; фиксируем отжатие клавишиLDR #B,18HRTSSSHIFT: LDR #B,72BTTL %C5,0001BJNZ RSSHIFTBICH %B1,1000B ; фиксируем нажатие клавишиLDR #B,18HRTSRSSHIFT:BISH %B1,1000B ; фиксируем отжатие клавишиLDR #B,18HRTSNMI: BICL %A1,0010B ; даем сигнал сбросаMOVL %A2,1 ; даем DATA0 = 1 (теперь на выходе NMI ПЛИС; появится 0)NOPNOPNOPNOPMOVL %A2,0 ; убираем 1 на DATA0BISL %A1,0010B ; снимаем сигнал сбросаRTS; **************************************************************RD_SC: ; Процедура чтения байта скан-кодаMOVL %C3,0 ; Будем читать в C3. Начальное значение 0MOV %C1,%A1BTTL %C1,1000B ; проверяем стартовый бит; JNZ ERROR; скан-кодMOVL %C2,8 ; будем читать 8 битSCAN1: JSR WAITJSR WAIT1 ; идем на процедуру ожидания следующего тактаSHR %C3MOV %C1,%A1BTTL %C1,1000BJZ SCAN2BISH %C3,1000B ; если DATA=1, то ставим эту 1 в C3SCAN2: DEC %C2JNZ SCAN1JSR WAIT ; Байт скан-кода вроде бы прочитали, теперь надо принять бит; четности и затем стоповый бит.JSR WAIT1; На бит четности сразу же кладем, ибо нафиг он не нуженJSR WAITJSR WAIT1MOV %C1,A1BTTL %C1,1000B ; Проверяем стоповый бит; JZ ERRORJSR WAIT ; ждем прихода в исходное состояниеRTS; ******************************************************************WR_SC: ; Процедура передачи байта скан-кода; Чтобы перейти в режим передачи данных, нужно удерживать 0 на линии; CLK не менее 60 мкс. На всякий случай будем держать 0 80 мкс.; 80 это 320 команд (640 тактов) при F=8 МГцMOVL %B1,00101011B ; будем писать в подрегистр типа вывода порта AMOVL %B1,00011110BBICL %A1,1000BBICH %A1,0001B ; выдаем 0 в CLK и DATAMOVL %C0,64 ; Ждем 64*5=320 командREPL: NOPNOPNOPDEC %C0JNZ REPL;BISH %A1,0001B ; снимаем 0 CLKMOVL %B1,00101011B ; будем писать в подрегистр типа вывода порта AMOVL %B1,00001110B ; переводим CLK на чтениеBICL %A1,1000BMOVL %C0,0 ; это будет счетчик единичных битов для формирования; бита четностиW1: MOVL %C7,255 ; ждем 0, т.е. начала тактирования процессаW2: BTTH %A1,0001BJZ E1NOPNOPNOPNOPNOPDEC %C7JNZ W2E1:; передачи данных девайсомMOVL %C2,8 ; будем передавать 8 битWR_0: ; передаем битWR1: SHR %C3JC WR2BICL %A1,1000BJMP WR3WR2: BISL %A1,1000BINC %C0 ; добавляем 1 к счетчикуWR3: BTTH %A1,0001B ; ждем 1 на CLKJZ WR3;; ожидаем защелкивания бита данных девайсом и его; готовности к приему следующего бита (0 на CLK)MOVL %C7,255WW2: BTTH %A1,0001BJZ EE1NOPNOPNOPNOPNOPDEC %C7JNZ WW2EE1: DEC %C2JNZ WR_0; Байт скан-кода вроде бы передали, теперь надо передать бит; четности и затем принять стоповый бит. Мля, ну какие же; ламеры придумали этот PS2 интерфейс :-EMOVL %C3,0SHR %C0 ; Значение бита четности берем из 0-го разряда %C0JNC BCNZ ; Если бит четности =0BISL %C3,1 ; если бит четности =1BCNZ: ; передаемSHR %C3JC WRR2BICL %A1,1000BJMP WRR3WRR2: BISL %A1,1000BINC %C0 ; добавляем 1 к счетчикуWRR3: ; ждем 1BTTH %A1,0001BJZ WRR3;; ожидаем защелкивания бита данных девайсом и его; готовности к приему следующего битаMOVL %C7,255 ; ждем 0WWA1: BTTH %A1,0001BJZ EEXNOPNOPNOPNOPNOPDEC %C7JNZ WWA1EEX: MOVL %B1,00101011B ; будем писать в подрегистр типа вывода порта AMOVL %B1,00000110B ; переводим DATA на чтениеIT: ; ждем 1BTTH %A1,0001BJZ IT;JSR WAIT1 ; ожидаем приход стопового битаMOVL %C7,255 ; ждем 0AWA1: BTTH %A1,0001BJZ WEXNOPNOPNOPNOPNOPDEC %C7JNZ AWA1WEX: MOV %C1,%A1BTTL %C1,1000B ; проверяем стоповый бит; только непонятно, зачемHT: ; ждем 1BTTH %A1,0001BJZ HT;RTSERROR: MOVL %C3,0WAIT: MOV %C1,%A1 ; ждем 1BTTH %C1,0001BJZ WAIT;RTSWAIT1: MOVL %C7,255 ; ждем 0WAI1: MOV %C1,%A1BTTH %C1,0001BJZ EXIT1NOPNOPNOPNOPNOPDEC %C7JNZ WAI1JMP ERROREXIT1: RTS; ********************WRFLSH: LDR #B,56 ; адрес регистра управления блока ЭСППЗУ данныхMOVL %B1,0 ; адрес ячейки = 0 (используем только один байт)MOV %B7,%D0MOVL %B0,00000001BWRF1: BTTL %B0,0001BJNZ WRF1 ; ждем, пока происходит записьLDR #B,18HRTS; ********************RDFLSH: LDR #B,56 ; адрес регистра управления блока ЭСППЗУ данныхMOVL %B1,0 ; адрес ячейки = 0 (используем только один байт)MOVL %B0,00000010BRDF1: BTTL %B0,0010BJNZ RDF1 ; ждем, пока происходит чтениеMOV %D0,%B7LDR #B,18HRTS; ********************INIT: MOVL %B1,00111011B ; Установка конфигурации порта AMOVL %B1,00000110BMOVL %B1,00011111BMOVL %B1,00000000BMOVL %B1,00000000BMOVL %B1,00000001B ; прерывание по отрицательному перепаду уровня; на линии CLK_KEY для опроса клавиатурыSTIEMOVL %B2,00011011B ; Установка конфигурации порта BMOVL %B2,11111111BMOVL %B2,11111111BMOVL %B2,0MOVL %B2,0MOVL %B2,0BISL %A1,0010B ; выдаем 1 на линию сброса регистров в ПЛИСBICL %A1,0100B ; выдаем 0 на линию STROBEMOVL %D5,0 ; флажок (потом пригодится)RTSKEYINI: ; инициализация матрицы клавиатурыLDR #B,72 ; весь буфер из 5-и байтов заполняемMOVL %B0,11111111B ; значениями 255MOVL %B1,11111111BMOVL %B2,11111111BMOVL %B3,11111111BMOVL %B4,11111111BLDR #B,18HJSR VYVODRTS; здесь надо бы отключать клавиатуру - потом поправлюMOUSINI:MOVL %D4,0LDR #B,72MOVL %B5,125MOVL %B6,125MOVL %B7,255LDR #B,18HJSR WAIT ; мышь подключили. Теперь ожидаем ее готовность; Теперь нужно передать в мышь команду запроса; ее состояния (здесь неудобно использовать; потоковый режим, хотя, в принципе, можно)MOVL %C3,11110000B ; код команды перехода в REMOTE MODEJSR WR_SCJSR MORE ; принимаем код подтвержденияCMPL %C3,11111010BJNZ MOUSOFF ; ERRORMOVL %C3,11110011BJSR WR_SCJSR MOREMOVL %C3,40JSR WR_SCJSR MORERTSMOUSOFF:MOVL %D4,255RTSTIMER: ; мышку будем опрашивать по прерываниям от таймераMOVL %A4,00010010B ; подключаем регистр конфигурации к адресу 5MOVL %A5,01001110B ; задаем коэффициент деления тактовой частоты 1/128MOVL %A4,00000010B ; подключаем регистр интервала (LOW) к адресу 5MOVL %A5,113MOVL %A4,00000110B ; подключаем регистр интервала (HI) к адресу 5MOVL %A5,2 ; итак, получили полный коэффициент; деления 128*(2?4?*256+113)=80000, прерывания от; таймера будут идти с частотой Fтакт/80000=100 ГцMOVL %A4,00000011B ; пускаем таймер на счетSTIE ; разрешаем прерыванияRTSREST: BICL %A1,0010BNOPNOPBISL %A1,0100BMOVL %C3,255TRMZ0: MOVL %C4,255TRMZ1: MOVL %C5,30TRMZ2: DEC %C5JNZ TRMZ2DEC %C4JNZ TRMZ1DEC %C3JNZ TRMZ0BICL %A1,0100BNOPNOPBISL %A1,0010BRSTJMP STARTSTERR: RSTJMP STARTSCODE: .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.END;