Разработать программу на языке Assembler, отсчитывающую время от заданного значения до 0. Значение может быть от 0001 до 9999.
Ввод необходимо осуществлять с помощью стандартной шестнадцатикнопочной терминальной клавиатуры. Переход в режим установки времени необходимо осуществлять нажатием клавиши 0. Выход из режима установки времени должен происходить автоматически по окончании ввода значений всех регистров.
Для ввода данных используется стандартная шестнадцатикнопочная клавиатура, подключаемая к параллельному порту (P1). Она имеет следующий вид:
Чтобы определить, нажата или нет какая-либо клавиша-ключ, сначала нужно выставить на линии регистра-защёлки P1, которые соединяет клавиша, разные логические значения. Затем необходимо считать из регистра значения этих линий: если оба бита стали равными «0», то значит, проверяемая клавиша была нажата.
Чтобы просто зафиксировать факт нажатия любой из цифровых клавиш, достаточно выставить в регистр P1 слово #00001111b и ожидать на P1 значение #0000x1xxb, где xÎ{0;1}.
Особенностью данной клавиатуры, которую надо учитывать при программировании, является наличие переходного процесса (дребезга) с длительностью
в несколько раз большей, чем время машинного цикла ОМЭВМ МК8051: ORG 8000h;
jmplbMain
ORG 800Bh;
jmp intTF0
lbMain:
; конфигурируем таймер T0:
anl TMOD, #0F0h; обнуляем младшую тетраду
orlTMOD, #01h; младшую тетраду приводим к виду "0001"
clrTR0; остановка таймера T0 (таймер - не считает)
; загружаем старший и младший байты регистра таймера T0 нулями:
movTH0, #0
movTL0, #0
; настраиваем систему прерываний:
movIP, #0; все прерывания (пока) - с одинаковым низким уровнем приоритета
movIE, #10000010b; IEN0: выставили биты EA(7),ES(4),ET0(1)
;mov 0E8h, #00001011b; IEN1: выставили биты 0,1,3
mov P1, #00001111b
mov 0EBh,#0FFh;
;;;; ОПИСАНИЕ ПЕРЕМЕННЫХ: ;;;;
;регистр R0 - единицы
;регистр R1 - десятки
;регистр R2 - сотни
;регистр R3 - тысячи
;регистры R4, R5, R6 - для разных промежуточных значений
;регистр R7 - буфер для хранения значения нажатой клавиши [#0..#9] или
; значения #FFh, если клавиша не нажата
CRequ 0Dh; "возврат каретки"
LFequ 0Ah; "перевод строки"
ESCequ 1Bh; "конец сообщения"
Buf_R0 equ 20h
Buf_R1 equ 21h
Buf_R2 equ 22h
Buf_R3 equ 23h
;;;; ТЕЛО ПРОГРАММЫ: ;;;;
;задаём начальное время:
mov R0, #0;
mov R1, #2;
mov R2, #1;
mov R3, #0;
mov Buf_R0, R0
mov Buf_R1, R1
mov Buf_R2, R2
mov Buf_R3, R3
mov DPTR, #msgShowTime_Mode
call prShowMessage; вывод сообщения msgShowTime_Mode
setb TR0
mov R4, #13
lbMainLoop:
nop
clr A
add A, R0
add A, R1
add A, R2
add A, R3
jz lbFinal
call prIs_SetTime_Mode;
jmp lbMainLoop
;call prDelay;
;call prDisplay;
;call prIs_SetTime_Mode;
intTF0:
clr TF0
djnz R4, lbTF0_End;
; реализация обратного отсчёта:
decR0
cjneR0, #0FFh, lbTF0_Next1;
mov R0, #9
dec R1
cjne R1, #0FFh, lbTF0_Next1;
mov R1, #9
dec R2
cjne R2, #0FFh, lbTF0_Next1;
mov R2, #9
dec R3
cjne R3, #0FFh, lbTF0_Next1;
lbTF0_Next1: mov R4, #13
lbTF0_End: call prDisplay
reti
lbFinal:
clr TR0
mov DPTR, #msgFinal
call prShowMessage
;call prIs_SetTime_Mode;
jmp $
;;;; ОПИСАНИЕ ПРОЦЕДУР: ;;;;
;; процедура prDelay:
; lb5: movR5, #1;
;
; lb3: djnz R6, lb3
; djnz R4, lb3
; djnz R5, lb3
;
; ret
;
; prDelay:
; mov R6, #0
; mov R4, #0
; mov R5, #6
; call lb3
;
; mov R4, #244; (!!!)
; call lb5
;
; mov R4, #1
; mov R6, #118; (!!!) Это следует менять для подстройки задержки!!!
; call lb5
;
; ret
;; процедура prSmartDelay:
;; небольшая задержка, за время которой на клавиатуре успевает
;; завершиться "переходный процесс"
prSmartDelay:
movR6, #0
mov R5, #0
lbSmartDelay_Loop:
djnz R5, $
djnz R6, lbSmartDelay_Loop
ret
;; процедура prDisplay:
;; [2 на вызов]+[2 на возврат]+[7]+[1]+[1432]=[1444]
;; переводим значения R0, R1, R2 в кодировку ACSII:
lbDrawElement:
add A, #30h
call prWaitFor_TI
mov SBUF, A
ret
prDisplay:
callprWaitFor_TI
movSBUF, #CR; переводим курсор в начало строки:
mov A, R3
call lbDrawElement
mov A, R2
call lbDrawElement
mov A, R1
call lbDrawElement
mov A, R0
call lbDrawElement
callprWaitFor_TI; для выравнивания подождём передачи последнего символа
setbTI; [1] иначе - программа "зависнет"
ret; [2]
;; процедура prWaitFor_TI:
;; ожидание "готовности передачи" по последовательному порту
prWaitFor_TI:
jnbTI, $; зациклить, пока TI=0
clrTI; устанавливаем "неготовность пердачи"
ret
;; процедура prIs_SetTime_Mode:
;; [2 на вызов]+[2+1+2+2]=[9]
;; проверяет, нажата ли клавиша входа в режим установки времени;
;; такой клавишей явл. '0'
prIs_SetTime_Mode:
mov P1, #00BFh
mov A, P1
cjne A, #00BEh, lbNot_SetTime_Mode
callprSetTime; переход в режим установки таймера
lbNot_SetTime_Mode:
cjne A, #00B7h, lbNotAnyMode
mov R0, Buf_R0
mov R1, Buf_R1
mov R2, Buf_R2
mov R3, Buf_R3
lbNotAnyMode: ret
;; процедура prExam_NumKeys:
;; опрос цифровых клавиш
prExam_NumKeys:
movR7, #0FFh; допустим, что ничего не будет нажато
;опрос первого столбца клавиш:
lbKey_1:
mov P1, #00DFh
mov A, P1
cjne A, #00DDh, lbKey_2
mov R7, #1
ret
lbKey_2:
cjne A, #00DEh, lbKey_3
mov R7, #2
ret
lbKey_3:
cjne A, #00D7h, lbKey_4
movR7, #3
ret
;опрос второго столбца клавиш:
lbKey_4:
mov P1, #00EFh
mov A, P1
cjne A, #00EDh, lbKey_5
mov R7, #4
ret
lbKey_5:
cjne A, #00EEh, lbKey_6
mov R7, #5
ret
lbKey_6:
cjne A, #00E7h, lbKey_7
movR7, #6
ret
;опрос третьего столбца клавиш:
lbKey_7:
mov P1, #007Fh
mov A, P1
cjne A, #007Dh, lbKey_8
mov R7, #7
ret
lbKey_8:
cjne A, #007Eh, lbKey_9
mov R7, #8
ret
lbKey_9:
cjne A, #0077h, lbKey_0
movR7, #9
ret
;опрос четвёртого столбца клавиш:
lbKey_0:
mov P1, #00BFh
mov A, P1
cjne A, #00BDh, lbEnd_Exam_NumKeys
mov R7, #0
lbEnd_Exam_NumKeys: ret
;; процедура prWaitFor_NextKey_Pressed:
;; ждёт СЛЕДУЮЩЕГО НАЖАТИЯ, чтобы долгое нажатие не "флудило"
prWaitFor_NextKey_Pressed:
lbPrevKey_Pressed: callprSmartDelay; чтоб избежать "дребезга" клавиатуры
call prExam_NumKeys;
cjne R7, #0FFh, lbPrevKey_Pressed;
call prSmartDelay; чтоб избежать "дребезга" клавиатуры
lbNo_NextKey_Pressed: call prExam_NumKeys
cjne R7, #0FFh, lbNextKey_Pressed
jmp lbNo_NextKey_Pressed
lbNextKey_Pressed: ret
;; процедура prSetTime:
;; режимустановкитаймера:
prSetTime:
clrEA
mov DPTR, #msgSetTime_Mode
call prShowMessage; вывод сообщения msgSetTime_Mode
call prDisplay
;регистр R3:
call prWaitFor_NextKey_Pressed;
mov R3, 07
call prDisplay
;регистр R2:
call prWaitFor_NextKey_Pressed
mov R2, 07
call prDisplay
;регистр R1:
call prWaitFor_NextKey_Pressed
mov R1, 07
call prDisplay
;регистр R0:
call prWaitFor_NextKey_Pressed
mov R0, 07
call prDisplay
mov Buf_R0, R0
mov Buf_R1, R1
mov Buf_R2, R2
mov Buf_R3, R3
mov DPTR, #msgShowTime_Mode
call prShowMessage; вывод сообщения msgShowTime_Mode
setb EA
ret
;; процедуры prIs_R0_more_59, prIs_R1_more_59, prIs_R2_more_23:
;; проверяют соответствующие регистры на корректность значений в них;
;; большие значения уменьшаются до ближайших верных
prCorrect_R0:
cjne R0, #59, lbR0_not_59
ret
lbR0_not_59: jnc lbR0_more_59
ret
lbR0_more_59: mov R0, #59
ret
prCorrect_R1:
cjne R1, #59, lbR1_not_59
ret
lbR1_not_59: jnc lbR1_more_59
ret
lbR1_more_59: mov R1, #59
ret
prCorrect_R2:
cjne R2, #23, lbR2_not_23
ret
lbR2_not_23: jnc lbR2_more_23
ret
lbR2_more_23: mov R2, #23
ret
;; процедура prShowMessage:
;; выводит на экран текстовое сообщение; символ ESC - признак конца сообщения
prShowMessage:
clr A
movc A, @A + DPTR
lbNextSymbol: call prWaitFor_TI
mov SBUF, A
inc DPTR
clr A
movc A, @A + DPTR
cjneA, #ESC, lbNextSymbol
ret
;;;; ОПИСАНИЕ ТЕКСТОВЫХ СООБЩЕНИЙ: ;;;;
msgSetTime_Mode: db LF,CR, '[Regim ustanovki taymera:]', CR, LF, ESC
msgShowTime_Mode: db LF,CR, 'OBRATNYI OTSCHET...', CR, LF, ESC
msgFinal: db CR, '!!!OBRATNYI OTSCHET ZAKONCHEN!!!:', CR, LF, ESC
END;;;; КОНЕЦ ПРОГРАММЫ