📄 pckb.asm
字号:
; ****************************************************************
; * PCkb.asm
; *
; * version 1.32 Oct 12, 2003 Alan Probandt Portland Oregon USA
; *
; * [ Use TimesNewRoman font in text editor to format this file correctly ]
; *
; * [ when programming the AVR chip, don't forget to include the EEPROM file]
; *
; * Read keypresses from PC Keyboard into Atmel AVR 90S2313
; * -Assemble logic changes on AVR pin 6 into PC keyboard scan codes.
; * -Decode keyboard scancodes into MIDI note numbers.
; * -Send MIDI messages out the MIDI out port 31250/8/N/1
; *
; Key Pad - pressing two digits on the keypad changes the voice number.
; for example, pressing '3' then '7' selects voice number 37 (in decimal).
;
; * PC keyboard clock signal on AVR pin 6 (INT1)
; * PC keyboard data signal on AVR pin 7 (PORTD bit 4)
; * MIDI out (31250 baud - 8/N/1) on AVR pin 8 (PB4)
;
; Atmel AVR 2313
; RESET low PIN 1 | VCC PIN 20 +5V
; PIN 2 PD0 | PB7 PIN 19 SCK
; PIN 3 PD1 | PB6 PIN 18
; 3.579MHz PIN 4 XTAL1 | PB5 PIN 17
; must use only PIN 5 XTAL2 | PB4 PIN 16
; INT0 kbd clk PIN 6 PD2 | PB3 PIN 15
; INT1 kbd data PIN 7 PD3 | PB2 PIN 14
; MIDI out PIN 8 PD4 | PB1 PIN 13
; PIN 9 PD5 | PB0 PIN 12
; PIN 10 GND | PD6 PIN 11
;
; ************************************************************
.nolist
.include "2313def.inc"
.list
.equ MIDItx = 4 ; MIDI Transmit pin 8 (PB4)
.equ INITIAL_BITCOUNT = 11 ; used by Get_Scan
.equ PARITY_BITCOUNT = 3 ; used by Get_Scan
.equ NOTEOFFSET = 34 ; check for adjustment -- Is 'Z' key really Middle C?
.equ CHANGE_VOICE_OFFSET = $2e
.equ SPACEBAR_OFFSET = $2d
.equ REGISTER_AVAILABLE = $FF
.equ POSSIBLE_NOTES = 8
.equ NOTE_ON_VELOCITY = 0x50
.equ NOTE_OFF_VELOCITY = 0
.equ NOTE_ON_MSG = $90
.equ CHANGE_VOICE_MSG = $c0
.equ MIDI_CONTROLLER_MSG = $b0
.equ ALL_NOTES_OFF_BYTE = 123 ; standard cmd $7b doesn't work on TQ5
.equ ALL_NOTES_OFF_2nd = 0
.equ CapsToggleValue = 0b01000000
.equ T_flag = 6
.equ Sign_flag = 4
.equ KeyboardClock = 2
.equ KeyboardData= 3
.equ SM_flag = 4 ; Sleep Mode (unused) - delay loop counter bit flag { 0 = long period is timed out}
.equ PushSwitch1 = 4
; keyflag register's bit values
.equ Edge_flag = 7 ; use up or down edge while inputting char from keyboard
.equ Caps_flag = 6 ; Caps Lock pressed on -- no NoteOff transmitted
.equ Shift_flag = 5 ; the shift key (either right or left) is being pressed
.equ Break_flag = 4 ; the last scancode was an $f0 - the break code sent when a key is released
.equ OnOff_flag = 3 ; set means turn on the note whose MIDI number was decoded from the most recent scancode
.equ Alt_flag = 2 ; the Alternate (Alt) key (either right or left) is being pressed
.equ E0_flag = 1 ; the extended scancode $E0 was sent
;*********************************************************************************************
; Register definitions
;
; lower registers r0 - r15
; r0 is temporary storage of byte retrieved from LPM instruction
.def OctaveOffset = r1
.def CurrentVoiceNumber = r2
.def txbitcnt = r3 ; used by PutMIDI
.def TableOffset = r5 ; used by PCKB_main to keep track of where a keypress has been found in a table
.def MIDIcounter = r6 ; FastMIDINumbers, SlowMIDINumbers - which MIDI number is being sent
.def regcnt = r6 ; used by PCKB_main to keep track of which notes are playing by having keys held down
.def OverflowCount = r6 ; used by T0_overflow IRQ to time a quarter-second
.def KPtemp = r6 ; used by PCKB_main to multiply keypad digit by 10
.def ParityCounter = r7 ; count the number of logic hi bits transmitted by the keyboard command output routine
.def SustainValue = r7
; upper registers
.def temp = r16 ; scratch register
.def irqtemp = r17 ; interrupt's scratch register
.def bitcount = r18 ; used by INT0 irq [getscan]
.def scancode = r19 ; storage of data bits received from PC keyboard
.def scanbits = r20 ; shift register for holding PC keyboard data while being assembled
.def keyflags = r21 ; status of previous scancodes. used to decode multiple scancode keypresses
.def DecodeValue = r22 ; PC keyboard char decoded to NoteOn value, ChangeVoice value, or AllNotesOff cmd
.def KPvalue = r23 ; the binary value of the pressed keypad digits. Used for new voice value.
.def Velocity = r24 ; the MIDI note-On volume value
.def TxData = r25 ; the byte being sent out the MIDI port - PutMIDI
.dseg
.org 0x60
FNtable: .BYTE 12
SoundingNotes: .BYTE POSSIBLE_NOTES
.cseg
.org $0000
; AT90S2313 interrupt table
rjmp reset ; Reset handler $000
.org INT0addr
rjmp get_scan ; External interrupt 0 handler $001 (pin 6) PC keyboard clock
.org INT1addr
reti ; External interrupt 1 handler $002
.org ICP1addr
reti ; Timer1 capture event handler $003
.org OC1addr
reti ; Timer1 compare match $004
.org OVF1addr
reti ; Timer1 overflow handler $005
.org OVF0addr
reti ; Timer0 overflow handler $006
.org URXCaddr
reti ; UART Rx Complete $007
.org UDREaddr
reti ; UART Data Register empty $008
.org UTXCaddr
reti ; UART Tx Complete $009
.org ACIaddr
reti ; Analog comparator handler $00a
reset:
ldi temp, RAMEND
out SPL ,temp ; init Stack Pointer
; set-up ports for input/output and pull-up resistors
ser temp
out portb,temp ; port B pull-up resistors are all on
out portd,temp ; port D pull-up resistors are all on
clr temp
out ddrb,temp ; port b is all inputs
ldi temp,(1<<MIDItx)
out ddrd,temp ; port D4 is output for MIDI
;*******************************************************************************************
;
; Get keypresses from standalone PC keyboard - convert to notes and send to MIDI port
;
; Subroutines: 'decode_data'
;
; Interrupt procedures: external interrupt int0 -- 'getscan'
;
; Most time spent in 'GetChar' which checks T flag for fully received keypress from PC keybrd
; When T is set, 'decode_data' called to convert keypress to note or cmd.
;*******************************************************************************************
;
PCkb_Main:
ldi temp,(1<<ISC01) ; setup INT0 interrupt on falling edge
out MCUCR, temp
ldi temp, (1<<INT0)
out GIMSK,temp ; enable external INT0 - General Interrupt Mask Register
sei ; switch on the AVR general interrupt enabler
clr keyflags
clr KPvalue
ldi bitcount,INITIAL_BITCOUNT ; = 11
clt ; T flag used to indicate that a char from kybd is ready to transmit
ldi temp, NOTEOFFSET
mov OctaveOffset, temp
clr temp ; set up the Function key voice select table with voices 00 - 0b
ldi ZH, high (FNtable)
ldi ZL, low (FNtable)
rst2: st z+, temp
inc temp
cpi temp, 12 ; the number of Function keys on a PC keyboard
brlo rst2
clr CurrentVoiceNumber ; set synth voice to 00h on AVR reset
rcall ChangeVoice
; setup completed -------- enter MAIN LOOP here
GetChar:in temp, SREG ; MAIN LOOP of program 99.9% of time spent here
; if T flag (SREG bit 6) is set, then char from keyboard is ready to transmit
sbrs temp,T_flag ; if T flag (SREG bit 6) is set, then char from keyboard is ready to transmit
rjmp GetChar
clt ; clear T flag - prepare for next char
rcall decode_data ; convert the scancode from the keyboard into an ASCII character
; send scancode and keyflags - return DecodeValue
tst DecodeValue ; if DecodeValue =0, then ignore scancode - it's a shift character or break scancode
brne Valid_key
rjmp GetChar
; DecodeValue less than $3f turns on/off note, $80-$89 are keypad numbers, $40-$4b map function keys F1 to F12, $c0 is spacebar
Valid_key:
cpi DecodeValue, $c0 ; space bar key
breq DoSubAllNotesOff
mov temp, DecodeValue
andi temp,0xc0
cpi temp,0x40 ; is the scancode a Function key activation?
brne VkyKP ; no, check for keypad press
rjmp FnKeyChangeVoice
VkyKP: mov temp, DecodeValue ; is the scancode a keypad press?
andi temp,0xc0 ; isolate the two most significant bits 1100 0000
cpi temp,0x80 ; test if the two most sig bits are '10'
brne VkyMN ; no, not a keypad press. So must be a note on/off.
rjmp DoKeyPad ; yes, scancode is a keypad activation.
VkyMN: andi DecodeValue,0b00111111 ; DecodeValue is lower than $3f
rjmp DoNote
DoSubAllNotesOff:
rcall AllNotesOff
rjmp GetChar
;*********************************************************
; Function Key Press - Assign Voice number to Fn key
;*********************************************************
FnKeyChangeVoice:
mov temp,DecodeValue ; DV will be $40 to $4b
andi temp, 0b00001111
; check Alt flag, if set then assign the Current Voice Number into the selected Function key.
; if Alt flag is clear, then change voice to the number assigned to the selected Function key.
sbrs keyflags, Alt_Flag
rjmp FKCV0 ; Alt_flag is set so jump to FKCV0
ldi ZH, high (FNtable) ; get the Function key's voice number
ldi ZL, low (FNtable)
add ZL, temp ; ZL plus Function key number will never be greater than $ff
st z, CurrentVoiceNumber
rjmp FKCV2
FKCV0: ldi ZH, high (FNtable) ; Alt_key is not pressed - Fn key pressed by itself
ldi ZL, low (FNtable) ;- send to the synth the number assigned to the pressed Function key
add ZL, temp ; ZL plus Function key number will never be greater than $ff
FKCV1: ld temp, z
mov CurrentVoiceNumber, temp
rcall ChangeVoice
FKCV2: rjmp GetChar
;***************************************************************************
; Key Pad - pressing two digits on the keypad changes the voice number.
; for example, pressing '3' then '7' selects voice number 37 (in decimal).
;****************************************************************************
DoKeyPad:
mov temp,DecodeValue ; DV will be $80 to $8f
andi temp, 0b00001111
; check if keypress is a digit. If yes, then check if 1st, 2nd, or third digit of voice number in base10
cpi temp, 10
brlo KP_digit ; yes it's a digit (0 to 9)
cpi temp, $0a ; was the NumLock key pressed
brne KP_isStar ; no
rjmp GetChar ; yes, do nothing for now
KP_isStar: cpi temp, $0b ; was the multiply on the keypad pressed?
brne KP_isMinus ; no
rjmp GetChar ; yes, do nothing for now
KP_isMinus: cpi temp, $0c ; was the minus sign on the keypad pressed?
brne KP_isPlus ; no
rjmp GetChar ; yes, do nothing for now
KP_isPlus: cpi temp, $0d ; was the plus sign on the keypad pressed?
brne KP_isPoint ; no
rjmp GetChar ; yes, do nothing for now
KP_isPoint: cpi temp, $0d ; was the decimal point on the keypad pressed?
brne KP_exit ; no
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -