📄 pckb1200.asm
字号:
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 + -