Dla klawiatury: zezwalasz na przerwanie INT1, oraz ogólnie zezwalasz na przyjmowanie przerwań.
Aby przerwanie wystąpiło, na jednym z wejść bramki AND sterowanej z portów P0.4, P0.5, P0.6 powinien pojawić się sygnał niski, zatem już wiesz, że stan jałowy portów P0.4, P0.5, P0.6 jest stanem wysokim. Wymuszenie stanu niskiego zostaw dla keypada; w tym celu, jednorazowo, podczas inicjacji/reset ustaw porty P0.0 ... P0.3 w stan niski. Wciśnięcie przycisku na keypadzie spowoduje zwarcie jednego wejścia bramki AND do stanu niskiego, oraz zgłoszenie przerwania.
Prawdę mówiąc, ta bramka wcale nie jest potrzebna. W ZX-Spectrum matrycowa klawiatura jest sprawdzana co 20ms, tutaj też można tak zrobić. Timer zgłaszający przerwanie masz, w jego obsłudze sterujesz multiplekserem LED i nic nie stoi na przeszkodzie, by tam dodpisać podprogram odczytywania klawiatury, co 20ms, lub rzadziej.
Odczyt Twojej klawiatury polega na przesuwaniu logicznego zera w jednej kolumnie matrycy i odczytaniu stanu drugiej kolumny, dla każdej pozycji zera w pierwszej kolumnie. Odczytanie zerowego bitu jest równoznaczne ze zwarciem na przecięciu pozycji zerowego bitu, a pozycją przesuwanego zera.
Przykład dla klawiszy 1,2,3:
- pozostawiam zero TYLKO na wierszu z przyciskami 1,2,3: orl P0,00000111b (zakładając, że poprzednio był tam stan jałowy ****0000b)
- odczytuję stan portu P0 do jakiegoś rejestru Rn. Jeżeli odczytany bit Rn.4 jest wyzerowany, to klawisz 3 jest wciśnięty - ten z kolumny podłączonej do P0.4. Podobnie dla dwóch pozostałych kolumn: if Rn.5=0 then key "2" is down; if Rn.6=0 then key "1" is down.
Sprawdzenie następnej kolumny różni się tylko podpunktem 1. Zamiast rozkazu ORL, wstawiam SETB P0.3 oraz CLR P0.2 (klawisze 456), wymuszany stan portu P0 to ****1011b, potem ponownie odczyt stanu P0 - odczyt stanu klawiszy 456. Kolejny rząd klawiszy 789 wymaga przesunięcia stanu niskiego na P0.1 ... potem klawisze *0#, i ostatecznie ustawienie stanu jałowego: wyzerowanie P0.0...P0.3.
Pisząc obsługę przerwań, nie zapomnij zapisać stanu rejestów które modyfikujesz i jednoczeście używasz w innych funkcjach, które mogą zostać przerwane, oraz flag (m.in. C,Z,O,P) jeżeli się zmieniają. Zamiast PUSH-ować wiele razy, możesz podmienić bank rejestrów Rn na jeden z trzech dodatkowych, dzięki czemu jedną instrukcją (np. XRL) "zapisujesz" stan ośmiu rejestru R0-R7. Nie powiem Ci który pseudo-rejestr odpowiada za aktywny bank rejestrów, bo ściągę zostawiłem prawie 1000km stąd. Wracając z podprogramu przerwania musisz odtworzyć stan rejestrów i flag (np. ponowne XRL na rejestrze banku rejestrów), ewentualnie kilka rozkazów POP w odwrotnej kolejności jak PUSH, wyzerowanie flagi "pending interupt", o ile taka istnieje, dla przerwania INT1 (modyfikując stanami logicznymi na kolumnach matrycy możesz nieumyślnie złosić przerwanie poprzez tą zbyteczną bramkę AND) oraz powrót instrukcją reti.
Problem przerwania w przerwaniu nie jest aż tak poważny, o ile go wykryjesz. Procesor obsługując przerwanie int1, może przyjąć kolejne przewranie int1 (zapamiętać, że wystąpiło), ale uruchomi je dopiero, gdy aktualnie aktywny podprogram obsługi tego przerwania powróci. Grozi to zapętleniem (niech ktoś sprostuje, jeżeli się mylę). Wyłączając przerwanie int1 na czas manipulacji portami, które mogą wywołać sprzętowe przerwanie int1, rozwiązuje problem.