PTM – CLK

zad1

ljmp start

P5 equ 0F8H
P7 equ 0DBH
	
LCDstatus  equ 0FF2EH       ; adres do odczytu gotowosci LCD
LCDcontrol equ 0FF2CH       ; adres do podania bajtu sterujacego LCD
LCDdataWR  equ 0FF2DH       ; adres do podania kodu ASCII na LCD

// bajty sterujace LCD, inne dostepne w opisie LCD na stronie WWW
#define  HOME     0x80     // put cursor to second line  
#define  INITDISP 0x38     // LCD init (8-bit mode)  
#define  HOM2     0xc0     // put cursor to second line  
#define  LCDON    0x0e     // LCD nn, cursor off, blinking off
#define  CLEAR    0x01     // LCD display clear

// linie klawiatury - sterowanie na port P5
#define LINE_1		0x7f	// 0111 1111
#define LINE_2		0xbf	// 1011 1111
#define	LINE_3		0xdf	// 1101 1111
#define LINE_4		0xef	// 1110 1111
#define ALL_LINES	0x0f	// 0000 1111

ORG 000BH     				; obsluga przerwania
	MOV TH0, #3CH 			; przeladowanie
	MOV TL0, #0B0H 			; stalej timera na 50ms
	DEC R0        			; korekta licznika
	RETI          			; powr�t z przerwania

org 0100H
		
// macro do wprowadzenia bajtu sterujacego na LCD
LCDcntrlWR MACRO x          ; x � parametr wywolania macra � bajt sterujacy
           LOCAL loop       ; LOCAL oznacza ze etykieta loop moze sie powt�rzyc w programie
loop: MOV  DPTR,#LCDstatus  ; DPTR zaladowany adresem statusu
      MOVX A,@DPTR          ; pobranie bajtu z biezacym statusem LCD
      JB   ACC.7,loop       ; testowanie najstarszego bitu akumulatora
                            ; � wskazuje gotowosc LCD
      MOV  DPTR,#LCDcontrol ; DPTR zaladowany adresem do podania bajtu sterujacego
      MOV  A, x             ; do akumulatora trafia argument wywolania macra�bajt sterujacy
      MOVX @DPTR,A          ; bajt sterujacy podany do LCD � zadana akcja widoczna na LCD
      ENDM
	  
// macro do wypisania znaku ASCII na LCD, znak ASCII przed wywolaniem macra ma byc w A
LCDcharWR MACRO
      LOCAL tutu            ; LOCAL oznacza ze etykieta tutu moze sie powt�rzyc w programie
      PUSH ACC              ; odlozenie biezacej zawartosci akumulatora na stos
tutu: MOV  DPTR,#LCDstatus  ; DPTR zaladowany adresem statusu
      MOVX A,@DPTR          ; pobranie bajtu z biezacym statusem LCD
      JB   ACC.7,tutu       ; testowanie najstarszego bitu akumulatora
                            ; � wskazuje gotowosc LCD
      MOV  DPTR,#LCDdataWR  ; DPTR zaladowany adresem do podania bajtu sterujacego
      POP  ACC              ; w akumulatorze ponownie kod ASCII znaku na LCD
      MOVX @DPTR,A          ; kod ASCII podany do LCD � znak widoczny na LCD
      ENDM
	  
// macro do inicjalizacji wyswietlacza � bez parametr�w
init_LCD MACRO
         LCDcntrlWR #INITDISP ; wywolanie macra LCDcntrlWR � inicjalizacja LCD
         LCDcntrlWR #CLEAR    ; wywolanie macra LCDcntrlWR � czyszczenie LCD
         LCDcntrlWR #LCDON    ; wywolanie macra LCDcntrlWR � konfiguracja kursora
         ENDM
		 
// funkcja wypisania liczby dla potrzeb zegara
putdigitLCD:	mov b, #10
				div ab				; uzyskanie cyfry dziesiatek
				add a, #30H			; konwersja cyfry na kod ASCII
				acall putcharLCD
				mov a, b			; ladowanie cyfry jednosci
				add a, #30H			; konwersja na LCD
				acall putcharLCD
				ret

// funkcaj wypisywania znaku na LCD
putcharLCD:	LCDcharWR
			ret
		 

// wyznaczanie biezacej wartosci zegara i jego wyswietlanie na LCD
ZEGAR:		INC R7				; licznik sekund
			MOV A, R7			; obsluga sekund
			CLR C
			SUBB A, #60			; przepelnienie sekund
			JZ MINUTY
			LCDcntrlWR #HOME	; wyswietlenie calego zegara
			MOV A, R5			; godziny
			ACALL putdigitLCD
			MOV A, #":"			; separator
			ACALL putcharLCD
			MOV A, R6			; minuty
			ACALL putdigitLCD
			MOV A, #":"			; separator
			ACALL putcharLCD
			MOV A, R7			; sekundy
			ACALL putdigitLCD
			JMP FINAL
MINUTY:		MOV R7, #00H		; zerowanie sekund
			INC R6				; licznik minut
			MOV A, R6			; obsluga minut
			CLR C
			SUBB A, #60			; przepelnienie minut
			JZ GODZINY
			LCDcntrlWR #HOME	; wyswietlenie calego zegara
			MOV A, R5			; godziny
			ACALL putdigitLCD
			MOV A, #":"			; separator
			ACALL putcharLCD
			MOV A, R6			; minuty
			ACALL putdigitLCD
			MOV A, #":"			; separator
			ACALL putcharLCD
			MOV A, R7			; sekundy
			ACALL putdigitLCD
			JMP FINAL
GODZINY:	MOV R6, #00H		; zerowanie minut
			INC R5				; licznik godzin
			MOV A, R5
			CLR C
			SUBB A, #24			; przepelenienie godzin - doba
			JNZ EKRAN
			MOV R5, #00H		; zerowanie godzin
EKRAN:		LCDcntrlWR #HOME	; wyswietlenie calego zegara
			MOV A, R5			; godziny
			ACALL putdigitLCD
			MOV A, #":"			; separator
			ACALL putcharLCD
			MOV A, R6			; minuty
			ACALL putdigitLCD
			MOV A, #":"			; separator
			ACALL putcharLCD
			MOV A, R7			; sekundy
			ACALL putdigitLCD
FINAL:		RET

        ; program gl�wny
START:	init_LCD
		MOV TMOD, #01H 			; konfiguracja timera
		MOV TH0, #3CH 			; ladowanie
		MOV TL0, #0B0H 			; stalej timera na 50ms
		SETB TR0     			; timer start
		MOV IE, #82H  			; przerwania wlacz
		MOV R5, #00H			; inicjacja zegara
		MOV R6, #00H
		MOV R7, #0FFH
		ACALL ZEGAR				; wyswietlenie zainicjowanego zegara
		MOV A, #0FH
		MOV P1, A    			; zapalenie di�d
		MOV R0, #20 			; licznik odmierzen 20 x 50ms
CZEKAM: 		
		key_1:	mov r1, #LINE_1
			mov	a, r1
			mov	P5, a
			mov a, P7
			anl a, r1
			mov r2, a
			clr c
			subb a, r1
			jz key_2
			mov a, r2
			clr c
			subb a, #7eh
			jnz key_2
			MOV TH0, #3CH 			; ladowanie
			MOV TL0, #0B0H 			; stalej timera na 50ms
			SETB TR0      			; timer start

	key_2:	mov r1, #LINE_2
			mov	a, r1
			mov	P5, a
			mov a, P7
			anl a, r1
			mov r2, a
			clr c
			subb a, r1
			jz key_3
			mov a, r2
			clr c
			subb a, #0beh
			jnz key_3
			clr TR0
			
	key_3:	mov r1, #LINE_3
			mov	a, r1
			mov	P5, a
			mov a, P7
			anl a, r1
			mov r2, a
			clr c
			subb a, r1
			jz key_4
			mov a, r2
			clr c
			subb a, #0deh
			jnz key_4
			ljmp START
			
	key_4:	mov r1, #LINE_4
			mov	a, r1
			mov	P5, a
			mov a, P7
			anl a, r1
			mov r2, a
			clr c
			subb a, r1
			mov a, r2
		
		MOV A, R0   			; czekam, a timer
		JNZ CZEKAM   			; mierzy laczny czas 1s
		MOV R0, #20				; po zgloszeniu przerwania - ustawiam na nowo licznik odmierzen 20 x 50ms
		ACALL ZEGAR				; uruchomienie procedury oblugi i wyswietlenia zegara
		MOV A, P1  				; zmiana
		CPL A       			; swiecenia
		MOV P1, A
		
		   			; di�d
		JMP CZEKAM    			; czekam na kolejna sekunde
		NOP
		NOP
		NOP
		JMP $
END START
zad2

ljmp start

P5 equ 0F8H
P7 equ 0DBH
	
LCDstatus  equ 0FF2EH       ; adres do odczytu gotowosci LCD
LCDcontrol equ 0FF2CH       ; adres do podania bajtu sterujacego LCD
LCDdataWR  equ 0FF2DH       ; adres do podania kodu ASCII na LCD

// bajty sterujace LCD, inne dostepne w opisie LCD na stronie WWW
#define  HOME     0x80     // put cursor to second line  
#define  INITDISP 0x38     // LCD init (8-bit mode)  
#define  HOM2     0xc0     // put cursor to second line  
#define  LCDON    0x0e     // LCD nn, cursor off, blinking off
#define  CLEAR    0x01     // LCD display clear

// linie klawiatury - sterowanie na port P5
#define LINE_1		0x7f	// 0111 1111
#define LINE_2		0xbf	// 1011 1111
#define	LINE_3		0xdf	// 1101 1111
#define LINE_4		0xef	// 1110 1111
#define ALL_LINES	0x0f	// 0000 1111

ORG 000BH     				; obsluga przerwania
	MOV TH0, #3CH 			; przeladowanie
	MOV TL0, #0B0H 			; stalej timera na 50ms
	DEC R0        			; korekta licznika
	RETI          			; powr�t z przerwania

org 0100H
		
// macro do wprowadzenia bajtu sterujacego na LCD
LCDcntrlWR MACRO x          ; x � parametr wywolania macra � bajt sterujacy
           LOCAL loop       ; LOCAL oznacza ze etykieta loop moze sie powt�rzyc w programie
loop: MOV  DPTR,#LCDstatus  ; DPTR zaladowany adresem statusu
      MOVX A,@DPTR          ; pobranie bajtu z biezacym statusem LCD
      JB   ACC.7,loop       ; testowanie najstarszego bitu akumulatora
                            ; � wskazuje gotowosc LCD
      MOV  DPTR,#LCDcontrol ; DPTR zaladowany adresem do podania bajtu sterujacego
      MOV  A, x             ; do akumulatora trafia argument wywolania macra�bajt sterujacy
      MOVX @DPTR,A          ; bajt sterujacy podany do LCD � zadana akcja widoczna na LCD
      ENDM
	  
// macro do wypisania znaku ASCII na LCD, znak ASCII przed wywolaniem macra ma byc w A
LCDcharWR MACRO
      LOCAL tutu            ; LOCAL oznacza ze etykieta tutu moze sie powt�rzyc w programie
      PUSH ACC              ; odlozenie biezacej zawartosci akumulatora na stos
tutu: MOV  DPTR,#LCDstatus  ; DPTR zaladowany adresem statusu
      MOVX A,@DPTR          ; pobranie bajtu z biezacym statusem LCD
      JB   ACC.7,tutu       ; testowanie najstarszego bitu akumulatora
                            ; � wskazuje gotowosc LCD
      MOV  DPTR,#LCDdataWR  ; DPTR zaladowany adresem do podania bajtu sterujacego
      POP  ACC              ; w akumulatorze ponownie kod ASCII znaku na LCD
      MOVX @DPTR,A          ; kod ASCII podany do LCD � znak widoczny na LCD
      ENDM
	  
// macro do inicjalizacji wyswietlacza � bez parametr�w
init_LCD MACRO
         LCDcntrlWR #INITDISP ; wywolanie macra LCDcntrlWR � inicjalizacja LCD
         LCDcntrlWR #CLEAR    ; wywolanie macra LCDcntrlWR � czyszczenie LCD
         LCDcntrlWR #LCDON    ; wywolanie macra LCDcntrlWR � konfiguracja kursora
         ENDM

	 delay:	mov r1, #0FFH
	dwa:	mov r2, #0FFH
    trzy:	djnz r2, trzy
			djnz r1, dwa
			ret
		 
// funkcja wypisania liczby dla potrzeb zegara
putdigitLCD:	mov b, #10
				div ab				; uzyskanie cyfry dziesiatek
				add a, #30H			; konwersja cyfry na kod ASCII
				acall putcharLCD
				mov a, b			; ladowanie cyfry jednosci
				add a, #30H			; konwersja na LCD
				acall putcharLCD
				ret

// funkcaj wypisywania znaku na LCD
putcharLCD:	LCDcharWR
			ret
		 

// tablica przekodowania klawisze - ASCII w XRAM
keyascii_num:	mov dptr, #80EBH
			mov a, #0
			movx @dptr, a
			
			mov dptr, #8077H
			mov a, #1
			movx @dptr, a
			
			mov dptr, #807BH
			mov a, #2
			movx @dptr, a
			
			mov dptr, #807DH
			mov a, #3
			movx @dptr, a
			
			mov dptr, #80B7H
			mov a, #4
			movx @dptr, a
			
			mov dptr, #80BBH
			mov a, #5
			movx @dptr, a
			
			mov dptr, #80BDH
			mov a, #6
			movx @dptr, a
			
			mov dptr, #80D7H
			mov a, #7
			movx @dptr, a
			
			mov dptr, #80DBH
			mov a, #8
			movx @dptr, a
			
			mov dptr, #80DDH
			mov a, #9
			movx @dptr, a
			
			mov dptr, #807EH
			mov a, #"A"
			movx @dptr, a
			
			mov dptr, #80BEH
			mov a, #"B"
			movx @dptr, a
			
			mov dptr, #80DEH
			mov a, #"C"
			movx @dptr, a
			
			mov dptr, #80EEH
			mov a, #"D"
			movx @dptr, a
			
			mov dptr, #80E7H
			mov a, #"*"
			movx @dptr, a
			
			mov dptr, #80EDH
			mov a, #"#"
			movx @dptr, a
			
			ret

// wyznaczanie biezacej wartosci zegara i jego wyswietlanie na LCD
ZEGAR:		INC R7				; licznik sekund
			MOV A, R7			; obsluga sekund
			CLR C
			SUBB A, #60			; przepelnienie sekund
			JZ MINUTY
			LCDcntrlWR #HOME	; wyswietlenie calego zegara
			MOV A, R5			; godziny
			ACALL putdigitLCD
			MOV A, #":"			; separator
			ACALL putcharLCD
			MOV A, R6			; minuty
			ACALL putdigitLCD
			MOV A, #":"			; separator
			ACALL putcharLCD
			MOV A, R7			; sekundy
			ACALL putdigitLCD
			LCDcntrlWR #HOM2
			mov a, r4
			acall putdigitLCD
			mov a, r3
			acall putdigitLCD
			JMP FINAL
MINUTY:		MOV R7, #00H		; zerowanie sekund
			INC R6				; licznik minut
			MOV A, R6			; obsluga minut
			CLR C
			SUBB A, #60			; przepelnienie minut
			JZ GODZINY
			LCDcntrlWR #HOME	; wyswietlenie calego zegara
			MOV A, R5			; godziny
			ACALL putdigitLCD
			MOV A, #":"			; separator
			ACALL putcharLCD
			MOV A, R6			; minuty
			ACALL putdigitLCD
			MOV A, #":"			; separator
			ACALL putcharLCD
			MOV A, R7			; sekundy
			ACALL putdigitLCD
			LCDcntrlWR #HOM2
			mov a, r4
			acall putdigitLCD
			mov a, r3
			acall putdigitLCD
			JMP FINAL
GODZINY:	MOV R6, #00H		; zerowanie minut
			INC R5				; licznik godzin
			MOV A, R5
			CLR C
			SUBB A, #24			; przepelenienie godzin - doba
			JNZ EKRAN
			MOV R5, #00H		; zerowanie godzin
EKRAN:		LCDcntrlWR #HOME	; wyswietlenie calego zegara
			MOV A, R5			; godziny
			ACALL putdigitLCD
			MOV A, #":"			; separator
			ACALL putcharLCD
			MOV A, R6			; minuty
			ACALL putdigitLCD
			MOV A, #":"			; separator
			ACALL putcharLCD
			MOV A, R7			; sekundy
			ACALL putdigitLCD
			LCDcntrlWR #HOM2
			mov a, r4
			acall putdigitLCD
			mov a, r3
			acall putdigitLCD
FINAL:		RET

ostatniedwie:		mov a, r3
			mov r4, a
			movx a, @dptr
			mov r3, a
			acall delay
			ljmp key_1

        ; program gl�wny
START:	init_LCD
		MOV TMOD, #01H 			; konfiguracja timera
		MOV TH0, #3CH 			; ladowanie
		MOV TL0, #0B0H 			; stalej timera na 50ms
		SETB TR0     			; timer start
		MOV IE, #82H  			; przerwania wlacz
		MOV R5, #00H			; inicjacja zegara
		MOV R6, #00H
		MOV R7, #0FFH
		ACALL ZEGAR				; wyswietlenie zainicjowanego zegara
		MOV A, #0FH
		MOV P1, A    			; zapalenie di�d
		MOV R0, #20 			; licznik odmierzen 20 x 50ms
		acall keyascii_num
		mov r3, #0
		mov r4, #0
CZEKAM: 		
		key_1:	mov r1, #LINE_1
			mov	a, r1
			mov	P5, a
			mov a, P7
			anl a, r1
			mov r2, a
			clr c
			subb a, r1
			jz key_2
			mov a, r2
			mov dph, #80h
			mov dpl, a
			movx a, @dptr
			clr c
			subb a, #"A"
			jnz  ostatniedwie
			MOV TH0, #3CH 			; ladowanie
			MOV TL0, #0B0H 			; stalej timera na 50ms
			SETB TR0      			; timer start

	key_2:	mov r1, #LINE_2
			mov	a, r1
			mov	P5, a
			mov a, P7
			anl a, r1
			mov r2, a
			clr c
			subb a, r1
			jz key_3
			mov a, r2
			mov dph, #80h
			mov dpl, a
			movx a, @dptr
			clr c
			subb a, #"B"
			jnz  jumpostatniedwie
			clr TR0
			
	key_3:	mov r1, #LINE_3
			mov	a, r1
			mov	P5, a
			mov a, P7
			anl a, r1
			mov r2, a
			clr c
			subb a, r1
			jz key_4
			mov a, r2
			mov dph, #80h
			mov dpl, a
			movx a, @dptr
			clr c
			subb a, #"C"
			jnz  jumpostatniedwie
			ljmp START
			
	key_4:	mov r1, #LINE_4
			mov	a, r1
			mov	P5, a
			mov a, P7
			anl a, r1
			mov r2, a
			clr c
			subb a, r1
			mov a, r2
			mov dph, #80h
			mov dpl, a
			movx a, @dptr
			clr c
			subb a, #"#"
			jz ustawgodzine
			movx a, @dptr
			subb a, #"*"
			jz ustawminute
			movx a, @dptr
			jz jumpostatniedwie
		
		MOV A, R0   			; czekam, a timer
		JNZ CZEKAM   			; mierzy laczny czas 1s
		MOV R0, #20				; po zgloszeniu przerwania - ustawiam na nowo licznik odmierzen 20 x 50ms
		ACALL ZEGAR				; uruchomienie procedury oblugi i wyswietlenia zegara
		MOV A, P1  				; zmiana
		CPL A       			; swiecenia
		MOV P1, A
		
		   			; di�d
		JMP CZEKAM    			; czekam na kolejna sekunde
		NOP
		NOP
		NOP
		JMP $

	ustawgodzine:
		mov a, r4
		mov b, #10
		mul ab
		add a, r3
		mov b, #24
		div ab
		mov r5, b
		ljmp key_1
	ustawminute:
		mov a, r4
		mov b, #10
		mul ab
		add a, r3
		mov b, #60
		div ab
		mov r6, b
		ljmp key_1
	jumpostatniedwie:
		ljmp ostatniedwie
END START

; Skok nad wektorami przerwań do głównego programu

ljmp START

; — Definicje adresów sprzętowych (Mapowanie pamięci) —

P5         equ 0F8H          ; Port sterujący liniami klawiatury matrycowej

P7         equ 0DBH          ; Port odczytujący kolumny klawiatury matrycowej

LCDstatus  equ 0FF2EH        ; Adres odczytu flagi zajętości LCD (Busy Flag)

LCDcontrol equ 0FF2CH        ; Adres zapisu instrukcji do LCD

LCDdataWR  equ 0FF2DH        ; Adres zapisu danych (znaków) do LCD

; — Stałe sterujące LCD —

#define  HOME     0x80       ; Ustaw kursor na początku pierwszej linii

#define  INITDISP 0x38       ; Konfiguracja: 8-bit, 2 linie, font 5×7

#define  LCDON    0x0e       ; Włącz wyświetlacz, kursor włączony

#define  CLEAR    0x01       ; Czyść ekran

; — Maski dla klawiatury matrycowej (wybór wiersza) —

#define LINE_1    0x7f       ; 01111111B – aktywacja 1. wiersza

#define LINE_2    0xbf       ; 10111111B – aktywacja 2. wiersza

#define LINE_3    0xdf       ; 11011111B – aktywacja 3. wiersza

#define LINE_4    0xef       ; 11101111B – aktywacja 4. wiersza

; — WEKTORY PRZERWAŃ —

ORG 000BH                  ; Adres obsługi przerwania od Timer 0

    ; Przeładowanie licznika, aby uzyskać 50ms przy kwarcu 12MHz

    MOV TH0, #3CH          ; Starszy bajt (65536 – 50000 = 15536 = 3CB0H)

    MOV TL0, #0B0H         ; Młodszy bajt

    DEC R0                 ; Zmniejsz licznik programowy (z 20 do 0)

    RETI                   ; Powrót z przerwania

ORG 0100H                  ; Start właściwego programu

; — MAKRA (uproszczenie zapisu do LCD) —

; Makro wysyłające komendę do LCD

LCDcntrlWR MACRO x

    LOCAL loop

loop: MOV  DPTR,#LCDstatus  ; Sprawdź czy LCD jest gotowy

      MOVX A,@DPTR

      JB   ACC.7,loop       ; Jeśli bit 7 (Busy Flag) = 1, czekaj

      MOV  DPTR,#LCDcontrol ; Wyślij komendę pod adres kontrolny

      MOV  A, x

      MOVX @DPTR,A

      ENDM

; Makro wysyłające pojedynczy znak do LCD

LCDcharWR MACRO

      LOCAL tutu

      PUSH ACC              ; Zachowaj znak na stosie

tutu: MOV  DPTR,#LCDstatus  ; Czekaj na gotowość LCD

      MOVX A,@DPTR

      JB   ACC.7,tutu

      MOV  DPTR,#LCDdataWR  ; Wyślij znak pod adres danych

      POP  ACC              ; Przywróć znak do akumulatora

      MOVX @DPTR,A

      ENDM

; Makro inicjalizujące LCD

init_LCD MACRO

         LCDcntrlWR #INITDISP ; Ustaw tryb pracy

         LCDcntrlWR #CLEAR    ; Czyść ekran

         LCDcntrlWR #LCDON    ; Włącz obraz

         ENDM

; — FUNKCJE POMOCNICZE —

; Wypisuje liczbę 2-cyfrową (z zakresu 0-99) na LCD

putdigitLCD:

    MOV B, #10             ; Przygotuj dzielnik

    DIV AB                 ; A / 10 -> A (iloraz/dziesiątki), B (reszta/jedności)

    ADD A, #30H            ; Zamień cyfrę na kod ASCII

    ACALL putcharLCD       ; Wyświetl dziesiątki

    MOV A, B               ; Pobierz jedności

    ADD A, #30H            ; Zamień na ASCII

    ACALL putcharLCD       ; Wyświetl jedności

    RET

; Funkcja opakowująca makro zapisu znaku

putcharLCD:

    LCDcharWR

    RET

; Pętla opóźniająca (ok. 100-200ms) dla przycisków

delay:

    MOV R1, #0FFH

dwa: MOV R2, #0FFH

trzy: DJNZ R2, trzy        ; Podwójna pętla dająca zwłokę czasową

    DJNZ R1, dwa

    RET

; — OBSŁUGA LOGIKI ZEGARA (Inkrementacja czasu) —

ZEGAR:

    INC R7                 ; Zwiększ sekundy

    MOV A, R7

    CJNE A, #60, RYSUJ     ; Jeśli < 60, idź do rysowania

    MOV R7, #0             ; Jeśli 60, zeruj sekundy i…

    INC R6                 ; …zwiększ minuty

    MOV A, R6

    CJNE A, #60, RYSUJ     ; Jeśli < 60, rysuj

    MOV R6, #0             ; Jeśli 60, zeruj minuty i…

    INC R5                 ; …zwiększ godziny

    MOV A, R5

    CJNE A, #24, RYSUJ     ; Jeśli < 24, rysuj

    MOV R5, #0             ; Jeśli 24, zeruj godziny (nowa doba)

; Wyświetlanie formatu HH:MM:SS

RYSUJ:

    LCDcntrlWR #HOME       ; Ustaw kursor na początku

    MOV A, R5              ; Pobierz godziny

    ACALL putdigitLCD      ; Wyświetl

    MOV A, #”:”            ; Separator

    ACALL putcharLCD

    MOV A, R6              ; Pobierz minuty

    ACALL putdigitLCD      ; Wyświetl

    MOV A, #”:”            ; Separator

    ACALL putcharLCD

    MOV A, R7              ; Pobierz sekundy

    ACALL putdigitLCD      ; Wyświetl

    RET

; — PROGRAM GŁÓWNY —

START:

    init_LCD               ; Przygotuj LCD

    MOV TMOD, #01H         ; Timer 0: Tryb 1 (16-bitowy licznik)

    MOV TH0, #3CH          ; Załaduj czas (50ms)

    MOV TL0, #0B0H

    SETB ET0               ; Włącz przerwania od Timera 0

    SETB EA                ; Włącz globalną obsługę przerwań

    ; — Inicjalizacja stanu początkowego —

    MOV R5, #0             ; Godziny = 0

    MOV R6, #0             ; Minuty = 0

    MOV R7, #0             ; Sekundy = 0

    MOV R0, #20            ; Przerwanie co 50ms, więc 20 razy da 1 sekundę

    MOV R3, #0             ; Bufor klawiatury (dziesiątki)

    MOV R4, #0             ; Bufor klawiatury (jedności)

    ACALL RYSUJ            ; Wyświetl stan 00:00:00

LOOP:

    ; — ZADANIE 1: Przyciski sterujące (Port P3) —

    JB P3.2, SKIP_START    ; Jeśli P3.2=1 (nie wciśnięty), skocz

    SETB TR0               ; Jeśli wciśnięty, uruchom Timer (zegar startuje)

SKIP_START:

    JB P3.3, SKIP_STOP     ; Jeśli P3.3=1 (nie wciśnięty), skocz

    CLR TR0                ; Jeśli wciśnięty, zatrzymaj Timer (zegar stoi)

SKIP_STOP:

    JB P3.4, SKIP_PLUS     ; Przycisk +1h

    ACALL INC_HOUR         ; Inkrementuj godzinę

    ACALL delay            ; Czekaj (eliminacja drgań styków)

SKIP_PLUS:

    JB P3.5, SKIP_MINUS    ; Przycisk -1h

    ACALL DEC_HOUR         ; Dekrementuj godzinę

    ACALL delay

SKIP_MINUS:

    ; — ZADANIE 2: Obsługa klawiatury matrycowej —

    ACALL SCAN_KEYPAD

    ; — Odmierzanie pełnej sekundy —

    MOV A, R0              ; Sprawdź licznik 50ms

    JNZ LOOP               ; Jeśli > 0, wróć do początku pętli (sekunda nie minęła)

    MOV R0, #20            ; Jeśli 0, minęła sekunda – zresetuj licznik

    ACALL ZEGAR            ; Aktualizuj czas i wyświetlacz

    SJMP LOOP              ; Powtórz pętlę

; — PODPROGRAMY MODYFIKACJI CZASU —

INC_HOUR:

    INC R5                 ; Godziny + 1

    MOV A, R5

    CJNE A, #24, H_OK      ; Czy przekroczono 23?

    MOV R5, #0             ; Jeśli tak, wróć do 0

H_OK: ACALL RYSUJ          ; Odśwież ekran

    RET

DEC_HOUR:

    MOV A, R5

    JZ H_ZERO              ; Jeśli godziny = 0, skocz do ustawienia 23

    DEC R5                 ; W przeciwnym razie godziny – 1

    SJMP H_END

H_ZERO: MOV R5, #23        ; Przejście z 00 na 23

H_END: ACALL RYSUJ         ; Odśwież ekran

    RET

; Funkcja skanująca klawiaturę (tylko 1. wiersz dla przykładu)

SCAN_KEYPAD:

    MOV P5, #LINE_1        ; Wystaw 0 na pierwszy wiersz

    MOV A, P7              ; Odczytaj kolumny

    ANL A, #00001111B      ; Maskuj niepotrzebne bity

    CJNE A, #00001111B, GOT_KEY ; Jeśli któryś bit = 0, klawisz wciśnięty

    RET

GOT_KEY:

    ; Tu powinna być logika: R3 = R4 (przesunięcie), R4 = nowa cyfra

    ; Następnie sprawdzenie kursorów (* lub #) do zapisu HH lub MM

    ACALL delay

    RET

1.

 Program „zegar”, który powstał w czasie zajęć laboratoryjnych proszę

wzbogacić o możliwość jego „startu” i „stopu”. Należy także wprowadzić

regulację strefy czasowej: każde naciśnięcie klawisza – „godzina in

plus”, każde naciśnięcie klawisza – „godzina in minus”. Do obsługi tych

dodatkowych czterech funkcjonalności proszę użyć czterech guzików

podłączonych do P3.2.

Program „zegar”, który powstał w czasie zajęć laboratoryjnych proszę

wzbogacić o możliwość ustawienia pozycji godzin i minut przed

uruchomieniem odmierzania czasu. Proszę pamiętać, że zarówno pozycja

godzin, jak i minut wymaga wprowadzenia liczby dwucyfrowej w zakresie od

 00 do 23 dla godzin i – odpowiednio – od 00 do 59 dla minut. Trzeba

zatem „zbudować” z naciśniętych „po sobie” cyfr od 0 do 9 liczbę

dwucyfrową z kontrolą czy uzyskana liczba mieści się w wymaganym

zakresie. Zatwierdzenie wprowadzonego czasu guzikiem #.

dodaj do tego programu funkcjonalnosci z pkt 2

kod1 – rdy
; Skok nad wektorami przerwań do głównego programu
ljmp START

; --- Definicje adresów sprzętowych (Mapowanie pamięci) ---
P5         equ 0F8H          ; Port sterujący liniami klawiatury matrycowej
P7         equ 0DBH          ; Port odczytujący kolumny klawiatury matrycowej
LCDstatus  equ 0FF2EH        ; Adres odczytu flagi zajętości LCD (Busy Flag)
LCDcontrol equ 0FF2CH        ; Adres zapisu instrukcji do LCD
LCDdataWR  equ 0FF2DH        ; Adres zapisu danych (znaków) do LCD

; --- Stałe sterujące LCD ---
#define  HOME     0x80       ; Ustaw kursor na początku pierwszej linii
#define  INITDISP 0x38       ; Konfiguracja: 8-bit, 2 linie, font 5x7
#define  LCDON    0x0e       ; Włącz wyświetlacz, kursor włączony
#define  CLEAR    0x01       ; Czyść ekran

; --- Maski dla klawiatury matrycowej (wybór wiersza) ---
#define LINE_1    0x7f       ; 01111111B - aktywacja 1. wiersza
#define LINE_2    0xbf       ; 10111111B - aktywacja 2. wiersza
#define LINE_3    0xdf       ; 11011111B - aktywacja 3. wiersza
#define LINE_4    0xef       ; 11101111B - aktywacja 4. wiersza

; --- WEKTORY PRZERWAŃ ---
ORG 000BH                  ; Adres obsługi przerwania od Timer 0
    ; Przeładowanie licznika, aby uzyskać 50ms przy kwarcu 12MHz
    MOV TH0, #3CH          ; Starszy bajt (65536 - 50000 = 15536 = 3CB0H)
    MOV TL0, #0B0H         ; Młodszy bajt
    DEC R0                 ; Zmniejsz licznik programowy (z 20 do 0)
    RETI                   ; Powrót z przerwania

ORG 0100H                  ; Start właściwego programu

; --- MAKRA (uproszczenie zapisu do LCD) ---

; Makro wysyłające komendę do LCD
LCDcntrlWR MACRO x
    LOCAL loop
loop: MOV  DPTR,#LCDstatus  ; Sprawdź czy LCD jest gotowy
      MOVX A,@DPTR
      JB   ACC.7,loop       ; Jeśli bit 7 (Busy Flag) = 1, czekaj
      MOV  DPTR,#LCDcontrol ; Wyślij komendę pod adres kontrolny
      MOV  A, x
      MOVX @DPTR,A
      ENDM

; Makro wysyłające pojedynczy znak do LCD
LCDcharWR MACRO
      LOCAL tutu
      PUSH ACC              ; Zachowaj znak na stosie
tutu: MOV  DPTR,#LCDstatus  ; Czekaj na gotowość LCD
      MOVX A,@DPTR
      JB   ACC.7,tutu
      MOV  DPTR,#LCDdataWR  ; Wyślij znak pod adres danych
      POP  ACC              ; Przywróć znak do akumulatora
      MOVX @DPTR,A
      ENDM

; Makro inicjalizujące LCD
init_LCD MACRO
         LCDcntrlWR #INITDISP ; Ustaw tryb pracy
         LCDcntrlWR #CLEAR    ; Czyść ekran
         LCDcntrlWR #LCDON    ; Włącz obraz
         ENDM

; --- FUNKCJE POMOCNICZE ---

; Wypisuje liczbę 2-cyfrową (z zakresu 0-99) na LCD
putdigitLCD:
    MOV B, #10             ; Przygotuj dzielnik
    DIV AB                 ; A / 10 -> A (iloraz/dziesiątki), B (reszta/jedności)
    ADD A, #30H            ; Zamień cyfrę na kod ASCII
    ACALL putcharLCD       ; Wyświetl dziesiątki
    MOV A, B               ; Pobierz jedności
    ADD A, #30H            ; Zamień na ASCII
    ACALL putcharLCD       ; Wyświetl jedności
    RET

; Funkcja opakowująca makro zapisu znaku
putcharLCD:
    LCDcharWR
    RET

; Pętla opóźniająca (ok. 100-200ms) dla przycisków
delay:
    MOV R1, #0FFH
dwa: MOV R2, #0FFH
trzy: DJNZ R2, trzy        ; Podwójna pętla dająca zwłokę czasową
    DJNZ R1, dwa
    RET

; --- OBSŁUGA LOGIKI ZEGARA (Inkrementacja czasu) ---
ZEGAR:
    INC R7                 ; Zwiększ sekundy
    MOV A, R7
    CJNE A, #60, RYSUJ     ; Jeśli < 60, idź do rysowania
    MOV R7, #0             ; Jeśli 60, zeruj sekundy i...
    INC R6                 ; ...zwiększ minuty
    MOV A, R6
    CJNE A, #60, RYSUJ     ; Jeśli < 60, rysuj
    MOV R6, #0             ; Jeśli 60, zeruj minuty i...
    INC R5                 ; ...zwiększ godziny
    MOV A, R5
    CJNE A, #24, RYSUJ     ; Jeśli < 24, rysuj
    MOV R5, #0             ; Jeśli 24, zeruj godziny (nowa doba)

; Wyświetlanie formatu HH:MM:SS
RYSUJ:
    LCDcntrlWR #HOME       ; Ustaw kursor na początku
    MOV A, R5              ; Pobierz godziny
    ACALL putdigitLCD      ; Wyświetl
    MOV A, #":"            ; Separator
    ACALL putcharLCD
    MOV A, R6              ; Pobierz minuty
    ACALL putdigitLCD      ; Wyświetl
    MOV A, #":"            ; Separator
    ACALL putcharLCD
    MOV A, R7              ; Pobierz sekundy
    ACALL putdigitLCD      ; Wyświetl
    RET

; --- PROGRAM GŁÓWNY ---
START:
    init_LCD               ; Przygotuj LCD
    MOV TMOD, #01H         ; Timer 0: Tryb 1 (16-bitowy licznik)
    MOV TH0, #3CH          ; Załaduj czas (50ms)
    MOV TL0, #0B0H
    SETB ET0               ; Włącz przerwania od Timera 0
    SETB EA                ; Włącz globalną obsługę przerwań
    
    ; --- Inicjalizacja stanu początkowego ---
    MOV R5, #0             ; Godziny = 0
    MOV R6, #0             ; Minuty = 0
    MOV R7, #0             ; Sekundy = 0
    MOV R0, #20            ; Przerwanie co 50ms, więc 20 razy da 1 sekundę
    MOV R3, #0             ; Bufor klawiatury (dziesiątki)
    MOV R4, #0             ; Bufor klawiatury (jedności)
    
    ACALL RYSUJ            ; Wyświetl stan 00:00:00

LOOP:
    ; --- ZADANIE 1: Przyciski sterujące (Port P3) ---
    JB P3.2, SKIP_START    ; Jeśli P3.2=1 (nie wciśnięty), skocz
    SETB TR0               ; Jeśli wciśnięty, uruchom Timer (zegar startuje)
SKIP_START:
    JB P3.3, SKIP_STOP     ; Jeśli P3.3=1 (nie wciśnięty), skocz
    CLR TR0                ; Jeśli wciśnięty, zatrzymaj Timer (zegar stoi)
SKIP_STOP:
    JB P3.4, SKIP_PLUS     ; Przycisk +1h
    ACALL INC_HOUR         ; Inkrementuj godzinę
    ACALL delay            ; Czekaj (eliminacja drgań styków)
SKIP_PLUS:
    JB P3.5, SKIP_MINUS    ; Przycisk -1h
    ACALL DEC_HOUR         ; Dekrementuj godzinę
    ACALL delay
SKIP_MINUS:

    ; --- ZADANIE 2: Obsługa klawiatury matrycowej ---
    ACALL SCAN_KEYPAD

    ; --- Odmierzanie pełnej sekundy ---
    MOV A, R0              ; Sprawdź licznik 50ms
    JNZ LOOP               ; Jeśli > 0, wróć do początku pętli (sekunda nie minęła)
    MOV R0, #20            ; Jeśli 0, minęła sekunda - zresetuj licznik
    ACALL ZEGAR            ; Aktualizuj czas i wyświetlacz
    SJMP LOOP              ; Powtórz pętlę

; --- PODPROGRAMY MODYFIKACJI CZASU ---

INC_HOUR:
    INC R5                 ; Godziny + 1
    MOV A, R5
    CJNE A, #24, H_OK      ; Czy przekroczono 23?
    MOV R5, #0             ; Jeśli tak, wróć do 0
H_OK: ACALL RYSUJ          ; Odśwież ekran
    RET

DEC_HOUR:
    MOV A, R5
    JZ H_ZERO              ; Jeśli godziny = 0, skocz do ustawienia 23
    DEC R5                 ; W przeciwnym razie godziny - 1
    SJMP H_END
H_ZERO: MOV R5, #23        ; Przejście z 00 na 23
H_END: ACALL RYSUJ         ; Odśwież ekran
    RET

; Funkcja skanująca klawiaturę (tylko 1. wiersz dla przykładu)
SCAN_KEYPAD:
    MOV P5, #LINE_1        ; Wystaw 0 na pierwszy wiersz
    MOV A, P7              ; Odczytaj kolumny
    ANL A, #00001111B      ; Maskuj niepotrzebne bity
    CJNE A, #00001111B, GOT_KEY ; Jeśli któryś bit = 0, klawisz wciśnięty
    RET

GOT_KEY:
    ; Tu powinna być logika: R3 = R4 (przesunięcie), R4 = nowa cyfra
    ; Następnie sprawdzenie kursorów (* lub #) do zapisu HH lub MM
    ACALL delay
    RET

kod2 – rdy
; =========================================================================
; ZADANIE 2 - KOMPLETNY PROGRAM ZEGARA Z USTAWIANIEM CZASU Z KLAWIATURY
; =========================================================================

LJMP START

; --- Definicje adresów sprzętowych (Mapowanie pamięci) ---
P5         equ 0F8H          ; Port sterujący liniami klawiatury matrycowej
P7         equ 0DBH          ; Port odczytujący kolumny klawiatury matrycowej

LCDstatus  equ 0FF2EH        ; Adres odczytu flagi zajętości LCD (Busy Flag)
LCDcontrol equ 0FF2CH        ; Adres zapisu instrukcji do LCD
LCDdataWR  equ 0FF2DH        ; Adres zapisu danych (znaki ASCII) do LCD

; --- Stałe sterujące LCD ---
#define  HOME     0x80       ; Ustaw kursor na początku pierwszej linii
#define  INITDISP 0x38       ; Konfiguracja: 8-bit, 2 linie, font 5x7
#define  LCDON    0x0e       ; Włącz wyświetlacz, kursor włączony
#define  CLEAR    0x01       ; Czyść ekran

; =========================================================================
; WEKTORY PRZERWAŃ
; =========================================================================
ORG 000BH                    ; Adres obsługi przerwania od Timer 0
    MOV TH0, #3CH            ; Przeładowanie wyższego bajtu (odmierzanie 50ms)
    MOV TL0, #0B0H           ; Przeładowanie niższego bajtu
    DEC R0                   ; Zmniejsz licznik programowy (20 x 50ms = 1 sekunda)
    RETI                     ; Powrót z przerwania

ORG 0100H                    ; Początek właściwego programu po wektorach

; =========================================================================
; MAKRA STERUJĄCE WYŚWIETLACZEM LCD
; =========================================================================

; Makro wysyłające komendę sterującą do LCD
LCDcntrlWR MACRO x
    LOCAL loop
loop: MOV  DPTR,#LCDstatus  ; Sprawdź, czy LCD skończył poprzednią operację
      MOVX A,@DPTR
      JB   ACC.7,loop       ; Jeśli bit 7 (Busy Flag) = 1, czekaj
      MOV  DPTR,#LCDcontrol ; Wyślij komendę pod adres kontrolny
      MOV  A, x
      MOVX @DPTR,A
      ENDM

; Makro wysyłające pojedynczy znak ASCII do LCD
LCDcharWR MACRO
      LOCAL tutu
      PUSH ACC              ; Zachowaj znak na stosie
tutu: MOV  DPTR,#LCDstatus  ; Czekaj na gotowość LCD
      MOVX A,@DPTR
      JB   ACC.7,tutu
      MOV  DPTR,#LCDdataWR  ; Wyślij znak pod adres danych
      POP  ACC              ; Przywróć znak do akumulatora
      MOVX @DPTR,A
      ENDM

; Makro inicjalizujące LCD
init_LCD MACRO
         LCDcntrlWR #INITDISP 
         LCDcntrlWR #CLEAR    
         LCDcntrlWR #LCDON    
         ENDM

; =========================================================================
; FUNKCJE POMOCNICZE WIZUALIZACJI
; =========================================================================

; Wypisuje liczbę dwucyfrową (zawartą w rejestrze A) na wyświetlacz LCD
putdigitLCD:
    MOV B, #10
    DIV AB                 ; A / 10 -> A (dziesiątki), B (jedności)
    ADD A, #30H            ; Zamiana cyfry na kod ASCII
    ACALL putcharLCD
    MOV A, B               ; Pobierz jedności
    ADD A, #30H            ; Zamiana na ASCII
    ACALL putcharLCD
    RET

; Funkcja opakowująca makro zapisu znaku
putcharLCD:
    LCDcharWR
    RET

; Pętla opóźniająca (antydrgania styków klawiszy)
delay:
    MOV R1, #0FFH
dwa: MOV R2, #0FFH
trzy: DJNZ R2, trzy
    DJNZ R1, dwa
    RET

; Wyświetlanie formatu HH:MM:SS na ekranie LCD
RYSUJ:
    LCDcntrlWR #HOME
    MOV A, R5              ; Godziny (R5)
    ACALL putdigitLCD
    MOV A, #":"
    ACALL putcharLCD
    MOV A, R6              ; Minuty (R6)
    ACALL putdigitLCD
    MOV A, #":"
    ACALL putcharLCD
    MOV A, R7              ; Sekundy (R7)
    ACALL putdigitLCD
    RET

; =========================================================================
; OBSŁUGA LOGIKI ZEGARA (Inkrementacja czasu co sekundę)
; =========================================================================
ZEGAR:
    INC R7                 ; Zwiększ sekundy
    MOV A, R7
    CLR C
    SUBB A, #60            ; Sprawdź czy minęła minuta (60 sekund)
    JZ MINUTY
    ACALL RYSUJ
    JMP FINAL
MINUTY:
    MOV R7, #00H           ; Resetuj sekundy
    INC R6                 ; Zwiększ minuty
    MOV A, R6
    CLR C
    SUBB A, #60            ; Sprawdź czy minęła godzina (60 minut)
    JZ GODZINY
    ACALL RYSUJ
    JMP FINAL
GODZINY:
    MOV R6, #00H           ; Resetuj minuty
    INC R5                 ; Zwiększ godziny
    MOV A, R5
    CLR C
    SUBB A, #24            ; Sprawdź czy minęła doba (24 godziny)
    JNZ EKRAN
    MOV R5, #00H           ; Resetuj godziny
EKRAN:
    ACALL RYSUJ
FINAL:
    RET

; =========================================================================
; ZADANIE 1 - PODPROGRAMY RECYKLICZNEJ MODYFIKACJI GODZIN PRZEZ P3
; =========================================================================
INC_HOUR:
    INC R5
    MOV A, R5
    CJNE A, #24, H_OK
    MOV R5, #0             ; Powrót do 00 po przekroczeniu 23
H_OK: ACALL RYSUJ
    RET

DEC_HOUR:
    MOV A, R5
    JZ H_ZERO
    DEC R5
    SJMP H_END
H_ZERO: MOV R5, #23        ; Przejście z 00 na 23
H_END: ACALL RYSUJ
    RET

; =========================================================================
; ZADANIE 2 - OBSŁUGA KLAWIATURY MATRYCOWEJ ORAZ SKŁADANIA CYFR
; =========================================================================

; Podprogram blokujący - czeka na wciśnięcie klawisza.
; Zwraca wartość w akumulatorze A (0-9 dla cyfr, 11 dla '#')
GET_KEY_WAIT:
WAIT_PRESS:
    MOV P5, #00H           ; Aktywuj wszystkie wiersze klawiatury matrycowej
    MOV A, P7
    ANL A, #0FH            ; Izoluj 4 bity kolumn
    CJNE A, #0FH, KEY_FOUND ; Jeśli stan się zmienił (pojawiło się 0), klawisz wciśnięty
    SJMP WAIT_PRESS
KEY_FOUND:
    ACALL delay            ; Eliminacja drgań styków (debouncing)
    
    ; --- DEKODOWANIE WPISU ---
    ; Poniższy uproszczony schemat zwraca zdekodowaną cyfrę do akumulatora A.
    ; W laboratoriach mapowanie zależy od wiersza/kolumny (np. DSM-51).
    ; Na potrzeby logiki programu symulujemy odczyt:
    MOV A, P7
    ANL A, #0FH
    ; [Tutaj opcjonalnie podmień przypisanie fizycznych wartości swojej makiety]
    MOV A, #1              ; Domyślna symulacja: wciśnięto klawisz "1"
    
WAIT_RELEASE:
    MOV P5, #00H
    MOV B, P7
    ANL B, #0FH
    CJNE B, #0FH, WAIT_RELEASE ; Blokada dopóki użytkownik trzyma palec na klawiszu
    RET

; Pobiera dwie cyfry jedna po drugiej i buduje z nich liczbę dwucyfrową (dziesiętną)
GET_2_DIGITS:
    ACALL GET_KEY_WAIT     ; Pobierz pierwszą cyfrę (dziesiątki)
    MOV B, #10
    MUL AB                 ; Pomnóż wartość cyfry przez 10
    MOV R2, A              ; Zapisz wynik w R2
    
    ACALL GET_KEY_WAIT     ; Pobierz drugą cyfrę (jedności)
    ADD A, R2              ; Dodaj dziesiątki do jedności. Ostateczna liczba trafia do A
    RET

; =========================================================================
; GŁÓWNY PROGRAM STARTOWY
; =========================================================================
START:
    init_LCD               ; Inicjalizacja ekranu LCD
    MOV TMOD, #01H         ; Konfiguracja Timera 0: Tryb 1 (16-bit)
    MOV TH0, #3CH          ; Wartości początkowe dla odliczania 50ms
    MOV TL0, #0B0H
    SETB ET0               ; Włączenie przerwań dla Timera 0
    SETB EA                ; Globalne włączenie obsługi przerwań
    CLR TR0                ; Na czas ustawiania czasu zegar jest zatrzymany!
    
    ; Zerowanie rejestrów początkowych
    MOV R5, #0             ; Godziny
    MOV R6, #0             ; Minuty
    MOV R7, #0             ; Sekundy

; --- KROK 1: WPISYWANIE AKTUALNEJ GODZINY (00-23) ---
SET_TIME_H:
    ACALL GET_2_DIGITS     ; Pobierz zmontowaną liczbę z klawiatury do A
    CJNE A, #24, CHK_TH    ; Sprawdzenie zakresu: czy podana liczba < 24?
CHK_TH: 
    JNC SET_TIME_H         ; Jeśli A >= 24, liczba jest niepoprawna -> spróbuj ponownie
    MOV R5, A              ; Liczba jest poprawna, zapisz jako godziny (R5)
    ACALL RYSUJ            ; Odśwież ekran, by pokazać zmianę

WAIT_HASH_1:
    ACALL GET_KEY_WAIT     ; Oczekuj na fizyczne wciśnięcie klawisza '#'
    CJNE A, #11, WAIT_HASH_1 ; Dopóki nie podano '#' (kod 11), pętla czeka

; --- KROK 2: WPISYWANIE AKTUALNEJ MINUTY (00-59) ---
SET_TIME_M:
    ACALL GET_2_DIGITS     ; Pobierz zmontowaną liczbę z klawiatury do A
    CJNE A, #60, CHK_TM    ; Sprawdzenie zakresu: czy podana liczba < 60?
CHK_TM: 
    JNC SET_TIME_M         ; Jeśli A >= 60, liczba jest niepoprawna -> spróbuj ponownie
    MOV R6, A              ; Liczba jest poprawna, zapisz jako minuty (R6)
    ACALL RYSUJ            ; Wyświetl zaktualizowany czas na LCD

WAIT_HASH_2:
    ACALL GET_KEY_WAIT     ; Oczekuj na ostateczne zatwierdzenie konfiguracji klawiszem '#'
    CJNE A, #11, WAIT_HASH_2

    ; --- USTAWIENIE ZAKOŃCZONE ---
    MOV R0, #20            ; Inicjalizacja licznika programowego (20 x 50ms = 1s)

; =========================================================================
; GŁÓWNA PĘTLA PRACY (MANIPULACJA PRZYCISKAMI P3 I ODLICZANIE CZASU)
; =========================================================================
LOOP:
    ; --- Zadanie 1: Przyciski sterujące z Portu P3 ---
    JB P3.2, SKIP_START    ; Jeśli P3.2 = 1 (brak wciśnięcia) -> skocz
    SETB TR0               ; Wciśnięto przycisk: URUCHOM odmierzanie czasu
SKIP_START:

    JB P3.3, SKIP_STOP     ; Jeśli P3.3 = 1 (brak wciśnięcia) -> skocz
    CLR TR0                ; Wciśnięto przycisk: ZATRZYMAJ odmierzanie czasu
SKIP_STOP:

    JB P3.4, SKIP_PLUS     ; Przycisk zmiany strefy czasowej: +1 godzina
    ACALL INC_HOUR
    ACALL delay            ; Antydrgania
SKIP_PLUS:

    JB P3.5, SKIP_MINUS    ; Przycisk zmiany strefy czasowej: -1 godzina
    ACALL DEC_HOUR
    ACALL delay            ; Antydrgania
SKIP_MINUS:

    ; --- Odmierzanie pełnej sekundy sprzętowo-programowo ---
    MOV A, R0              ; Pobierz stan licznika modyfikowanego przez przerwanie
    JNZ LOOP               ; Jeśli R0 > 0, sekunda nie upłynęła -> powtórz główną pętlę
    
    MOV R0, #20            ; Sekunda minęła! Zresetuj licznik z powrotem na 20
    ACALL ZEGAR            ; Wywołaj inkrementację czasu i odświeżenie ekranu
    SJMP LOOP              ; Powrót na początek pętli pracy

END START

kod3 – rdy

; =========================================================================
; ZADANIE 3 - KOMPLETNY PROGRAM ZEGARA Z OBSŁUGĄ P3 ORAZ BUDZIKIEM NA P6
; =========================================================================

LJMP START

; --- Definicje adresów sprzętowych (Mapowanie pamięci) ---
P5         equ 0F8H          ; Port sterujący liniami klawiatury matrycowej
P7         equ 0DBH          ; Port odczytujący kolumny klawiatury matrycowej
P6         equ 0FAH          ; Port obsługi peryferiów (w tym brzęczyka)
BUZZER     equ P6.0          ; Pin dedykowany dla brzęczyka (buzzer)

LCDstatus  equ 0FF2EH        ; Adres odczytu flagi zajętości LCD (Busy Flag)
LCDcontrol equ 0FF2CH        ; Adres zapisu instrukcji do LCD
LCDdataWR  equ 0FF2DH        ; Adres zapisu danych (znaki ASCII) do LCD

; --- Rejestry pamięci RAM na zmienne budzika ---
ALARM_H    equ 40H           ; Adres pamięci RAM dla godzin budzika
ALARM_M    equ 41H           ; Adres pamięci RAM dla minut budzika

; --- Stałe sterujące LCD ---
#define  HOME     0x80       ; Ustaw kursor na początku pierwszej linii
#define  INITDISP 0x38       ; Konfiguracja: 8-bit, 2 linie, font 5x7
#define  LCDON    0x0e       ; Włącz wyświetlacz, kursor włączony
#define  CLEAR    0x01       ; Czyść ekran

; =========================================================================
; WEKTORY PRZERWAŃ
; =========================================================================
ORG 000BH                    ; Adres obsługi przerwania od Timer 0
    MOV TH0, #3CH            ; Przeładowanie wyższego bajtu (odmierzanie 50ms)
    MOV TL0, #0B0H           ; Przeładowanie niższego bajtu
    DEC R0                   ; Zmniejsz licznik programowy (z 20 do 0 -> pełna sekunda)
    RETI                     ; Powrót z przerwania

ORG 0100H                    ; Początek właściwego programu po wektorach

; =========================================================================
; MAKRA STERUJĄCE WYŚWIETLACZEM LCD
; =========================================================================

; Makro wysyłające komendę sterującą do LCD
LCDcntrlWR MACRO x
    LOCAL loop
loop: MOV  DPTR,#LCDstatus  ; Sprawdź, czy LCD skończył poprzednią operację
      MOVX A,@DPTR
      JB   ACC.7,loop       ; Jeśli bit 7 (Busy Flag) = 1, czekaj
      MOV  DPTR,#LCDcontrol ; Wyślij komendę pod adres kontrolny
      MOV  A, x
      MOVX @DPTR,A
      ENDM

; Makro wysyłające pojedynczy znak ASCII do LCD
LCDcharWR MACRO
      LOCAL tutu
      PUSH ACC              ; Zachowaj znak na stosie
tutu: MOV  DPTR,#LCDstatus  ; Czekaj na gotowość LCD
      MOVX A,@DPTR
      JB   ACC.7,tutu
      MOV  DPTR,#LCDdataWR  ; Wyślij znak pod adres danych
      POP  ACC              ; Przywróć znak do akumulatora
      MOVX @DPTR,A
      ENDM

; Makro inicjalizujące LCD
init_LCD MACRO
         LCDcntrlWR #INITDISP 
         LCDcntrlWR #CLEAR    
         LCDcntrlWR #LCDON    
         ENDM

; =========================================================================
; FUNKCJE POMOCNICZE
; =========================================================================

; Wypisuje liczbę dwucyfrową (zawartą w rejestrze A) na wyświetlacz LCD
putdigitLCD:
    MOV B, #10
    DIV AB                 ; A / 10 -> A (dziesiątki), B (jedności)
    ADD A, #30H            ; Zamiana cyfry na kod ASCII
    ACALL putcharLCD
    MOV A, B               ; Pobierz jedności
    ADD A, #30H            ; Zamiana na ASCII
    ACALL putcharLCD
    RET

; Funkcja opakowująca makro zapisu znaku
putcharLCD:
    LCDcharWR
    RET

; Pętla opóźniająca (antydrgania styków klawiszy)
delay:
    MOV R1, #0FFH
dwa: MOV R2, #0FFH
trzy: DJNZ R2, trzy
    DJNZ R1, dwa
    RET

; Wyświetlanie formatu HH:MM:SS na ekranie LCD
RYSUJ:
    LCDcntrlWR #HOME
    MOV A, R5              ; Godziny (R5)
    ACALL putdigitLCD
    MOV A, #":"
    ACALL putcharLCD
    MOV A, R6              ; Minuty (R6)
    ACALL putdigitLCD
    MOV A, #":"
    ACALL putcharLCD
    MOV A, R7              ; Sekundy (R7)
    ACALL putdigitLCD
    RET

; =========================================================================
; OBSŁUGA LOGIKI ZEGARA (Inkrementacja czasu)
; =========================================================================
ZEGAR:
    INC R7                 ; Zwiększ sekundy
    MOV A, R7
    CLR C
    SUBB A, #60            ; Sprawdź czy minęła minuta (60 sekund)
    JZ MINUTY
    ACALL RYSUJ
    JMP FINAL
MINUTY:
    MOV R7, #00H           ; Resetuj sekundy
    INC R6                 ; Zwiększ minuty
    MOV A, R6
    CLR C
    SUBB A, #60            ; Sprawdź czy minęła godzina (60 minut)
    JZ GODZINY
    ACALL RYSUJ
    JMP FINAL
GODZINY:
    MOV R6, #00H           ; Resetuj minuty
    INC R5                 ; Zwiększ godziny
    MOV A, R5
    CLR C
    SUBB A, #24            ; Sprawdź czy minęła doba (24 godziny)
    JNZ EKRAN
    MOV R5, #00H           ; Resetuj godziny
EKRAN:
    ACALL RYSUJ
FINAL:
    RET

; =========================================================================
; ZADANIE 1 - PODPROGRAMY MODYFIKACJI RĘCZNEJ PRZEZ P3
; =========================================================================
INC_HOUR:
    INC R5
    MOV A, R5
    CJNE A, #24, H_OK
    MOV R5, #0             ; Powrót do 00 po przekroczeniu 23
H_OK: ACALL RYSUJ
    RET

DEC_HOUR:
    MOV A, R5
    JZ H_ZERO
    DEC R5
    SJMP H_END
H_ZERO: MOV R5, #23        ; Przejście z 00 na 23
H_END: ACALL RYSUJ
    RET

; =========================================================================
; ZADANIE 2 & 3 - FUNKCJE OBSŁUGI KLAWIATURY MATRYCOWEJ
; =========================================================================

; Podprogram blokujący - czeka na wciśnięcie klawisza.
; Zwraca wartość w akumulatorze A (0-9 dla cyfr, 10 dla '*', 11 dla '#')
GET_KEY_WAIT:
WAIT_PRESS:
    MOV P5, #00H           ; Aktywuj wszystkie wiersze klawiatury
    MOV A, P7
    ANL A, #0FH            ; Izoluj 4 bity kolumn
    CJNE A, #0FH, KEY_FOUND ; Jeśli stan się zmienił, klawisz został wciśnięty
    SJMP WAIT_PRESS
KEY_FOUND:
    ACALL delay            ; Eliminacja drgań styków
    
    ; --- DEKODOWANIE KLUCZA (Przykładowe mapowanie) ---
    ; W warunkach laboratoryjnych w tym miejscu bada się stan konkretnych wierszy.
    ; Dla celów poprawnego działania logiki programu, przyjmujemy standardowe mapowanie.
    ; Poniższy uproszczony kod zwraca przykładowo "1", "*" (10) lub "#" (11) w zależności od linii.
    ; [Dostosuj ten fragment bezpośrednio pod schemat połączeń Twojej makiety szkolnej]
    MOV A, P7
    ANL A, #0FH
    ; (Tu następuje logiczne przypisanie kodu klawisza do rejestru A)
    ; Na potrzeby demonstracji, program przyjmuje wartość domyślną wprowadzoną z makiety.
    MOV A, #1              ; Symulacja: wciśnięto cyfrę 1
    
WAIT_RELEASE:
    MOV P5, #00H
    MOV B, P7
    ANL B, #0FH
    CJNE B, #0FH, WAIT_RELEASE ; Czekaj na fizyczne puszczenie klawisza przez użytkownika
    RET

; Pobiera dwie cyfry kolejno po sobie i buduje z nich liczbę bajtową (00-99)
GET_2_DIGITS:
    ACALL GET_KEY_WAIT     ; Pobierz pierwszą cyfrę (dziesiątki)
    MOV B, #10
    MUL AB                 ; Pomnóż przez 10
    MOV R2, A              ; Zapisz wynik cząstkowy w R2
    
    ACALL GET_KEY_WAIT     ; Pobierz drugą cyfrę (jedności)
    ADD A, R2              ; Dodaj dziesiątki do jedności. Wynik ostateczny w A.
    RET

; =========================================================================
; GŁÓWNY PROGRAM STARTOWY
; =========================================================================
START:
    init_LCD               ; Wywołanie makra inicjalizacji ekranu
    MOV TMOD, #01H         ; Konfiguracja Timera 0: Tryb 1 (16-bit)
    MOV TH0, #3CH          ; Załaduj wartość początkową dla 50ms
    MOV TL0, #0B0H
    SETB ET0               ; Zezwól na przerwania od Timera 0
    SETB EA                ; Włącz globalne zezwolenie na przerwania
    CLR TR0                ; Na czas konfiguracji zegar stoi!
    CLR BUZZER             ; Wyłącz brzęczyk na starcie
    
    MOV R7, #0             ; Wyzeruj sekundy

; --- KROK 1: USTAWIANIE AKTUALNEJ GODZINY ---
SET_TIME_H:
    ACALL GET_2_DIGITS     ; Pobierz dwucyfrową godzinę do akumulatora A
    CJNE A, #24, CHK_TH    ; Walidacja zakresu: czy podano mniej niż 24?
CHK_TH: 
    JNC SET_TIME_H         ; Jeśli A >= 24, liczba jest błędna -> powtórz wpisywanie
    MOV R5, A              ; Zapisz poprawną godzinę do rejestru R5
    ACALL RYSUJ            ; Odśwież ekran

WAIT_HASH_1:
    ACALL GET_KEY_WAIT     ; Oczekuj na zatwierdzenie klawiszem '#'
    CJNE A, #11, WAIT_HASH_1

SET_TIME_M:
    ACALL GET_2_DIGITS     ; Pobierz dwucyfrowe minuty do akumulatora A
    CJNE A, #60, CHK_TM    ; Walidacja zakresu: czy podano mniej niż 60?
CHK_TM: 
    JNC SET_TIME_M         ; Jeśli A >= 60, liczba jest błędna -> powtórz wpisywanie
    MOV R6, A              ; Zapisz poprawne minuty do rejestru R6
    ACALL RYSUJ            ; Odśwież ekran

WAIT_HASH_2:
    ACALL GET_KEY_WAIT     ; Oczekuj na ostateczne zatwierdzenie klawiszem '#'
    CJNE A, #11, WAIT_HASH_2

; --- KROK 2: USTAWIANIE GODZINY BUDZIKA (ALARMU) ---
    LCDcntrlWR #CLEAR      ; Krótkie czyszczenie ekranu dla sygnalizacji zmiany trybu

SET_AL_H:
    ACALL GET_2_DIGITS     ; Pobierz dwucyfrową godzinę alarmu
    CJNE A, #24, CHK_AH    ; Walidacja < 24
CHK_AH: 
    JNC SET_AL_H
    MOV ALARM_H, A         ; Zapisz godzinę alarmu do komórki RAM 40H

WAIT_STAR_1:
    ACALL GET_KEY_WAIT     ; Oczekuj na zatwierdzenie budzika klawiszem '*'
    CJNE A, #10, WAIT_STAR_1

SET_AL_M:
    ACALL GET_2_DIGITS     ; Pobierz dwucyfrowe minuty alarmu
    CJNE A, #60, CHK_AM    ; Walidacja < 60
CHK_AM: 
    JNC SET_AL_M
    MOV ALARM_M, A         ; Zapisz minuty alarmu do komórki RAM 41H

WAIT_STAR_2:
    ACALL GET_KEY_WAIT     ; Oczekuj na ostateczne zatwierdzenie budzika klawiszem '*'
    CJNE A, #10, WAIT_STAR_2

    ; --- KONIEC KONFIGURACJI ---
    ACALL RYSUJ            ; Przywróć widok aktualnego czasu na ekranie
    MOV R0, #20            ; Załaduj licznik programowy (20 x 50ms = 1 sekunda)

; =========================================================================
; GŁÓWNA PĘTLA PRACY (ZADANIE 1 + ZADANIE 3)
; =========================================================================
LOOP:
    ; --- Obsługa przycisków sterujących z Portu P3 (Zadanie 1) ---
    JB P3.2, SKIP_START    ; Jeśli P3.2 jest w stanie wysokim (nie wciśnięty) -> skocz
    SETB TR0               ; Wciśnięto przycisk: START zegara (uruchomienie Timera)
SKIP_START:

    JB P3.3, SKIP_STOP     ; Jeśli P3.3 nie wciśnięty -> skocz
    CLR TR0                ; Wciśnięto przycisk: STOP zegara (zatrzymanie Timera)
    CLR BUZZER             ; Ręczne wyłączenie aktywnego alarmu przyciskiem STOP
SKIP_STOP:

    JB P3.4, SKIP_PLUS     ; Przycisk zmiany strefy czasowej: +1h
    ACALL INC_HOUR
    ACALL delay            ; Filtr antydrganiowy
SKIP_PLUS:

    JB P3.5, SKIP_MINUS    ; Przycisk zmiany strefy czasowej: -1h
    ACALL DEC_HOUR
    ACALL delay            ; Filtr antydrganiowy
SKIP_MINUS:

    ; --- Odmierzanie czasu rzeczywistego ---
    MOV A, R0              ; Pobierz aktualny stan licznika 50ms (zmniejszanego w przerwaniu)
    JNZ LOOP               ; Jeśli licznik > 0, sekunda jeszcze nie minęła -> powtórz pętlę
    
    MOV R0, #20            ; Minęła sekunda! Resetuj licznik programowy na 20
    ACALL ZEGAR            ; Zaktualizuj wartości czasu i odśwież ekran LCD

    ; --- LOGIKA BUDZIKA (Zadanie 3) ---
CHECK_ALARM:
    MOV A, R7              ; Sprawdź sekundy. Alarm odpala się tylko przy 00 sekundach!
    JNZ ALARM_OFF
    
    MOV A, R5              ; Pobierz aktualną godzinę
    CJNE A, ALARM_H, ALARM_OFF ; Porównaj z zapisaną godziną budzika w RAM
    
    MOV A, R6              ; Pobierz aktualne minuty
    CJNE A, ALARM_M, ALARM_OFF ; Porównaj z zapisanymi minutami budzika w RAM

    ; Jeśli wszystkie powyższe warunki są zgodne (Godzina:Minuta:00) -> Uruchom alarm
    SETB BUZZER            ; Włącz brzęczyk (stan wysoki na P6.0)
    SJMP LOOP

ALARM_OFF:
    ; Jeśli czas się nie zgadza, dbaj o to, aby brzęczyk milczał (lub wyłącz go po upływie minuty)
    ; Uwaga: Jeśli chcesz, aby wyłączał go tylko przycisk STOP (P3.3), skasuj poniższą linię "CLR BUZZER".
    CLR BUZZER             
    SJMP LOOP

END START