; Test 4: Even more about the board: timer in interupt mode
; New things to learn here:
; - Timer in interrupt mode
; - Interrupts, Interrupt-vektor
; - BCD-arithmetic
; Here is my substitute for the DEVICE-command
.NOLIST
.INCLUDE "8515def.inc"
.LIST
; Universal register definition
.DEF mp = R16
; Counter for timer timeouts, MSB timer driven by software
.DEF z1 = R0
; Working register for the Interrupt-Service-Routine
; Note that any registers used during an interrupt, including the
; status-register with all the flags, must either be
; reserved for that purpose or they have to reset to their initial
; value at the end of the service routine! Otherwise nice and
; nearly unpredictable effects will occur.
.DEF ri = R1
; Register for counting the seconds as packed BCD
.DEF sec = R2
; Reset-vector to adress 0000
rjmp main
; This is the first time we really need this RJMP command, because here
; we have to put interrupt vectors to position 1, 2, 3 and so on.
; Interrupt-vector definitions not used here (all but the timer overflow vector)
; are dummied by the return-from-interrupt command RETI,
; RETI is a special return command for interrupt service routines as it
; preserves the interrupt-flags in the status-register. Be sure that the jump
; to the interrupt service routine tc0i is exactly at adress 0007, otherwise
; the interrupt fails. The following mechanism goes on: If the timer overflows
; (transition from 255 to 0) the program run is interrupted, the current adress
; in the program counter is pushed to the stack, the command at adress 0007
; is executed (usually a jump instruction). After finishing execution of the
; interrupt service routine the program counter value is restored from the
; stack and program execution maintains at that point.
; The interrupt-vector commands, 1 Byte each:
reti ; Int0-Interrupt
reti ; Int1-Interrupt
reti ; TC1-Capture
reti ; TC1-Compare A
reti ; TC1-Compare B
reti ; TC1-Overflow
rjmp tc0i ; Timer/Counter 0 Overflow, my jump-vector!
reti ; Serial Transfer complete
reti ; UART Rx complete
reti ; UART Data register empty
reti ; UART Tx complete
reti ; Analog Comparator
; Interrupt-Service-Routine for the counter
tc0i:
in ri,SREG ; save the content of the flag register
inc z1 ; increment the software counter
out SREG,ri ; restore the initial value of the flag register
reti ; Return from interrupt
; The main program starts here
main:
ldi mp,LOW(RAMEND) ;Initiate Stackpointer
out SPL,mp ; for the use by interrupts and subroutines
ldi mp,HIGH(RAMEND)
out SPH,mp
; Software-Counter-Register reset to zero
ldi mp,0 ; z1 cannot be set to a constant value, so we set mp
mov z1,mp ; to zero and copy that to R0=z1
mov sec,mp ; and set the seconds to zero
; Prescaler of the counter/timer = 256, that is 4 MHz/256 = 15625 Hz = $3D09
ldi mp,0x04 ;Initiate Timer/Counter 0 Prescaler
out TCCR0,mp ; to Timer 0 Control Register
; Port B is LED-port
ldi mp,0xFF ; all bits are output
out DDRB,mp ; to data direction register
; enable interrupts for timer 0
ldi mp,$02 ; set Bit 1
out TIMSK,mp ; in the Timer Interupt Mask Register
; enable all interrupts generally
sei ; enable all interrupts by setting the flag in the status-register
; The 8-bit counter overflows from time to time and the interrupt service
; routine increments a counter in a register. The main program loop reads this
; counter register and waits until it reaches hex 3D. Then the timer is read until
; he reaches 09 (one second = dez 15625 = hex 3D09 timer pulses). The timer
; and the register are set to zero and one second is incremented. The seconds
; are handled as packed BCD-digits (one digit = four bits, 1 Byte represents
; two digits). The seconds are reset to zero if 60 is reached. The seconds
; are displayed on the LEDs.
loop:
ldi mp,$3D ; compare value for register counter
loop1:
cp z1,mp ; compare with the register
brlt loop1 ; z1 < mp, wait
loop2:
in mp,TCNT0 ; read LSB in the hardware counter
cpi mp,$09 ; compare with the target value
brlt loop2 ; TCNT0 < 09, wait
ldi mp,0 ; set register zero and ...
out TCNT0,mp ; reset hardware-counter LSB
mov z1,mp ; and software-counter MSB
rcall IncSec ; call the subroutine to increment the seconds
rcall Display ; call subroutine to display the seconds
rjmp loop ; once again the same
; subroutine increment second counter
; in BCD-arithmetic! Lower nibble = Bit 0..3, upper nibble = 4..7
IncSec:
sec ; Setze Carry-Flag for adding an additional one to the seconds
ldi mp,6 ; povoke overflow of the lower nibble by adding 6
adc sec,mp ; add 6 + 1 (Carry)
brhs Chk60 ; if overflow of the lower nibble occurred go to 60 check
sub sec,mp ; subtract the additional 6 as no overflow occurred
Chk60:
ldi mp,$60 ; 60 seconds already reached?
cp sec,mp
brlt SecRet ; jump if less than 60
ldi mp,256-$60 ; Load mp to add sec to zero
add sec,mp ; Add mp to reset sec to zero
SecRet:
ret ; return to the main program loop
; subrountine for displaying the sonds on the LEDs
Display:
mov mp,sec ; copy seconds to mp
com mp ; One-complement = XOR(FF) to invert the bits
out PORTB,mp ; send to the LED port
ret ; return to main program loop