Reloj del zak80
La idea es hacer un sistema versátil, que provea un reloj estable a una frecuencia de funcionamiento (4MHz), asi como herramientas para desarrollo; un reloj automático a 1Hz, y un reloj manual por interruptor.
Para ello se utilizará un microcontrolador AVR, serie attiny (attiny24 versión final, attiny2313 para desarrollo) que se encargará de leer unos interruptores de configuración al inicio, y seleccionar el modo de funcionamiento adecuado. Además este microcontrolador controlará la señal de reset del z80, manteniendolo en reset durante unos milisegundos al inicio, y reseteandolo cuando el mismo sea reseteado.
Para ello, se ha diseñado el siguiente código ensamblador, para garantizar timings adecuados:
; z80 clock generator
; David Pello 2011
; generates a standard clock (half clk in), a ~1Hz clock
; and a manual clock (using a switch)
; attiny2313 version, PB3 reset output, PB2 clock output
; PB0 and PB1 mode select. PB4 external clock input
;
.include "tn2313def.inc"
.cseg
.org 0
; interrupt vectors
rjmp reset ; reset interrupt
reti ; INT0 interrupt
reti ; PCINT0 interrupt
reti ; Timer 0 overflow interrupt
reti ; EE_RDY interrupt
reti ; ANA_COMP interrupt
reti ; TIM0_COMPA interrupt
reti ; TIM0_COMPB interrupt
reti ; WDT interrupt
reti ; ADC interrupt
reset:
cli ; clear interrupts
ldi r16, 0b10000000 ; clock preescaler enable sequence
out CLKPR, r16
ldi r16, 0b00000001 ; clock preescaler = 2 (16/2) = 8MHz
out CLKPR, r16
ldi r16, 0b00001100 ; PORTB, PB2 output (clock), PB3 output(reset), PB0 & PB1 inputs (clocksel), PB4 input (clock switch)
out DDRB, r16
cbi PORTB, 3 ; PB3 = 0, z80 on reset.
; read clocksel
in r17, PINB
andi r17, 0b00000011 ; we have selection in r17
; wait 10ms to come out from reset
rcall delay_10ms
; check clocksel
cpi r17, 0x00 ; 00, 4 MHz mode
breq clk_4mhz
cpi r17, 0x01 ; 01, 1 Hz mode
breq clk_1hz
cpi r17, 0x02 ; 10, Manual mode
breq clk_manual
halt: ; 11, halt mode, no clock
sleep
rjmp halt
; 4MHz selected
clk_4mhz:
ldi r16, 0b01000011 ; Timer0, toggle OC0A on compare match, OC0B disconected, waveform generator Fast PWM mode
out TCCR0A, r16 ; set
ldi r16, 0b00001001 ; FOC0A and FOC0B unset, waveform Fast PWM, preescaler = 1
out TCCR0B, r16 ; start oscillate
ldi r16, 0x01 ; OCR0A = 1, toggle OC0A each 2 timer interrupts, freq = clk/4 = 4Mhz
out OCR0A, r16 ; set
sbi PORTB, 3 ; come out from reset
sei ; interrups activated
loop_auto:
sleep ; do nothing, interrupts take care
rjmp loop_auto
; 1Hz selected
clk_1hz:
sbi PORTB, 3 ; come out from reset
loop_1hz:
sbi PORTB, 2 ; clock high
rcall delay_500ms
cbi PORTB, 2 ; clock low
rcall delay_500ms
rjmp loop_1hz
; manual mode selected
clk_manual:
sbi PORTB, 3 ; come out from reset
loop_manual:
in r18, PINB ; read button
andi r18, 0b00010000
cpi r18, 0b00010000
breq clk_manual_in
; no press, continue
rjmp loop_manual
clk_manual_in:
; we got a pulse, send clock
sbi PORTB, 2 ; high
rcall delay_10ms ; wait
cbi PORTB, 2 ; low
; wait...
rcall delay_500ms
rjmp loop_manual
; delay 10ms @ 8MHz
delay_10ms:
; delaying 79998 cycles:
ldi r20, $86
d10msl0:
ldi r21, $C6
d10msl1:
dec r21
brne d10msl1
dec r20
brne d10msl0
; -----------------------------
; delaying 2 cycles:
nop
nop
ret
; delay 0.5s @ 8MHz
delay_500ms:
; delaying 3999996 cycles:
ldi r20, $24
d500msl0:
ldi r21, $BC
d500msl1:
ldi r22, $C4
d500msl2:
dec r22
brne d500msl2
dec r21
brne d500msl1
dec r20
brne d500msl0
; -----------------------------
; delaying 3 cycles:
ldi r20, $01
d500msl3:
dec r20
brne d500msl3
; -----------------------------
; delaying 1 cycle:
nop
ret
Salida a 4MHz: