📄 main_asm.txt
字号:
;*******************************************************************************
; Title: Keyboardswitch-deluxe
; Author: Anders Runeson, arune@users.sf.net
; Andreas Fritiofson,
; Version:
; Date: 2003/06/xx - 2004/xx/xx
;
; Target: AT90Sxxxx (All AVR Devices)
;
; DESCRIPTION
;
;
; The timing is adapted for 4 MHz xtal
;
; Usage:
;
;
;*******************************************************************************
;todo:
;
;funktion: blinka led d堥n pc bootas
;
;spara saker i eeprom
;
.include "2313def.inc"
;Hardware:
.equ KBD_CK =PB7 ;PD5
.equ KBD_DATA =PB6 ;PD4
.equ PCc_CK =PB4 ;PB5
.equ PCc_DATA =PB5 ;PB4
.equ PCb_CK =PB2 ;PB3
.equ PCb_DATA =PB3 ;PB2
.equ PCa_CK =PB0 ;PB1
.equ PCa_DATA =PB1 ;PB0
.equ MOUSEc_E =PD3
.equ MOUSEb_E =PD4
.equ MOUSEa_E =PD5
;debug
.equ Uart_TxD =PD1 ;o_Data to UART is PD1
.equ Uart_RxD =PD0 ;i_Data from UART is PD0
;.equ LED =PD2
; Status flags
.equ VALID =0 ; There's new valid data in RxByte
.equ ERROR =1 ; There was an error during last reception
.equ BREAK =2 ; Last byte received was a break (F0)
.equ EXTENDED =3 ; Last byte received was "extended" (E0)
.equ KB_CAPS =4 ; Use Caps as function-key, flag indicates if Caps is being pressed
;Define regs used
.def parity_cnt =R4
.def temp2 =R8
.def temp3 =R9
.def blkcnt1 =R10
.def blkcnt2 =R11
.def blkcnt3 =R12
.def ledstatus =R13
.def bootingcomp =R14
.def temp =R17
.def Status =R18
.def bitcnt =R20
.def TxByte =R21 ;kan anv䮤a samma reg ifall det krisar sig (kanske inte, kolla processdata)
.def RxByte =R22
.def LoopPC =R23
.def CurrentPC =R24
.def ConnectedPCs =R25
;for PCa: LoopPC=0b00000001, PCb: LoopPC=0b00000100, PCc: LoopPC=0b00010000, PCd: LoopPC=0b01000000
;same with CurrentPC
;***** Other
;This is what avr responds when pc asks for ID (0xF2)
.equ KBD_ID0 =0xAB
.equ KBD_ID1 =0x83
.equ KBD_ACK =0xFA
.equ KBD_BAT_SUCCESS =0xAA
.equ KBD_ECHO =0xEE
.equ KBD_TMDelay =0b01 ;Typematic repeat delay (0b00=250ms, 0b01=500ms, 0b10=750ms, 0b11=1000ms)
.equ KBD_TMRate =0b00100 ;Typematic repeat rate
;0b00000=30.0cps -> 0b11111=2.0cps
;30.0, 26.7, 24.0, 21.8, 20.0, 18.5, 17.1, 16.0 , 15.0, 13.3, 12.0, 10.9, 10.0, 9.2, 8.6, 8.0, 7.5, 6.7, 6.0, 5.5, 5.0, 4.6, 4.3, 4.0, 3.7, 3.3, 3.0, 2.7, 2.5, 2.3, 2.1, 2.0
.equ MOUSEPORTMASK =0b00111000 ;which ports on PD have a mouse_enable connected
;TODO; flytta om dessa, klart?
.equ NUMLOCKLED =0x02
.equ SCROLLOCKLED =0x01
.equ CAPSLOCKLED =0x04
;.equ CONNECTED_PCs =0b00001100 ;1 PCs 0b00000011, 2 PCs 0b00001111, 3 PCs 0b00111111, 4 PCs 0b11111111
;#############################################################
;# RESET #
;#############################################################
.ORG 0x00
rjmp RESET ;Reset Handler
;.ORG OVF1addr
; rjmp OVFL_TIM1 ;Timer1 Overflow handler
RESET:
cli ;Disable interrupts
;#############################################################
;# INIT #
;#############################################################
;INIT STACKPOINTER
ldi temp,low(RAMEND)
out SPL,temp
;INIT PORT D
;set inputs/outputs (1 = output)
ldi temp,(1<<MOUSEa_E)|(1<<MOUSEb_E)|(1<<MOUSEc_E) ;|(1<<LED)
out DDRD,temp
;set PORT-status (1 = pullup for inputs)
ldi temp,(0<<MOUSEa_E)|(0<<MOUSEb_E)|(0<<MOUSEc_E) ;|(1<<LED)
out PORTD,temp
;INIT PORT B
;set inputs/outputs (1 = output)
ldi temp,(0<<KBD_CK)|(0<<KBD_DATA)|(0<<PCa_CK)|(0<<PCa_DATA)|(0<<PCb_CK)|(0<<PCb_DATA)|(0<<PCc_CK)|(0<<PCc_DATA) ;|(0<<PCd_CK)|(0<<PCd_DATA)
out DDRB,temp
;set PORT-status (1 = pullup for inputs)
ldi temp,(0<<KBD_CK)|(0<<KBD_DATA)|(0<<PCa_CK)|(0<<PCa_DATA)|(0<<PCb_CK)|(0<<PCb_DATA)|(0<<PCc_CK)|(0<<PCc_DATA) ;|(0<<PCd_CK)|(0<<PCd_DATA)
out PORTB, temp
;INIT UART
; ldi temp,(1<<TXEN) ;|(1<<RXEN) ;0b00011000
; out UCR,temp
; ldi temp,12 ;25=9600, 12=19200,
; out UBRR,temp
;INIT TIMER1 - 16bit
;start timer1
;timer1 is used as watchdog timeout (not exacly, but almost), so that the program doesnt get stuck
;in routines checking ports (loops)
ldi temp,0b00000001 ;no prescaler, running timer on CK
out TCCR1B,temp
;INIT EXT-INTERRUPT
;INIT KEYBOARD
;almost all commands from the pc is ignored (see process_to_keyb)
;below there are some commands sent to keyb manually, like reset and set typematic rate/delay
;Send keyboard reset
ldi TxByte,0xFF
rcall avr_to_keyb
rcall delay100us
rcall Wait_keyb_byte ;catch ack
rcall delay100us
rcall Wait_keyb_byte ;catch BAT_SUCCESS
rcall delay100us
;send command typematic rate/delay to keyboard
ldi TxByte,0xF3
rcall avr_to_keyb
rcall delay100us
rcall Wait_keyb_byte ;catch ack
rcall delay100us
;send argument to command "typematic rate/delay"
ldi TxByte,(KBD_TMDelay<<5)|(KBD_TMRate<<0)
rcall avr_to_keyb
rcall delay100us
rcall Wait_keyb_byte ;catch ack
rcall delay100us
ldi LoopPC,0b00000001
ldi CurrentPC,0b00000001 ;IMPORTANT INSTRUCTION!
clr ConnectedPCs ;let routines check which pcs are "on"
sbi PORTD,MOUSEa_E ;choose a mouse at reset
clr Status
sei
;#############################################################
;# MAIN #
;#############################################################
;***** Program Execution Starts Here **************************************
;debug
;ldi TxByte, 0x1C
;rcall avr_to_pc
;ldi TxByte,0xEE
;rcall avr_to_keyb
Start:
;debug
;sbi PORTD,LED
;reset timer
clr temp
out TCNT1h,temp
out TCNT1l,temp
;debug (uart-rx is not connected!)
;sbic USR,RXC
;rcall uart_reciev
;check if pc specified by LoopPC is active/on/ok
rcall check_connected_pc
lsl LoopPC
rol LoopPC
cpi LoopPC,0b01000000
brne skipKBports
ldi LoopPC,0b00000001
;brne skip_carry_rol
;rol LoopPC
;skip_carry_rol:
skipKBports:
mov temp,LoopPC
and temp,ConnectedPCs ;check if pc is "connected"
breq Start ;if not connected goto start and check if connected, then goto next computer
;debug
;mov temp,LoopPC
;rcall uartsend
check_ports:
rcall pc_to_avr ; Read from pc if it tries to send
sbrc Status, VALID ; If new data is available, process it
rcall process_to_keyb ; and send it to the keyboard if neccessary
rcall keyb_to_avr ; Read from keyboard if it tries to send
sbrc Status, VALID ; If new data is available, process it and
rcall process_to_pc ; send it to the current pc if neccessary
rjmp Start
check_connected_pc:
;check all pcs clock- and data-line, if they are idle then set bit in ConnectedPCs
;f?ara hur denna rutinen fungerar
; push LoopPC
; ldi LoopPC,0b00000001
; ccp_do:
rcall read_pc_data ;if not idle, jump/quit routine
breq ccp_skip_setb
rcall read_pc_ck ;if not idle, jump/quit routine
breq ccp_skip_setb
;both lines is idle, this is the key to being considered connected after being disconnected,
;if any of the ports is not idle there can be an error, both line idle => secure to consider connected
;(at least if idle for some ms)
mov temp,ConnectedPCs
and temp,LoopPC ;if connected
brne ccp_skip_setb ;return
;else:
or ConnectedPCs,LoopPC ;consider connected
ccp_skip_setb:
ret
Delay_halfbit:
ldi temp, (36*4-9)/3 ; Delay half a bit or around 36 ?s at 4 MHz and 3 cycles per loop
Delay_halfbit_inner:
dec temp
brne Delay_halfbit_inner
ret
delay100us:
ldi temp,(100*4-9)/3 ; Delay 100 ?s at 4 MHz and 3 cycles per loop
delay100us1:
dec temp
brne delay100us1
ret
delay5us:
ldi temp,(5*4-9)/3 ; Delay 5 ?s at 4 MHz and 3 cycles per loop
delay5us1:
dec temp
brne delay5us1
ret
;debug
uart_reciev:
in RxByte,UDR
;rcall avr_to_pc
;ldi TxByte,0xEE
ldi TxByte,0xEE
cpi RxByte,0x02
brne nextcomp1
ldi TxByte,0xF3
rjmp klart
nextcomp1:
cpi RxByte,0x03
brne nextcomp2
ldi TxByte,0x00
rjmp klart
nextcomp2:
cpi RxByte,0x04
brne nextcomp3
;ldi TxByte,0b00000010
ldi TxByte,0x20
rjmp klart
nextcomp3:
klart:
;ldi TxByte,0x02
;rcall avr_to_keyb
rcall avr_to_keyb
; in temp,UDR
waitloop:
sbis USR,UDRE
rjmp waitloop
;out UDR,temp
ret
;debug
uartsend:
us_waitloop:
sbis USR,UDRE
rjmp us_waitloop
out UDR,temp
ret
.MACRO m_clear_keyb_ck
sbi DDRB,KBD_CK
.ENDMACRO
.MACRO m_release_keyb_ck
cbi DDRB,KBD_CK
.ENDMACRO
.MACRO m_clear_keyb_data
sbi DDRB,KBD_DATA
.ENDMACRO
.MACRO m_release_keyb_data
cbi DDRB,KBD_DATA
.ENDMACRO
clear_pc_data: ;(set port as output, port is already low)
mov temp2,LoopPC
lsl temp2
in temp3,DDRB
or temp3,temp2
out DDRB,temp3
ret
release_pc_data: ;(set port as input, pullup makes it high)
mov temp2,LoopPC
lsl temp2
com temp2
in temp3,DDRB
and temp3,temp2
out DDRB,temp3
ret
clear_pc_ck: ;(set port as output, port is already low)
mov temp2,LoopPC
in temp3,DDRB
or temp3,temp2
out DDRB,temp3
ret
release_pc_ck: ;(set port as input, pullup makes it high)
mov temp2,LoopPC
com temp2
in temp3,DDRB
and temp3,temp2
out DDRB,temp3
ret
read_pc_ck:
in temp3,PINB
and temp3,LoopPC
ret ;returns z=1 if PINB.x=0 (data low)
read_pc_data:
in temp3,PINB
lsr temp3
and temp3,LoopPC
ret ;returns z=1 if PINB.x=0 (ck low)
;#############################################################
;# avr_to_pc #
;# Transmits the byte in TxByte to the pc #
;#############################################################
avr_to_pc:
cbr Status,1<<ERROR ; Reset the error bit
ldi temp, 50*4/25 ; Reset delay to 75 ?s at 4 MHz and 25 cycles per loop
atp_Wait_idle: ; Check if both CLK and DATA lines are idle (high)
rcall read_pc_data
breq atp_Abort ; Bus is not idle, so abort
rcall read_pc_ck
breq atp_Abort ; Bus is not idle, so abort
dec temp ; Check again if not yet idle for 75 ?s
brne atp_Wait_idle
ldi bitcnt, 11 ; Number of bits including start bit, 8 data bits, parity bit and stop bit
clr parity_cnt ; Clear the parity counter
clc ; Clear carry to send a zero as the first bit (start bit)
atp_Shift_data:
brcs atp_Output_1 ; Set DATA according to the carry flag
rcall clear_pc_data ; Take DATA low
rjmp atp_Send_clock
atp_Output_1:
inc parity_cnt ; Count # of ones in the data byte
rcall release_pc_data ; Release DATA
atp_Send_clock:
rcall clear_pc_ck ; Take CK low
rcall Delay_halfbit
rcall release_pc_ck ; Release CK
rcall Delay_halfbit
dec bitcnt
breq atp_Send_done ; No bits left: we're done
rcall read_pc_ck ; If host takes clock low during transmission, abort and
breq atp_Abort ; exit subroutine
cpi bitcnt, 1
breq atp_Stop_bit ; One bit left: the stop bit
cpi bitcnt, 2
breq atp_Parity_bit ; Two bits left: time for the parity bit
ror TxByte ; >2 bits left: rotate next bit in data byte to carry flag
rjmp atp_Shift_data
atp_Parity_bit:
inc parity_cnt ; Add 1 to the number of ones in the data byte (odd parity)
ror parity_cnt ; Set carry flag to the lsb of the parity counter+1
rjmp atp_Shift_data
atp_Stop_bit:
sec ; Set carry to send a one as the last bit (stop bit)
rjmp atp_Shift_data
atp_Abort:
rcall release_pc_data ; Make sure we don't pull data low
sbr Status,1<<ERROR ; We couldn't send the byte
rjmp atp_very_done
atp_Send_done:
;rcall read_pc_ck
;breq atp_Send_done ; Bus is not idle, so loop until it is
atp_very_done:
ret
;#############################################################
;# pc_to_avr #
;# Receive one byte from pc to RxByte #
;#############################################################
pc_to_avr:
;debug
;rcall release_pc_ck
cbr Status, (1<<VALID)|(1<<ERROR) ; Reset valid and error bits
rcall read_pc_ck
brne pta_Done ; Abort if host is not requesting to send
m_clear_keyb_ck ; Set ~CTS to keyboard to stop it from sending
pta_Wait_ck_high: ; Clock is low, so pc is requesting attention
;if timer overflowed, quit routine, means that this computer is not ok (disconnected or off)
in temp,TIFR
sbrc temp,TOV1
rjmp pta_Timeout
rcall read_pc_ck
breq pta_Wait_ck_high ; Wait until ck goes high again
rcall read_pc_data
brne pta_Done ; Abort if ck goes high w/o data=0
; (no request_to_send, only ~clear_to_send)
;; Otherwise start clocking in the data
clr bitcnt ; Clear the bit slot counter
clr parity_cnt ; Clear the parity counter
pta_Clock_cycle:
rcall Delay_halfbit
rcall clear_pc_ck ; Take ck low
rcall Delay_halfbit ; Pc will set data during this time
rcall release_pc_ck ; Release ck
cpi bitcnt, 8
brlo pta_Data_bit ; bit 0-7: Data bits, clock them in
breq pta_Parity_bit ; bit 8: Parity bit, check it
cpi bitcnt, 10
brlo pta_Ack ; bit 9: Ack, send it
breq pta_End_ack ; bit 10: End of ack
dec bitcnt ; bit 11+: Pc holds data low,
rjmp pta_Bit_done ; keep sending clocks until it's released
pta_Data_bit:
rcall read_pc_data ; Read data bit from pc
breq pta_Input0 ; Set carry to the data bit
sec
inc parity_cnt ; Increase the parity counter if bit=1
rjmp pta_Shift_data
pta_Input0:
clc
pta_Shift_data:
ror RxByte ; Shift carry into RxData, lsb first
rjmp pta_Bit_done
pta_Parity_bit:
rjmp pta_Bit_done ; For the moment, we don't give a f**k
pta_Ack:
rcall clear_pc_data ; Send ack by pulling data low during 10th bit
rjmp pta_Bit_done
pta_End_ack:
rcall release_pc_data ; Release data again after ack sent
;rjmp pta_Bit_done ;(pointless)
pta_Bit_done:
inc bitcnt ; Increase bit slot counter
cpi bitcnt, 11 ; Abort reception if pc holds ck low before 11th bit
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -