⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 pckb1200.asm

📁 把PC键盘变成MIDi音符控制适配器电路单片机程序
💻 ASM
📖 第 1 页 / 共 2 页
字号:
		out		MCUCR,irqtemp
		ldi		irqtemp, (1<<INT0)
		out		GIMSK,irqtemp      ;  enable external INT0 - General Interrupt Mask Register
		
		reti       	


;**************************************************************************************************************************
;  DECODE_DATA  -- subroutine to convert the scancode from the keyboard
;   input: scancode (register value)   output = DecodeValue (register value)
;
;   DecodeValue = 0  ==  key not found in the table nor is it a shift or extended key
;   DecodeValue < $80 (alphanumeric keys) == turn on or off MIDI note (depending on whether key is pressed down or released)
;   DecodeValue = $80 (spacebar)          == turn off all notes
;   DecodeValue > $80 ( function keys)    == change voice
;
;    A key pressed down will send the one-byte scancode to the PC for most characters.
;    When the key is released (key is let up after being pressed down), the keyboard 
;      sends an $f0 byte followed by the scancode of the key being released.
;    This routine uses a flag called Break to determine if the scancode comes from a keypress down or release.
; Break == clear means that scancode just received is a keypress down and should be decoded to Note On for that keypress.
; Break == set means that the scancode just received was $f0 and the next scancode means turn Note Off for that keypress.
;**************************************************************************************************************************  
decode_data:
;   first check if the scancode is the keyboard acknowledge byte
		cpi		scancode,KBD_ACK
		brne		chkBreakFlag
		rjmp		ACKset
	
;   first check that Break flag is clear *** 
chkBreakFlag:
		sbrc		keyflags, Break_flag   ; Break flag is bit 4
		rjmp		Break_set

;   Break is clear, so do check the first three special conditions	
		cpi		scancode,$f0  ; 	$F0 =Breakcode  finger-off-the-key identifier
		brne		notF0
		sbr		keyflags, (1<<Break_flag)  ; set Break flag  	
		rjmp		setZero
	
notF0: 	    	
		cpi		scancode,$12  ;   $12   Left SHIFT
		brne		not12
		sbr		keyflags,(1<<Shift_flag) ; set shift flag	
		rjmp		setZero
	
not12: 	    	
		cpi		scancode,$59  ;  $59  Right SHIFT
		brne		isExtendedCode
		sbr		keyflags,(1<<Shift_flag) ; set shift flag			
		rjmp		setZero

isExtendedCode: ; check for E0 flag
		cpi		scancode,$e0  ;  $e0  extended char table
		brne		isE0set  ; scancode is not one of the three special cases
		sbr		keyflags,(1<<E0_flag) ; set E0 flag			
		rjmp		setZero	
	
isE0set: ; check if extended char scancode was previously sent
		sbrc		keyflags,E0_flag  ; test extended char flag 
		rjmp		get_E0char
	
;no special chars, so do a table lookup from the EEPROM section 
       		clr 		EEPROM_Ptr
cmpnext: 
		out 		EEAR,EEPROM_Ptr ;Set the EEPROM's address
		sbi 		EECR,EERE        ;Send the Read strobe
		in		r0,EEDR           ;Put the data in the transmit register
		
		tst 		r0               ; reached the end of the table?
		breq 		setZero
		cp		r0,scancode
		breq 		scan_found
		
		inc 		EEPROM_Ptr
		rjmp 		cmpnext
scan_found:
		mov 		DecodeValue,EEPROM_Ptr
		sbr		keyflags,(1<<OnOff_flag)  ; set the note on/off flag		
		rjmp 		exit_decode
	
; scancode when shift_flag is set is same as unshifted but the decode value comes from the shiftkey table
;		sbrc		keyflags,Shift_flag  ; test shift flag 
;		rjmp		get_shiftchar

;  Break flag is set - this means the previous scancode was $f0 (a key release)
;     If the key being released is a shift key, then the shift flag should be cleared.
Break_set:
		cbr		keyflags,(1<<Break_flag) ; clear Break flag
;  is the key being released a SHIFT key?
		cpi		scancode,$12  ;   $12  Left SHIFT key released
		brne		not12a
		cbr		keyflags,(1<<Shift_flag) ; clear shift flag
		rjmp		setZero
		
not12a: 	
		cpi		scancode,$59  ;  $59   Right SHIFT key released
		brne		turnNoteOFFflag
		cbr		keyflags,(1<<Shift_flag) ; clear shift flag
		rjmp		setZero
	
turnNoteOFFflag:
		cbr		keyflags,(1<<OnOff_flag)  ; clear the note on/off flag
;determine which note needs to be turned off
       		clr 		EEPROM_Ptr
cmpnxt1: 
		out 		EEAR,EEPROM_Ptr ;Set the EEPROM's address
		sbi 		EECR,EERE        ;Send the Read strobe
		in		r0,EEDR           ;Put the data in the transmit register
		
		tst 		r0               ; reached the end of the table?
		breq 		setZero      ; not found so turn all off
		cp		r0,scancode
		breq 		scan_fnd1
		
		inc 		EEPROM_Ptr
		rjmp 		cmpnxt1
scan_fnd1:
		mov 		DecodeValue,EEPROM_Ptr
		rjmp		exit_decode
setZero:  
		clr		DecodeValue
exit_decode: 				; subroutine single exit point
		ret

ACKset:		sbr		keyflags, (1<<ACK_flag)  ; set keyboard acknowledge flag
		rjmp		setZero

get_E0char:
		cbr		keyflags,(1<<E0_flag)  ; clear the E0 flag
		cpi 		scancode,$75  ; up arrow
		brne		isDownArrow
		mov		temp,NoteOffsetValue
		cpi		temp,$7f-12
		brsh		setZero  ; branch is same or higher (unsigned)
		subi		temp,-12
		mov		NoteOffsetValue,temp
		rjmp		setZero
	
isDownArrow:
		cpi 		scancode,$72  ; down arrow
		brne		setZero
		mov		temp,NoteOffsetValue
		cpi		temp,12
		brlo		setZero  ; branch is same or higher (unsigned)
		subi		temp,12
		mov		NoteOffsetValue,temp
		rjmp		setZero
; ***** end of DecodeData subroutine *******************


;*********************************************************
;. playMIDInote    not a subroutine
;   receives: keyflags, DecodeValue 
;   registers used:  TxData
playMIDInote:
		ldi		ZL,FIRST_STORAGE_REGISTER - 1
		ldi		regcnt,NUMBER_of_POSSIBLE_NOTES
PMN1:	inc		zl  ; first check 8 storage registers to see if the note is already playing
		ld		temp,z
		cp		temp, DecodeValue
		breq		PMN3 ; found identical note number, which means that the Note had been already playing.
;						The key was held down long enough trigger typematic auto-repeat.
		dec		regcnt
		breq		NewNote
		rjmp		PMN1
				
NewNote: 	ldi		ZL,FIRST_STORAGE_REGISTER - 1
		ldi		regcnt,NUMBER_of_POSSIBLE_NOTES
PMN2:	inc		zl  
		dec		regcnt
		breq		PMN3
		ld		temp,z ; now check the 8 storage registers to see if there is a voice available 
		cpi		temp,REGISTER_AVAILABLE  ; to play the new note
		brne		PMN2
		st		z,DecodeValue
		
		ldi		TxData,NOTE_ON_MSG
		rcall		putMIDI
		add		DecodeValue,NoteOffsetValue
		mov		TxData,DecodeValue
		rcall		putMIDI
		ldi		TxData,NOTE_ON_VELOCITY
		rcall		putMIDI        
		rjmp		getchar     	
		
PMN3: ; found identical note number  
;	key held down long enough trigger typematic auto-repeat
	
PMN_OutOfRegs:
		rjmp		getchar

;*********************************************************
;. TurnOffMIDInote    not a subroutine
;   receives: keyflags, DecodeValue 
;   registers used:  TxData,keyflags, regcnt
TurnOffMIDInote:
		ldi		ZL,FIRST_STORAGE_REGISTER - 1
		ldi		regcnt,NUMBER_of_POSSIBLE_NOTES
ToMN1:
		inc		zl

		ld		temp,z	
		cp		temp,DecodeValue
		breq		ToMN2
		
		dec		regcnt
		breq		ToMN_OutOfRegs
			
		rjmp		ToMN1
	
ToMN2: ; found note number in register storage
		ser		temp
		st		z,temp  ; let program that this reg is available 
		ldi		TxData,NOTE_ON_MSG
		rcall		putMIDI
		add		DecodeValue,NoteOffsetValue	
		mov		TxData,DecodeValue
		rcall		putMIDI
		ldi		TxData,NOTE_OFF_VELOCITY
		rcall		putMIDI
		rjmp		getchar
		
ToMN_OutOfRegs:	rjmp		AllNotesOff  ; couldn't find note in storage so turn off all notes
	
	
;***************************************************************************
;*
;* "putMIDI"   Data bits are fed to an output using a timing loop, not an interrupt.
;*                    This is a subroutine.  It returns to the code that called it after the data is finished 
;*			transmitting (after about 325 microseconds).
;*
;* This subroutine transmits the byte stored in the "TxData" register
;*
;* Number of words	:14 including return
;* Number of cycles	:Depends on bit rate
;* Low registers used	:None
;* High registers used	:2 (txbitcnt,TxData)
;* Pointers used	:None
;*
;*  time used  == 10 * halfbit_delay + @120 cycles
;*  subroutine does not exit until finished
;
;delay-----|----|-----|----|-----|----|-----|----|----|----|------
;data-\___/----\___/-----\___/-----\___/----\___/-----------
;Txbit  10 9  9 8 8 7  7 6 6 5  5 4 4 3  3 2 2 1  1 0
;Tx    start    0     1     2      3      4      5     6     7   stop  
.equ	b	= 36	; ((b-1)*3) +10 = 116 cycles = 32uSec [clock period = 279.4nS]
;***************************************************************************

putMIDI:	ldi		txbitcnt,10	; 1+8+sb (sb is # of stop bits)
		com		TxData		; Invert everything [the lsr always puts a 0 into the carry]
		sec				; Start bit
putMIDI0:	brcc		putMIDI1		; 1 or 2
		cbi		PORTD,MIDItxpin	; 2   send a '0' (clear bit I/O)
		rjmp		putMIDI2		; 2   else	
putMIDI1:	sbi		PORTD,MIDItxpin	; 2   send a '1'
		nop				; 1  	
putMIDI2:	ldi		temp,b 		; 1 cycle
bit_delay1: dec		temp		; 1 cycle
		brne		bit_delay1	; 2 if true, 1 if false
		lsr		TxData		; 1  Get next bit
		dec		txbitcnt	; 1  If not all bit sent
		brne		putMIDI0	; 2  send next
		ret			


;*********************************************************************
;    keypress tables      PC keyboard scancode, value returned in 'DecodeValue'
;*********************************************************************
.eseg

unshifted: 
.db $0e,$0d,$16,$15,$1e,$1d,$26,$24 ; 00 -07  notes A-3 to E-2
.db $2d,$2e,$2c,$36,$35,$3d,$3c,$43 ; 08- 0f   notes F-2 to C-1
.db $46,$44,$45,$4d,$54,$55,$5b,$66 ; 10 -17  notes  C#-1 to G#-1
.db $5d,$4e,$1c,$1a,$1b,$22,$23,$21 ; 18- 1f   notes  A-1 to E0
.db $2a,$34,$32,$33,$31,$3b,$3a,$41 ; 20 -27  notes  F0 to C+1
.db $4b,$49,$4c,$4a,$52,$29,$05,$06 ; 29=spbr  offset $2d
.db $04,$0c,$03,$0b,$83,$0a,$01,$09 ; 30 -37
.db $78,$07, 0				    ; 38 -3a


⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -