; ; bmcnv.asm MIDI converter for PSX BeatMania Controler V1.0 ; for Microchip PIC16F84-10 ; Copyright(C)1998 By H.Kashima ; ; 98/11/28 Start ; 98/12/05 Ver1.0 ; ; ; PIN I/O ; ; RA0: MIDI OUT in ; 1: n.c. ; 2: /LED out ; 3: MIDI IN out ; 4: n.c. ; RB0: data in ; 1: cmd out ; 2: mode0 in ; 3: mode1 in ; 4: /sel1 out ; 5: /sel2 out ; 6: clk out ; 7: ack in title bmcnv processor 16f84 radix dec list c = 110, n = 88 ;#define debug 1 #include __CONFIG _CP_OFF & _WDT_OFF & _HS_OSC & _PWRTE_ON #define BS(x) ( 1 << x ) ; bit set ; supplyed clock #define XTAL 8000000 ; supplyed clock #define RTCLK ( XTAL / 4 ) ; internal timer clock, @500ns ; PORTA contents #define P_MOUT 0 #define P_LED 2 #define P_MIN 3 ; PORTB contents #define P_DAT 0 #define P_CMD 1 #define P_MODE0 2 #define P_MODE1 3 #define P_SEL1 4 #define P_SEL2 5 #define P_CLK 6 #define P_ACK 7 ; PSX_DATA[2] contents #define PSP0_LEFT 7 #define PSP0_DOWN 6 #define PSP0_RIGHT 5 #define PSP0_UP 4 #define PSP0_START 3 #define PSP0_SELECT 0 ; PSX_DATA[3] contents #define PSP1_SQUARE 7 #define PSP1_CROSS 6 #define PSP1_CIRCLE 5 #define PSP1_TRIANG 4 #define PSP1_R1 3 #define PSP1_L1 2 #define PSP1_R2 1 #define PSP1_L2 0 ;MODE_F contents #define M_1PCON 0 ; 1P connected #define M_2PCON 1 ; 2P connected #define M_TMMAP 5 ; TypeMania mode #define M_ALLMAP 6 ; liner keymap mode #define M_BULK 7 ; exclusive bulk mode ;PREV_ETC contents #define M_TURNED 0 ; table direction changed flag ;LED Delay cycle #define LED_DELAY 32 ;TurnTable ignore count value for TypeMania #define TM_IGNCNT 24 ; ; work area ; cblock 0x0c PSX_DAT1:8 ; 1P buffer PREV_ALLOW1, PREV_KEY1 ; previous key conditions PREV_ETC1 PSX_DAT2:8 ; 2P buffer PREV_ALLOW2, PREV_KEY2 PREV_ETC2 VAR1, VAR2, VAR3, VAR4, VAR5 ; variables AM_DAT1, AM_DAT2 ; temp. for allmap mode AM_PRV1, AM_PRV2 V_WAIT, SDATA, RDATA ; shift registers MODE_F, LEDTMR ; others endc ; ; ; program code ; ; reset vector ; org 0x000 goto start ; interrupt vector ; ; org 0x004 ; goto int_sub ; ; cold start ; MCU initialize ; start BANKSEL OPTION_REG ; bank select RP0 = 1 movlw BS(PS2) | BS(PS1) | BS(PS0) movwf OPTION_REG ; pullup PORTB, PS=1:256 movlw b'11110011' ; initialize I/O port movwf TRISA movlw b'10001101' movwf TRISB BANKSEL PORTA ; bank select RP0 = 0 movlw 0xff movwf PORTA ; initialize output movwf PORTB movwf PREV_KEY1 movwf PREV_ALLOW1 movwf PREV_KEY2 movwf PREV_ALLOW2 movlw LED_DELAY movwf LEDTMR clrf PREV_ETC1 clrf PREV_ETC2 clrf MODE_F ; set convertion mode ; comf PORTB, W andlw b'00001100' btfsc STATUS, Z goto chkkey movwf VAR1 btfss VAR1, P_MODE1 ; PB3 bulk mode goto setc1 bsf MODE_F, M_BULK goto mloop setc1 btfss VAR1, P_MODE0 ; PB2 keymap select goto setc2 bsf MODE_F, M_ALLMAP goto mloop setc2 bsf MODE_F, M_TMMAP goto mloop ; ; mode pin has not specified, set mode by controler button ; chkkey movlw 4 ; configure retry movwf VAR2 chkk0 movlw 128 ; wait 16ms call w128 bcf PORTB, P_SEL1 ; SEL1 goes low movlw PSX_DAT1 ; set buffer top call GetPSXC ; get data from 1P controler bsf PORTB, P_SEL1 ; SEL1 goes high andlw 0xff ; read error? btfsc STATUS, Z goto chkk0a ; 1P connected decfsz VAR2, F goto chkk0 goto getEEP ; not configured chkk0a btfsc PSX_DAT1 + 2, PSP0_START goto chkk1 bsf MODE_F, M_BULK ; START button = bulk mode goto setEEP chkk1 movlw 0x41 ; check normal control pad ID subwf PSX_DAT1, W btfss STATUS, Z goto getEEP btfsc PSX_DAT1 + 3, PSP1_CROSS goto chkk2 bsf MODE_F, M_ALLMAP ; SQUARE button = all mapped mode goto setEEP chkk2 btfsc PSX_DAT1 + 3, PSP1_SQUARE goto getEEP bsf MODE_F, M_TMMAP ; default = TypeMania mode setEEP movfw MODE_F ; store config to EEPROM movwf EEDATA movlw 0 movwf EEADR call eewrite movlw 4 ; blink LED, mode configured movwf VAR1 blk1 bcf PORTA, P_LED ; LED on movlw 5 movwf VAR2 blk2 movlw 195 ; wait 25ms call w128 decfsz VAR2, F goto blk2 ; wait 125ms bsf PORTA, P_LED ; LED off movlw 5 movwf VAR2 blk3 movlw 195 ; wait 25ms call w128 decfsz VAR2, F goto blk3 ; wait 125ms decfsz VAR1, F goto blk1 goto mloop getEEP movlw 0 ; restore config form EEPROM call eeread movwf MODE_F ; ; Main Loop ; mloop bcf PORTB, P_SEL1 ; SEL1 goes low movlw PSX_DAT1 ; set buffer top call GetPSXC ; get data from 1P controler bsf PORTB, P_SEL1 ; SEL1 goes high andlw 0xff ; read error? btfsc STATUS, Z goto ml1 ; connected bcf MODE_F, M_1PCON ; clear connect flag movlw 0xff movwf PREV_ALLOW1 movwf PREV_KEY1 clrf PREV_ETC1 goto ml2 ml1 bsf MODE_F, M_1PCON ; set connect flag ml2 bcf PORTB, P_SEL2 ; SEL2 goes low movlw PSX_DAT2 ; set buffer top call GetPSXC ; get data from 2P controler bsf PORTB, P_SEL2 ; SEL2 goes high andlw 0xff ; read error? btfsc STATUS, Z goto ml3 bcf MODE_F, M_2PCON ; clear connect flag movlw 0xff movwf PREV_ALLOW2 movwf PREV_KEY2 clrf PREV_ETC2 goto ml4 ml3 bsf MODE_F, M_2PCON ; set connect flag ; bulk mode proccessing ; ml4 btfss MODE_F, M_BULK goto ml5 ; ; bulk dump mode ; put MIDI Exclusive message ; + 0 0xf0 MIDI Exclusive ; + 1 0x00 maker ID ; b7 b6 b5 b4 b3 b2 b1 b0 ; + 2 0 <-------data-------> 1P bulk data ; + 6 | ; + 7 1P remained bit7 datas ; + 8 0 <-------data-------> 2P bulk data ; +15 2P remained bit7 datas ; +16 0xf7 End of Exclusive ; movlw 0xf0 call MIDIout movlw 0 call MIDIout movlw PSX_DAT1 movwf VAR4 call blk2mid movlw PSX_DAT2 movwf VAR4 call blk2mid movlw 0xf7 call MIDIout goto ml8 ; loop end proc ; TypeMania proccessing ; ml5 btfss MODE_F, M_TMMAP goto ml7 ; (PSX_DATA 1P) -> (SDATA MIDI) ; PSP0_UP = ControlChange(#0x42:Softenute) ; PSP0_DOWN = ControlChange(#0x42:Softenute) ; PSP1_SQUARE = C3(0x3c) ; PSP1_L1 = C#3(0x3d) ; PSP1_CROSS = D3(0x3e) ; PSP1_R1 = D#3(0x3f) ; PSP1_CIRCLE = E3(0x40) btfss MODE_F, M_1PCON ; connected? goto ml6 movlw 0x41 ; check normal control pad ID subwf PSX_DAT1, W btfss STATUS, Z goto ml6 movlw PSX_DAT1 movwf VAR4 movlw 0x3c movwf VAR2 movlw BS(PSP1_SQUARE) movwf VAR3 call key2mid movlw 0x3d movwf VAR2 movlw BS(PSP1_L1) movwf VAR3 call key2mid movlw 0x3e movwf VAR2 movlw BS(PSP1_CROSS) movwf VAR3 call key2mid movlw 0x3f movwf VAR2 movlw BS(PSP1_R1) movwf VAR3 call key2mid movlw 0x40 movwf VAR2 movlw BS(PSP1_CIRCLE) movwf VAR3 call key2mid movfw PSX_DAT1 + 3 ; save current key movwf PREV_KEY1 ; ;turn table movlw 0x42 ; C.C. softenute movwf VAR2 call TM2mid ; (PSX_DATA 2P) -> (SDATA MIDI) ; PSP0_UP = ControlChange(#0x40:Hold1) ; PSP0_DOWN = ControlChange(#0x40:Hold1) ; PSP1_SQUARE = C3(0x48) ; PSP1_L1 = C#3(0x49) ; PSP1_CROSS = D3(0x4a) ; PSP1_R1 = D#3(0x4b) ; PSP1_CIRCLE = E3(0x4c) ml6 btfss MODE_F, M_2PCON ; connected? goto ml7 movlw 0x41 ; check normal control pad ID subwf PSX_DAT2, W btfss STATUS, Z goto ml7 movlw PSX_DAT2 movwf VAR4 movlw 0x48 movwf VAR2 movlw BS(PSP1_SQUARE) movwf VAR3 call key2mid movlw 0x49 movwf VAR2 movlw BS(PSP1_L1) movwf VAR3 call key2mid movlw 0x4a movwf VAR2 movlw BS(PSP1_CROSS) movwf VAR3 call key2mid movlw 0x4b movwf VAR2 movlw BS(PSP1_R1) movwf VAR3 call key2mid movlw 0x4c movwf VAR2 movlw BS(PSP1_CIRCLE) movwf VAR3 call key2mid movfw PSX_DAT2 + 3 ; save current key movwf PREV_KEY2 ; ;turn table movlw 0x40 ; C.C. Hold.1 movwf VAR2 call TM2mid goto ml8 ; loop end proc ; All keymap proccessing ; ml7 btfss MODE_F, M_ALLMAP goto ml8 btfss MODE_F, M_1PCON ; connected? goto ml7a movlw 60 ; MIDI C3 movwf VAR2 movfw PSX_DAT1 + 2 ; push psx datas movwf AM_DAT1 movfw PSX_DAT1 + 3 movwf AM_DAT2 movfw PREV_ALLOW1 movwf AM_PRV1 movfw PREV_KEY1 movwf AM_PRV2 call ModeA ; All keymap movfw PSX_DAT1 + 2 ; save current condition movwf PREV_ALLOW1 movfw PSX_DAT1 + 3 movwf PREV_KEY1 ml7a btfss MODE_F, M_2PCON ; connected? goto ml8 movlw 84 ; MIDI C5 movwf VAR2 movfw PSX_DAT2 + 2 ; push psx datas movwf AM_DAT1 movfw PSX_DAT2 + 3 movwf AM_DAT2 movfw PREV_ALLOW2 movwf AM_PRV1 movfw PREV_KEY2 movwf AM_PRV2 call ModeA ; All keymap movfw PSX_DAT2 + 2 ; save current condition movwf PREV_ALLOW2 movfw PSX_DAT2 + 3 movwf PREV_KEY2 ; ; ml8 movlw LED_DELAY ; light timer subwf LEDTMR, W btfsc STATUS, Z goto ml9 incf LEDTMR, F ; count up timer goto mloop ml9 bsf PORTA, P_LED ; light down LED goto mloop ; ; Subroutines ; ; ; ; bulk send to MIDI ; VAR4: PSX buffer top ; variable: VAR2, VAR3 blk2mid movfw VAR4 movwf FSR movlw 8 movwf VAR3 b2m movlw 7 subwf VAR3, W ; skip fixed data 'Z' btfsc STATUS, Z goto b2m_2 rlf INDF, W ; save bit7 rrf VAR2, F movlw 0x7f andwf INDF, W ; MIDI is 7bit call MIDIout b2m_2 incf FSR, F decfsz VAR3, F goto b2m rrf VAR2, W andlw 0x7f ; send remain all bit7s call MIDIout return ; ; key2mid ; VAR2: midi key number, VAR3: Key mask, VAR4: PSX_DAT top ; variable: VAR5 key2mid movfw VAR4 addlw 3 movwf FSR ; FSR = PSX_DAT[3] comf INDF, W ; check current condition andwf VAR3, W movwf VAR5 ; VAR5 = ~PSX_DAT[3] & MASK btfsc STATUS, Z goto k2m_1 ; button released movlw 6 ; button push downed addwf FSR, F ; FSR = PSX_DAT[9], PREV_KEY comf INDF, W ; check previous condition andwf VAR3, W subwf VAR5, W ; if( ( ~PREV & MASK ) == VAR5 ) btfsc STATUS, Z goto k2m_2 ; condition not changed movlw 0x90 ; condition changed, NOTE ON call MIDIout movfw VAR2 ; MIDI key value call MIDIout movlw 0x7f ; velocity 127 call MIDIout goto k2m_2 k2m_1 movlw 6 ; button push downed addwf FSR, F ; FSR = PSX_DAT[9], PREV_KEY comf INDF, W ; check previous condition andwf VAR3, W subwf VAR5, W ; if((~PREV & MASK) == VAR5) btfsc STATUS, Z goto k2m_2 ; condition not changed movlw 0x80 ; condition changed, NOTE ON call MIDIout movfw VAR2 ; MIDI key value call MIDIout movlw 0x00 ; velocity 0 call MIDIout k2m_2 return ; ; TM2mid, special routine for the turn table controled for TypeMania ; VAR2: ControlChange Function, VAR4: PSX_DAT buffer top ; variable: VAR5 TM2mid movfw VAR4 ; check previous status addlw 10 movwf FSR ; FSR = PSX_DAT[10], PREV_ETC movfw INDF btfsc STATUS, Z goto t2m_0 movlw 1 ; PREV_ETC -= 1 subwf INDF, F btfsc STATUS, Z goto t2m_1 ; just changed goto t2m_end t2m_0 movfw VAR4 addlw 2 movwf FSR ; FSR = PSX_DAT[2] comf INDF, W ; check current condition andlw BS(PSP0_UP) | BS(PSP0_DOWN) movwf VAR5 ; VAR5 = ~PSX_DAT[2] & MASK btfsc STATUS, Z goto t2m_2 ; button released movlw 6 ; button push downed addwf FSR, F ; FSR = PSX_DAT[8] comf INDF, W ; check previous condition andlw BS(PSP0_UP) | BS(PSP0_DOWN) subwf VAR5, W ; if( ( ~PREV & MASK ) == VAR5 ) btfsc STATUS, Z goto t2m_3 ; condition not changed movlw 0xb0 ; condition changed, send Control Change call MIDIout movfw VAR2 ; Function call MIDIout movlw 0x00 ; value(0:OFF) call MIDIout incf FSR, F ; set turned flag incf FSR, F movlw TM_IGNCNT ; set ignore count movwf INDF goto t2m_end t2m_1 clrf INDF ; clear just changed flag movlw 0xb0 ; Control Change call MIDIout movfw VAR2 ; Function call MIDIout movlw 0x7f ; value(0:ON) call MIDIout goto t2m_3 t2m_2 movlw 6 addwf FSR, F ; FSR = PSX_DAT[8] comf INDF, W ; check previous condition andlw BS(PSP0_UP) | BS(PSP0_DOWN) subwf VAR5, W ; if((~PREV & MASK) == VAR5) btfsc STATUS, Z goto t2m_3 ; condition not changed movlw 0xb0 ; Control Change call MIDIout movfw VAR2 ; Control call MIDIout movlw 0x00 ; value(0:OFF) call MIDIout t2m_3 movfw VAR4 addlw 2 movwf FSR ; FSR = PSX_DAT[2] movfw INDF ; store current allow condition movwf VAR5 movlw 6 addwf FSR, F ; FSR = PSX_DAT[8] movfw VAR5 movwf INDF t2m_end return ; ; all keymap from (VAR2) ; VAR2: top midi key number, VAR4: PSX_DAT top ; variable: VAR5 ModeA movlw 16 movwf VAR5 ; loop counter mda1 rrf AM_DAT1, F ; AM_DAT >> 1 rrf AM_DAT2, F btfsc STATUS, C goto mda2 ; key off rrf AM_PRV1, F ; compare previous condition rrf AM_PRV2, F btfss STATUS, C goto mda3 movlw 0x90 ; NOTE ON call MIDIout movfw VAR2 ; key call MIDIout movlw 0x7f ; velocity call MIDIout goto mda3 mda2 rrf AM_PRV1, F ; compare previous condition rrf AM_PRV2, F btfsc STATUS, C goto mda3 movlw 0x80 ; NOTE OFF call MIDIout movfw VAR2 call MIDIout movlw 0x7f ; velocity call MIDIout mda3 incf VAR2, F ; key++ decfsz VAR5, F goto mda1 mda4 return ; get data from PSX controler ; W: buffer top ; variable: FSR, SDATA, RDATA ; return: PSX_DAT[8] GetPSXC movwf FSR movlw 0x01 ; data request command[1] movwf SDATA call PSrecv ; send data call PSwAck ; wait ACK andlw 0xff btfss STATUS, Z ; ACK returned? retlw 0xff ; return with error movlw 0x42 ; request command[2] movwf SDATA call PSrecv ; send the request call PSwAck ; wait ACK movfw RDATA movwf INDF ; get controler ID incf FSR, F rlf RDATA, W ; calculate data length andlw 0x06 movwf VAR2 clrf SDATA gpc1 call PSrecv ; send the request call PSwAck ; wait ACK movfw RDATA movwf INDF ; store buffer incf FSR, F ; incriment pointer decfsz VAR2, F goto gpc1 call PSrecv ; send the request ; *not require wait for ACK movfw RDATA movwf INDF ; store buffer last data retlw 0 ; ; data transfer for PSX ; send SDATA, return: RDATA ; variable: VAR1 PSrecv movlw 8 movwf VAR1 psr1 rrf SDATA, F btfsc STATUS, C goto psr2 bcf PORTB, P_CLK ; clock goes low bcf PORTB, P_CMD goto psr3 psr2 bcf PORTB, P_CLK ; clock goes low bsf PORTB, P_CMD psr3 rrf PORTB, W ; shift P_DAT => STATUS:C rrf RDATA, F bsf PORTB, P_CLK ; clock goes high nop decfsz VAR1, F goto psr1 return ; ; wait ACK with timeout 100us ; variable: VAR1 PSwAck movlw 200 ; wait ACK with timeout 100us movwf VAR1 psw1 btfss PORTB, P_ACK retlw 0 ; ACK received, return 0 decfsz VAR1, F goto psw1 retlw 0xff ; ACK no reaction, return -1 ; ; send MIDI port ; output W with 31.25Kbps, S8N1 MIDIout movwf SDATA clrf LEDTMR ; light up LED bcf PORTA, P_LED movlw 8 movwf VAR1 bcf PORTA, P_MIN ; start bit movlw 17 call wait_W nop nop out1 rrf SDATA, F btfsc STATUS, C goto out2 nop bcf PORTA, P_MIN goto out3 out2 bsf PORTA, P_MIN nop nop out3 movlw 16 call wait_W decfsz VAR1, F goto out1 nop nop nop nop nop bsf PORTA, P_MIN ; stop bit movlw 19 call wait_W return ; ; wait loop (W) ; cyc = 3W + 4 wait_W movwf V_WAIT wait1 decfsz V_WAIT, F goto wait1 return ; ; wait @128us ; w128 movwf V_WAIT comf V_WAIT, F incf V_WAIT, W ; TMR0 = - W movwf TMR0 w128a movfw TMR0 ; TMR0 @128us btfss STATUS, Z goto w128a return ; read EEPROM ; W: EEPROM address, return W: EEPROM data eeread movwf EEADR bsf STATUS, RP0 bsf EECON1, RD bcf STATUS, RP0 movfw EEDATA return ; write EEPROM ; EEDATA: data for write, EEADR: address for write eewrite bsf STATUS, RP0 bsf EECON1, WREN ; write enable movlw 0x55 movwf EECON2 movlw 0xaa movwf EECON2 bsf EECON1, WR wr1 btfsc EECON1, WR goto wr1 bcf EECON1, WREN ; write disable bcf STATUS, RP0 return end