;AY-Voice ----- Thomas Henry ;Last Revision: 7/5/2012 PWH/elmegil ;****************** Constant Definitions ****************** symbol control = pinsA ;control lines to the AY chip symbol BC1 = A.0 ;A.0 is BC1 on the AY chip symbol BDIR = A.1 ;A.1 is BDIR on the AY chip symbol bus = pinsB ;data/address bus to AY chip symbol buttons = pinsC ;C.2-C.5 are arrow buttons ;These are time constants based on a 64 MHz clock. The numbers ;may seem inconsistent since they refer to the pause, pauseus ;or pulsout commands which all use different base units. symbol AYpulse = 4 ;2.5 uSec for pulsout symbol LCDpulse = 8 ;10 uSec for pauseus symbol uSec50 = 40 ;50 uSec for pauseus symbol mSec = 8 ;1 mSec for pause symbol mSec2 = 16 ;2 mSec for pause symbol mSec10 = 80 ;10 mSec for pause symbol mSec50 = 400 ;50 mSec for pause symbol mSec200 = 1600 ;200 mSec for pause symbol mSec500 = 4000 ;500 mSec for pause symbol secOne = 8000 ;one second symbol sec3 = 24000 ;3 seconds ;AY-8910 control codes and register addresses symbol inactive = %00000000 ;data/address bus inactive symbol readReg = %00000001 ;read addressed register symbol latchAddr = %00000011 ;latch the address on the DA bus symbol Reg0 = 0 ;tone A, coarse (4 bits only) symbol Reg1 = 1 ;tone A, fine symbol Reg2 = 2 ;tone B, coarse (4 bits only) symbol Reg3 = 3 ;tone B, fine symbol Reg4 = 4 ;tone C, coarse (4 bits only) symbol Reg5 = 5 ;tone C, fine symbol Reg6 = 6 ;noise period symbol Reg7 = 7 ;enable register symbol Reg8 = 8 ;amplitude A symbol Reg9 = 9 ;amplitude B symbol Reg10 = 10 ;amplitude C symbol Reg11 = 11 ;envelope period LSB symbol Reg12 = 12 ;envelope period MSB symbol Reg13 = 13 ;envelope type symbol Reg14 = 14 ;Port IOA symbol Reg15 = 15 ;Port IOB symbol IOABOut = %11000000 ;IOA and IOB are outputs ;Here are various LCD commands which can be used. symbol clrHome = 1 ;clear the display, home the cursor symbol home = 2 ;home the cursor only symbol RtoL = 4 ;print characters right to left symbol insR = 5 ;insert characters to right symbol LtoR = 6 ;print characters left to right symbol insL = 7 ;insert characters to left symbol lcdOff = 8 ;LCD screen off symbol lcdOn = 12 ;LCD screen on, no cursor symbol curOff = 12 ;an alias for the above symbol block = 13 ;LCD screen on, block cursor symbol under = 14 ;LCD screen on, underline cursor symbol undblk = 15 ;blinking and underline cursor symbol panR = 24 ;pan viewing window right symbol panL = 28 ;pan viewing window left symbol mode1 = 32 ;one-line mode (4-bit data) symbol mode2 = 40 ;two-line mode (4-bit data) symbol bus4 = 32 ;4-bit data bus mode symbol bus8 = 48 ;8-bit data bus mode symbol uchar = 64 ;start of user-defined character RAM symbol line1 = 128 ;go to start of line 1 symbol line2 = 192 ;go to start of line 2 symbol unchecked = 0 ;ASCII code for the unchecked box symbol checked = 1 ;ASCII code for the checked box ;arrow button values symbol up = 1 symbol down = 2 symbol left = 3 symbol right = 4 ;the supported MIDI modes symbol mono = 1 symbol omni = 2 symbol noMIDI = 3 ;checkbox flags symbol BoxA = 4 symbol BoxB = 2 symbol BoxC = 1 ;envelope types symbol AR = 1 ;attack/release symbol perc = 2 ;percussive (0 attack) symbol eRev = 3 ;reversed (0 release) symbol trem = 4 ;tremolo (attack = release, repeats) ;tone frequency limits symbol fMin = 33 ;min frequency symbol fMax = 15625 ;nax frequency (Hz) symbol eMin = 0 ;minimum envelope time symbol eMax = 7999 ;maximum envelope time (mS) symbol levelMin = 0 ;minimum voice level symbol levelMax = 15 ;maximum voice level symbol nMin = 0 ;minimum noise frequency symbol nMax = 31 ;maximum noise frequency ; midi values, not all used symbol MIDI_NOTEOFF = %10000000 symbol MIDI_NOTEON = %10010000 symbol MIDI_PAT = %10100000 symbol MIDI_CTRLCHG = %10110000 symbol MIDI_PGM = %11000000 symbol MIDI_CAT = %11010000 symbol MIDI_MODWHL = %11100000 ;****************** Variable Definitions ****************** symbol bitFlags = w0 ;bit flags---unused at present ;AY Voice Parameters - do not use for anything else symbol aTime = w1 ;attack time in mSec symbol rTime = w2 ;release time in mSec symbol toneA = w3 ;tone frequency A symbol toneB = w4 ;tone frequency B symbol toneC = w5 ;tone frequency C symbol tEnable = b12 ;tone enable flags symbol nEnable = b13 ;noise enable flags symbol eEnable = b14 ;envelope enable flags symbol aLevel = b15 ;channel A voice level (0-15) symbol bLevel = b16 ;channel B voice level (0-15) symbol cLevel = b17 ;channel C voice level (0-15) symbol envType = b18 ;AR, tremolo, percussive, reverse symbol mode = b19 ;mono, omni, off symbol nFreq = b20 ;noise frequency symbol base = b21 ;base MIDI channel (1 to 13) ;MIDI variables symbol head = w11 ;head of MIDI queue symbol midi = b24 ;message byte to be processed symbol place = b24 ;(alias) decimal place in number symbol dummy = b25 ; unused for now, possible "current channel" ? symbol dataflag = bit0 ; bit flag, used to indicate we have the first data byte of a 2 byte command ;setup mode variables - may be reused with care symbol eTime = w13 ;parameter for time print routine symbol toneP = w13 ;(alias) parameter for tone frequency symbol periodW = w13 ;(alias) period of wave symbol tempW = w14 ;temporary word variable symbol powerW = w14 ;(alias) power of ten symbol numW = w15 ;number-to-ASCII input parameter symbol placeW = w16 ;place value for multiplier symbol pMinW = w17 ;parameter minimum value symbol pMaxW = w18 ;parameter maximum value symbol char = b38 ;ASCII character to be displayed symbol cmd = b38 ;(alias) LCD command byte symbol temp = b39 ;local variable only for subs symbol colSave = b40 ;save old column number symbol chrSave = b40 ;(alias) save current character symbol arrow = b41 ;current arrow button pressed symbol dig4 = b42 ;(local) ten-thousands digit in number symbol dig3 = b43 ;(local) thousands digit in number symbol dig2 = b44 ;(local) hundreds digit in number symbol dig1 = b45 ;(local) tens digit in number symbol dig0 = b46 ;(local) units digit in number symbol row = b47 ;current LCD row symbol col = b48 ;current LCD column symbol i = b49 ;loop variable symbol vLevel = b50 ;level parameter for subroutine symbol digits = b51 ;number of digits in string symbol ABC = b42 ;(alias) 4=box A, 2=box B, 1=box C symbol check = b43 ;(alias) checkbox flag symbol msg = b44 ;(alias) offset into message table symbol value = b45 ;(alias) AY register number (0-15) symbol register = b46 ;(alias) value to store in AY register ; more midi values symbol midi_status = b52 symbol midi_data1 = b53 symbol midi_prev = b54 symbol midi_current = b55 symbol midi_lsb = b28 ; (alias) half of tempW, LSB of note symbol midi_msb = b29 ; (alias) other half of tempW, MSB of note ;*********************** Messages ************************* eeprom 0, ("AY-VOICE",0) eeprom 9, ("SETUP MENU",0) eeprom 20, ("MIDI MODE",0) eeprom 30, ("MONO",0) eeprom 35, ("OMNI",0) eeprom 40, ("OFF",0) eeprom 44, ("MIDI CHANNEL",0) eeprom 57, ("TONE ENABLE",0) eeprom 69, ("NOISE ENABLE",0) eeprom 82, ("ENVELOPE ENABLE",0) eeprom 98, ("ENVELOPE TYPE",0) eeprom 112,("ATTACK/RELEASE",0) eeprom 127,("PERCUSSIVE",0) eeprom 138,("REVERSE",0) eeprom 146,("TREMOLO",0) eeprom 154,("ENVELOPE TIME",0) eeprom 168,("VOICE LEVELS",0) eeprom 181,("TONE FREQUENCY",0) eeprom 196,("NOISE FREQUENCY",0) eeprom 212,("HIT DOWN TO PLAY",0) ;*********************** MIDI Table ************************* ; note# = table index of LSB symbol msbOffset = 120 ;note# + offset = table index of MSB symbol noteC1 = 24 ;lowest possible note symbol noteB9 = 131 ;highest possible note ;*********************** Note LSB Values ************************* table 24,(238) ;C1 table 25,(24) ;C#1 table 26,(77) ;D1 table 27,(142) ;D#1 table 28,(218) ;E1 table 29,(47) ;F1 table 30,(143) ;F#1 table 31,(247) ;G1 table 32,(104) ;G#1 table 33,(225) ;A1 table 34,(97) ;A#1 table 35,(233) ;B1 table 36,(119) ;C2 table 37,(12) ;C#2 table 38,(167) ;D2 table 39,(71) ;D#2 table 40,(237) ;E2 table 41,(152) ;F2 table 42,(71) ;F#2 table 43,(252) ;G2 table 44,(180) ;G#2 table 45,(112) ;A2 table 46,(49) ;A#2 table 47,(244) ;B2 table 48,(188) ;C3 table 49,(134) ;C#3 table 50,(83) ;D3 table 51,(36) ;D#3 table 52,(246) ;E3 table 53,(204) ;F3 table 54,(164) ;F#3 table 55,(126) ;G3 table 56,(90) ;G#3 table 57,(56) ;A3 table 58,(24) ;A#3 table 59,(250) ;B3 table 60,(222) ;C4 table 61,(195) ;C#4 table 62,(170) ;D4 table 63,(146) ;D#4 table 64,(123) ;E4 table 65,(102) ;F4 table 66,(82) ;F#4 table 67,(63) ;G4 table 68,(45) ;G#4 table 69,(28) ;A4 table 70,(12) ;A#4 table 71,(253) ;B4 table 72,(239) ;C5 table 73,(225) ;C#5 table 74,(213) ;D5 table 75,(201) ;D#5 table 76,(190) ;E5 table 77,(179) ;F5 table 78,(169) ;F#5 table 79,(159) ;G5 table 80,(150) ;G#5 table 81,(142) ;A5 table 82,(134) ;A#5 table 83,(127) ;B5 table 84,(119) ;C6 table 85,(113) ;C#6 table 86,(106) ;D6 table 87,(100) ;D#6 table 88,(95) ;E6 table 89,(89) ;F6 table 90,(84) ;F#6 table 91,(80) ;G6 table 92,(75) ;G#6 table 93,(71) ;A6 table 94,(67) ;A#6 table 95,(63) ;B6 table 96,(60) ;C7 table 97,(56) ;C#7 table 98,(53) ;D7 table 99,(50) ;D#7 table 100,(47) ;E7 table 101,(45) ;F7 table 102,(42) ;F#7 table 103,(40) ;G7 table 104,(38) ;G#7 table 105,(36) ;A7 table 106,(34) ;A#7 table 107,(32) ;B7 table 108,(30) ;C8 table 109,(28) ;C#8 table 110,(27) ;D8 table 111,(25) ;D#8 table 112,(24) ;E8 table 113,(22) ;F8 table 114,(21) ;F#8 table 115,(20) ;G8 table 116,(19) ;G#8 table 117,(18) ;A8 table 118,(17) ;A#8 table 119,(16) ;B8 table 120,(15) ;C9 table 121,(14) ;C#9 table 122,(13) ;D9 table 123,(13) ;D#9 table 124,(12) ;E9 table 125,(11) ;F9 table 126,(11) ;F#9 table 127,(10) ;G9 table 128,(9) ;G#9 table 129,(9) ;A9 table 130,(8) ;A#9 table 131,(8) ;B9 ;*********************** Note MSB Values ************************* table 144, (14) ;C1 table 145, (14) ;C#1 table 146, (13) ;D1 table 147, (12) ;D#1 table 148, (11) ;E1 table 149, (11) ;F1 table 150, (10) ;F#1 table 151, (9) ;G1 table 152, (9) ;G#1 table 153, (8) ;A1 table 154, (8) ;A#1 table 155, (7) ;B1 table 156, (7) ;C2 table 157, (7) ;C#2 table 158, (6) ;D2 table 159, (6) ;D#2 table 160, (5) ;E2 table 161, (5) ;F2 table 162, (5) ;F#2 table 163, (4) ;G2 table 164, (4) ;G#2 table 165, (4) ;A2 table 166, (4) ;A#2 table 167, (3) ;B2 table 168, (3) ;C3 table 169, (3) ;C#3 table 170, (3) ;D3 table 171, (3) ;D#3 table 172, (2) ;E3 table 173, (2) ;F3 table 174, (2) ;F#3 table 175, (2) ;G3 table 176, (2) ;G#3 table 177, (2) ;A3 table 178, (2) ;A#3 table 179, (1) ;B3 table 180, (1) ;C4 table 181, (1) ;C#4 table 182, (1) ;D4 table 183, (1) ;D#4 table 184, (1) ;E4 table 185, (1) ;F4 table 186, (1) ;F#4 table 187, (1) ;G4 table 188, (1) ;G#4 table 189, (1) ;A4 table 190, (1) ;A#4 table 191, (0) ;B4 table 192, (0) ;C5 table 193, (0) ;C#5 table 194, (0) ;D5 table 195, (0) ;D#5 table 196, (0) ;E5 table 197, (0) ;F5 table 198, (0) ;F#5 table 199, (0) ;G5 table 200, (0) ;G#5 table 201, (0) ;A5 table 202, (0) ;A#5 table 203, (0) ;B5 table 204, (0) ;C6 table 205, (0) ;C#6 table 206, (0) ;D6 table 207, (0) ;D#6 table 208, (0) ;E6 table 209, (0) ;F6 table 210, (0) ;F#6 table 211, (0) ;G6 table 212, (0) ;G#6 table 213, (0) ;A6 table 214, (0) ;A#6 table 215, (0) ;B6 table 216, (0) ;C7 table 217, (0) ;C#7 table 218, (0) ;D7 table 219, (0) ;D#7 table 220, (0) ;E7 table 221, (0) ;F7 table 222, (0) ;F#7 table 223, (0) ;G7 table 224, (0) ;G#7 table 225, (0) ;A7 table 226, (0) ;A#7 table 227, (0) ;B7 table 228, (0) ;C8 table 229, (0) ;C#8 table 230, (0) ;D8 table 231, (0) ;D#8 table 232, (0) ;E8 table 233, (0) ;F8 table 234, (0) ;F#8 table 235, (0) ;G8 table 236, (0) ;G#8 table 237, (0) ;A8 table 238, (0) ;A#8 table 239, (0) ;B8 table 240, (0) ;C9 table 241, (0) ;C#9 table 242, (0) ;D9 table 243, (0) ;D#9 table 244, (0) ;E9 table 245, (0) ;F9 table 246, (0) ;F#9 table 247, (0) ;G9 table 248, (0) ;G#9 table 249, (0) ;A9 table 250, (0) ;A#9 table 251, (0) ;B9 ;********************** Main Program ********************** pgm: setfreq em64 ;64 MHz external resonator dirsA = %00001111 ;A.0-A.3 are outputs dirsB = %11111111 ;B.0-B.7 are normally outputs dirsC = %00000011 ;C.2-C.7 are inputs pwmout C.1,7,16 ;start AY clock at 2 MHz head = 0 ;initialize queue mode = mono ;mono mode by default base = 0 ;base channel for MIDI envType = AR ;attack/release at the outset toneA = fMin ;tone frequencies at minimum toneB = fMin toneC = fMin gosub init ;initialize AY and LCD gosub makeChrs ;create user-defined characters ; for MIDI testing, in place of menus, I'm setting additional defaults directly ;----- Tone Enable Menu ----- tEnable = %00000000 ; disable all register = reg7 ;read enable register gosub fetch ;get current stored value check = not tEnable ;complement tone enable flags check = check rev 3 ;reverse order of A, B and C check = check & %00000111 ;mask off any junk value = value & %11111000 ;strip A, B and C off value = value | check ;factor in the flags gosub store ;and store new result ;----- Noise Enable Menu ----- nEnable = %00000000 ; disable all register = reg7 ;enable register gosub fetch ;get current stored value check = not nEnable ;complement the bits check = check rev 3 ;reverse order of A, B and C check = check << 3 ;shift left three bits check = check & %00111000 ;mask off junk value = value & %11000111 ;strip out A, B and C value = value | check ;factor in the flags gosub store ;and store new result ;----- Envelope Enable Menu ----- eEnable = %00000000 ;update them register = reg8 ;get amplitude A gosub fetch ABC = check & %00000100 ;get A flag ABC = ABC << 2 ;shift to enable bit position value = value & %11101111 ;remove old flag value = value | ABC ;pop in the new one gosub store register = reg9 ;get amplitude B gosub fetch ABC = check & %00000010 ;get B flag ABC = ABC << 3 ;shift to enable bit position value = value & %11101111 ;remove old flag value = value | ABC gosub store register = reg10 ;get amplitude C gosub fetch ABC = check & %00000001 ;get C flag ABC = ABC << 4 ;shift to enable bit position value = value & %11101111 ;remove old flag value = value | ABC gosub store ;----- Envelope Type Menu ----- aLevel = levelMax register = reg8 ;get amplitude A gosub fetch value = value & %11110000 ;remove old level value = value | aLevel ;factor in new level gosub store bLevel = levelMax register = reg9 gosub fetch value = value & %11110000 ;remove old level value = value | bLevel gosub store cLevel = levelMax register = reg10 gosub fetch value = value & %11110000 ;remove old level value = value | cLevel gosub store ;----- Tone Frequency Menus ----- ; defaults are already fMin, but need to store them periodW = toneA / 2 ;compute the period periodW = 62500 / periodW value = periodW % 256 ;store fine tune register = reg0 gosub store value = periodW / 256 ;store coarse tune register = reg1 gosub store periodW = toneB / 2 ;compute the period periodW = 62500 / periodW value = periodW % 256 ;store fine tune register = reg2 gosub store value = periodW / 256 ;store coarse tune register = reg3 gosub store periodW = toneC / 2 ;compute the period periodW = 62500 / periodW value = periodW % 256 ;store fine tune register = reg4 gosub store value = periodW / 256 ;store coarse tune register = reg5 gosub store ; End of setting/storing defaults ; Start of MIDI hsersetup B31250_64,%00001001 ;turn on MIDI input doing here so we don't have a big backlog before we get the rest of setup done midi_status = 0 ; required for proper functioning of process_midi dataflag = 0 ; possibly unnecessary to set 0 here midi_prev = $FF ; previous note, $FF is not valid, no previous note saved midi_current = $FF ; current note, top of stack, $FF is all notes off main: if head != hserptr then ;anything in queue? ptr = head ;yes midi = @ptrinc ;get the byte head = ptr ;move head one byte along if head = 1024 then ;but don't go beyond end head = 0 ;wrap around endif gosub process_midi endif goto main ;repeat forever (obviously we want a way to escape to the menu should we need to) end ;end of main program ;************************************************************* ;* * ;* Process MIDI byte * ;* Input parameters: midi * ;* Static variables: midi_status (must be=0 at startup), midi_data1 * ;* Local variables: temp * ;* * ;************************************************************* process_midi: if midi >= %10000000 then ;status byte ;SerTxd("status byte", CR, LF) if midi >= %11110000 then ;system common ;SerTxd(" system common", CR, LF) if midi >= %11111000 then goto sys_rt ;ignore all other system common msgs, including sysex midi_status = 0 ;reset running status SerTxd(" reset running status", CR, LF) else ;channel message ;check channel, if not ours set midi_status = 0 midi_status = 0 temp = midi & %00001111 ; mask off the channel number for i = 0 to 3 check = base + i ; reusing check variable if temp = check then midi_status = midi ; we have a winner! endif next i midi_data1 = 0 dataflag = 0 endif else ;data byte if midi_status > 0 then ;status sent or valid running status temp = midi_status & %11110000 ;the following messages require 1 data byte, which we just got if temp = MIDI_PGM then goto prog_change if temp = MIDI_CAT then goto chan_atouch ;the following messages require 2 data bytes if dataflag = 0 then ; first byte, but we need another one midi_data1 = midi dataflag = 1 ; got our first byte flag else dataflag = 0 ; got our second byte, flip the flag back if temp = MIDI_NOTEON then goto note_on if temp = MIDI_NOTEOFF then goto note_off if temp = MIDI_CTRLCHG then goto control_change if temp = MIDI_MODWHL then goto mod_wheel if temp = MIDI_PAT then goto poly_atouch endif ;else ignore endif endif return ; for the moment ignoring the nuances of voiceA/voiceB/voiceC/Gates ; all changes go only to voiceA note_on: ;note=midi_data1, vel=midi if midi = 0 then goto note_off SerTxd("Note On, Note #", #midi_data1,9, " vel ", #midi,9,CR,LF) SerTxd(" On Entry, Prev was ", #midi_prev, " and Current was ", #midi_current,CR,LF) midi_prev = midi_current midi_current = midi_data1 note_on2: ; entry from note_off for trill ; handle out of range notes if midi_data1 < noteC1 or midi_data1 > noteB9 then ; ERROR out of range! else ;process note on here temp = midi_data1 + msbOffset readtable midi_data1, midi_lsb readtable temp, midi_msb ;SerTxd(" MSB ", #midi_msb, " LSB ", #midi_lsb,CR,LF) register = 0 value = midi_lsb gosub store register = 1 value = midi_msb gosub store tEnable = %00000100 ; enable A register = reg7 ;read enable register gosub fetch ;get current stored value check = not tEnable ;complement tone enable flags check = check rev 3 ;reverse order of A, B and C check = check & %00000111 ;mask off any junk value = value & %11111000 ;strip A, B and C off value = value | check ;factor in the flags gosub store ; should have actual generate trigger here midi_data1 = 0 SerTxd(" On Exit, Prev is ", #midi_prev, " and Current is ", #midi_current,CR,LF) endif return note_off: ;note=midi_data1, vel=midi SerTxd("Note Off, Note #", #midi_data1,9, " vel ", #midi,9,CR,LF) SerTxd(" On Entry, Prev was ", #midi_prev, " and Current was ", #midi_current,CR,LF) if midi_data1 < noteC1 or midi_data1 > noteB9 then ; ERROR out of range! else if midi_data1 = midi_current then midi_current = midi_prev midi_prev = $FF if midi_current <> $FF then midi_data1 = midi_current SerTxd(" Going to trill with note ", #midi_data1,CR,LF) goto note_on2 else ;process note off here tEnable = %00000000 ; disable all register = reg7 ;read enable register gosub fetch ;get current stored value check = not tEnable ;complement tone enable flags check = check rev 3 ;reverse order of A, B and C check = check & %00000111 ;mask off any junk value = value & %11111000 ;strip A, B and C off value = value | check ;factor in the flags gosub store endif else ; note off is either prev or before note if midi_data1 = midi_prev then ; previous note turning off, don't turn off current midi_prev = $FF ; but take prev out of the stack ;else the note turning off is prior to previous note, ignore it endif endif endif midi_data1 = 0 SerTxd(" On Exit, Prev is ", #midi_prev, " and Current is ", #midi_current,CR,LF) return ; from here out, aside from clearing midi_data bytes, nothing tested ;unhandled MIDI msgs (fill-in as appropriate) poly_atouch: ;note=midi_data1, value=midi control_change: ;control=midi_data1, value=midi mod_wheel: ;lsb=midi_data1, msb=midi midi_data1 = 0 return prog_change: ;prog=midi chan_atouch: ;value=midi sys_rt: ;msg=midi midi_data1 = 0 ; possibly redundant/unnecessary return ;************************************************************* ;* * ;* Initialize AY and LCD on power-up * ;* Input parameters: none * ;* Output parameters: none * ;* Local variables: i * ;* * ;************************************************************* init: control = inactive ;clear BC1, BDIR ;set up the AY Port IOB for output control = latchAddr ;latch address to register 7 bus = Reg7 control = inactive bus = IOABOut ;make IOA and IOB all outputs pulsout BDIR,AYpulse init2: pause mSec200 ;let LCD settle for 200 mS at power-up control = latchAddr ;latch address to register 15 bus = Reg15 control = inactive bus = %00100011 ;set to 8-bit mode pulsout BDIR,AYPulse bus = %00000011 pulsout BDIR,AYPulse pause mSec10 ;pause a bit for i = 1 to 2 ;send twice more for security bus = %00100011 pulsout BDIR,AYPulse bus = %00000011 pulsout BDIR,AYPulse next i for i= 1 to 2 bus = %00100010 ;set to 4-bit mode pulsout BDIR,AYPulse bus = %00000010 pulsout BDIR,AYPulse next i bus = %00101000 ;set to 2-line mode pulsout BDIR,AYPulse bus = %00001000 pulsout BDIR,AYPulse pause mSec10 cmd = lcdOn ;display on, no cursor gosub sendCmd pauseus uSec50 return ;************************************************************* ;* * ;* Send a Character to the LCD, RS must be high * ;* Input parameters: char * ;* Output parameters: none * ;* Local variables: temp * ;* * ;************************************************************* sendChr: control = latchAddr ;latch address mode bus = Reg15 ;access Port IOB control = inactive ;address now latched temp = char >> 4 ;shift high nibble right bus = temp | %00110000 ;Enable high (and RS) pulsout BDIR,AYpulse ;latch data pause LCDpulse ;keep high for 200 uS bus = temp | %00010000 ;Enable low, RS still high pulsout BDIR,AYpulse ;latch data temp = char & %00001111 ;grab low nibble bus = temp | %00110000 ;Enable high,RS high pulsout BDIR,AYPulse ;latch data pause LCDpulse bus = temp | %00010000 ;Enable low pulsout BDIR,AYPulse ;latch data return ;************************************************************* ;* * ;* Send a command to the LCD, RS must be low * ;* Input parameters: cmd * ;* Output parameters: none * ;* Local variables: temp * ;* * ;************************************************************* sendCmd: control = latchAddr ;latch address mode bus = Reg15 ;access Port IOB control = inactive ;address now latched temp = cmd >> 4 ;shift high nibble right bus = temp | %00100000 ;Enable high,RS low pulsout BDIR,AYpulse ;latch data pause LCDpulse bus = temp ;Enable low, RS still low pulsout BDIR,AYpulse ;latch data temp = char & %00001111 ;grab low nibble bus = temp | %00100000 ;Enable high,RS low pulsout BDIR,AYPulse ;latch data pause LCDpulse bus = temp ;Enable low, RS still low pulsout BDIR,AYPulse ;latch data pause mSec ;commands take longer return ;************************************************************* ;* * ;* Clear LCD and home cursor * ;* Input parameters: none * ;* Output parameters: row=0, col=0 * ;* Local variables: none * ;* * ;************************************************************* clr: cmd = clrHome ;clear display and home cursor gosub sendCmd pause mSec2 ;clear LCD takes longer to execute row = 0 col = 0 return ;************************************************************* ;* * ;* Clear line two only * ;* Input parameters: none * ;* Output parameters: row=1, col=0 * ;* Local variables: i * ;* * ;************************************************************* clrLine2: cmd = line2 ;put cursor on second row gosub sendCmd char = " " ;blank it out for i = 1 to 16 gosub sendChr next i row = 1 ;put cursor at start of row col = 0 gosub locate return ;************************************************************* ;* * ;* Create the two user-defined characters * ;* Input parameters: none * ;* Output parameters: none * ;* Local variables: i * ;* * ;************************************************************* makeChrs: cmd = uchar ;point to start of user character RAM gosub sendCmd char = %00000 ;unchecked box gosub sendChr char = %11111 gosub sendChr char = %10001 for i = 1 to 3 gosub sendChr next i char = %11111 gosub sendChr char = %00000 gosub sendChr gosub sendChr gosub sendChr ;checked box char = %11111 for i = 1 to 5 gosub sendChr next i char = %00000 gosub sendChr gosub sendChr return ;************************************************************* ;* * ;* Place the cursor in desired location * ;* Input parameters: row, col * ;* Output parameters: none * ;* Local variables: temp * ;* * ;************************************************************* locate: if row = 0 then ;choose the row temp = line1 else temp = line2 endif cmd = temp+col ;factor in the column gosub sendCmd return ;************************************************************* ;* * ;* Print message to the LCD * ;* Input parameters: msg, row, col * ;* Output parameters: none * ;* Local variables: none * ;* * ;************************************************************* printMsg: gosub locate read msg,char ;prime the loop do while char != 0 ;null terminates the string gosub sendChr inc msg read msg,char loop return ;************************************************************* ;* * ;* Print a two-digit number * ;* Input parameters: numW * ;* Output parameters: none * ;* Local variables: dig4, dig3, dig2, dig1, dig0 * ;* * ;************************************************************* printNum2: bintoASCII numW,dig4, dig3, dig2, dig1, dig0 goto printNum5c ;************************************************************* ;* * ;* Print a four-digit number * ;* Input parameters: numW * ;* Output parameters: none * ;* Local variables: dig4, dig3, dig2, dig1, dig0 * ;* * ;************************************************************* printNum4: bintoASCII numW,dig4, dig3, dig2, dig1, dig0 goto printNum5b ;************************************************************* ;* * ;* Print a five-digit number * ;* Input parameters: numW * ;* Output parameters: none * ;* Local variables: dig4, dig3, dig2, dig1, dig0 * ;* * ;************************************************************* printNum5: bintoASCII numW,dig4, dig3, dig2, dig1, dig0 char = dig4 gosub sendChr printNum5b: char = dig3 gosub sendChr char = dig2 gosub sendChr printNum5c: char = dig1 gosub sendChr char = dig0 gosub sendChr return ;************************************************************* ;* * ;* Print single envelope time * ;* Input parameters: eTime, char (A or R) * ;* Output parameters: none * ;* Local variables: numW * ;* * ;************************************************************* printTime: gosub sendChr ;print the letter label char = ":" gosub sendChr numW = eTime gosub printNum4 ;0000 to 7999 milliseconds return ;************************************************************* ;* * ;* Print both envelope times * ;* Input parameters: none * ;* Output parameters: none * ;* Local variables: eTime * ;* * ;************************************************************* printTimes: gosub clrLine2 ;print attack time char = "A" eTime = aTime gosub printTime col = 10 ;print release time gosub locate char = "R" eTime = rTime gosub printTime return ;************************************************************* ;* * ;* Get an envelope time * ;* Input parameters: eTime, col * ;* Output parameters: eTime, arrow (L/R only) * ;* Local variables: digits, numW, pMaxW, pMinW * ;* * ;************************************************************* getTime: gosub locate ;sit on leftmost digit getTime1: numW = eTime ;show current time gosub printNum4 ;print it digits = 4 ;four digit number input pMaxW = eMax ;max envelope time in mS pMinW = eMin ;min envelope time in mS gosub getNum ;fetch new number eTime = numW ;update new result return ;************************************************************* ;* * ;* Display Voice Levels * ;* Input parameters: none * ;* Output parameters: none * ;* Local variables: none * ;* * ;************************************************************* printLevels: gosub clrLine2 char = "A" gosub sendChr ;print the channel letter char = ":" gosub sendChr ;then a colon numW = aLevel gosub printNum2 ;and finally the level char = " " ;two spaces gosub sendChr gosub sendChr char = "B" ;repeat for B gosub sendChr char = ":" gosub sendChr numW = bLevel gosub printNum2 char = " " gosub sendChr gosub sendChr char = "C" ;and now C gosub sendChr char = ":" gosub sendChr numW = cLevel gosub printNum2 return ;************************************************************* ;* * ;* Get a Voice Level * ;* Input parameters: vLevel, row, col * ;* Output parameters: vLevel, arrow (L/R) * ;* Local variables: numW, digits, pMinW, pMaxW * ;* * ;************************************************************* getLevel: gosub locate ;locate on the tens digit cmd = block ;blink cursor gosub sendCmd numW = vLevel ;pass in current value digits = 2 ;a two-digit number only pMinW = levelMin pMaxW = levelMax gosub getNum ;get new value vLevel = numW ;update the level return ;************************************************************* ;* * ;* Get a Voice Frequency * ;* Input parameters: toneP, row, col * ;* Output parameters: toneP, arrow (L/R) * ;* Local variables: numW, digits, pMinW, pMaxW * ;* * ;************************************************************* getFreq: gosub clrLine2 ;print current value numW = toneP gosub printNum5 char = " " gosub sendChr char = "H" gosub sendChr char = "z" gosub sendChr col = 0 digits = 5 ;a five-digit number pMinW = fMin pMaxW = fMax gosub getNum ;get new value toneP = numW ;update the frequency return ;*************************************************************** ;* * ;* Get a decimal digit * ;* Input parameters: numW, col, digits, place, pMaxW, pMinW * ;* Output parameters: numW, arrow (L/R only) * ;* Local variables: powerW, i * ;* * ;*************************************************************** ; ;digits = how many digits in the number ;place = which digit to work on (1=units, 2=hundreds, etc.) ;col = position of leftmost digit in the number getDigit: colSave = col ;save start of number pointer getDigit2: col = col + digits - place gosub locate ;place cursor on digit powerW = 1 ;create power of ten i = place do while i > 1 powerW = 10 * powerW dec i loop gosub getKey if arrow = left then ;exit and go to previous goto getDigit3 endif if arrow = right then ;exit and go to next goto getDigit3 endif if arrow = up then ;else change number numW = numW + powerW else if numW >= powerW then ;safe to decrement numW = numW - powerW min pMinW endif endif if numW > pMaxW then ;got too big numW = numW - powerW ;so undo endif col = colSave ;restore column pointer gosub locate ;update the display if digits = 5 then gosub printNum5 elseif digits = 4 then gosub printNum4 else gosub printNum2 endif goto getDigit2 getDigit3: col = colSave return ;************************************************************* ;* * ;* Get a two, four or five digit number * ;* Input parameters: numW, col, digits, pMaxW, pMinW * ;* Output parameters: numW, arrow (L/R only) * ;* Local variables: place * ;* * ;************************************************************* getNum: cmd = block ;blink the cursor gosub sendCmd if digits = 4 then ;handle four digits goto getNum2 elseif digits = 2 then ;or two digits goto getNum4 endif ;otherwise five digits getNum1: place = 5 ;handle 10000s digit gosub getDigit if arrow = left then ;exit and go to previous menu goto getNum6 endif if arrow = right then ;go to next digit goto getNum2 endif goto getNum1 getNum2: place = 4 ;handle 1000s digit gosub getDigit if arrow = left then if digits = 5 then goto getNum1 ;5 digits, previous digit else goto getNum6 ;4 digits, previous menu endif endif if arrow = right then ;go to next digit goto getNum3 endif goto getNum2 getNum3: place = 3 ;handle 100s digit gosub getDigit if arrow = left then ;exit and go to previous digit goto getNum2 endif if arrow = right then ;go to next digit goto getNum4 endif goto getNum3 getNum4: place = 2 ;handle 10s digit gosub getDigit if arrow = left then if digits > 2 then goto getNum3 ;5 or 4 digits, previous digit else goto getNum6 ;2 digits, previous menu endif endif if arrow = right then ;go to next digit goto getNum5 endif goto getNum4 getNum5: place = 1 ;handle ones digit gosub getDigit if arrow = left then ;exit and go to previous digit goto getNum4 endif if arrow = right then ;go to next menu goto getNum6 endif goto getNum5 getNum6: cmd = curOff gosub sendCmd return ;************************************************************* ;* * ;* Do the checkboxes * ;* Input parameters: check * ;* Output parameters: check, arrow (L/R only) * ;* Local variables: ABC, temp * ;* * ;************************************************************* checkbox: ABC = BoxA checkbox2: row = 1 ;start with A col = 0 gosub locate temp = check & BoxA if temp = BoxA then char = checked else char = unchecked endif gosub sendChr ;print box char = "A" gosub sendChr col = 7 ;now do B gosub locate temp = check & BoxB if temp = BoxB then char = checked else char = unchecked endif gosub sendChr char = "B" gosub sendChr col = 14 ;then do C gosub locate temp = check & BoxC if temp = BoxC then char = checked else char = unchecked endif gosub sendChr char = "C" gosub sendChr select case ABC ;position the cursor case BoxA cmd = line2 case BoxB cmd = line2+7 case BoxC cmd = line2+14 end select gosub sendCmd ;and blink it cmd = block gosub sendCmd gosub getKey ;read the arrow keys select case arrow case up, down check = check xor ABC ;toggle the select bit case left ABC = ABC << 1 ;move to previous case right ABC = ABC >> 1 ;move to next end select if arrow = up then goto checkbox2 if arrow = down then goto checkbox2 if ABC > BoxA then goto checkbox3 ;change of menu if ABC < BoxC then goto checkbox3 ;ditto goto checkbox2 checkbox3: cmd = curOff ;turn off the cursor gosub sendCmd return ;************************************************************* ;* * ;* Wait for and fetch arrow keystroke * ;* Input parameters: none * ;* Output parameters: arrow * ;* Local variables: temp * ;* * ;************************************************************* getKey: temp = not buttons ;read switch port temp = temp >> 2 ;shift into lowest part of byte temp = temp & %00001111 ;mask off junk above select case temp ;covert to simple form case 1 arrow = up case 2 arrow = down case 4 arrow = left case 8 arrow = right else ;more than one or arrow = 0 ;no buttons pressed end select if arrow = 0 then ;loop until pressed goto getKey endif pause mSec50 ;pause for debounce do ;wait for open switches temp = not buttons ;read switch port temp = temp >> 2 ;shift into lowest part of byte temp = temp & %00011111 ;mask off junk above loop while temp != 0 return ;************************************************************* ;* * ;* Send Value to AY Register * ;* Input parameters: register, value * ;* Output parameters: none * ;* Local variables: none * ;* * ;************************************************************* store: control = inactive ;clear BC1, BDIR control = latchAddr ;latch address of register bus = register control = inactive bus = value ;get value and pulsout BDIR,AYPulse ;store it return ;************************************************************* ;* * ;* Fetch Value from AY Register * ;* Input parameters: register * ;* Output parameters: value * ;* Local variables: none * ;* * ;************************************************************* fetch: control = inactive ;clear BC1, BDIR control = latchAddr ;latch address of register bus = register control = inactive control = readReg ;read the register dirsB = %00000000 ;make bus input value = bus dirsB = %11111111 ;restore outputs control = inactive return ;************************************************************* ;* * ;* Print A, B or C Labels * ;* Input parameters: register, value * ;* Output parameters: none * ;* Local variables: temp * ;* * ;************************************************************* printA: chrSave = "A" goto printLetter printB: chrSave = "B" goto printLetter printC: chrSave = "C" printLetter: row = 0 col = 15 gosub locate char = chrSave gosub sendChr return