HELP! - Sound Generator using 6502 & VIA

Programming the 6502 microprocessor and its relatives in assembly and other languages.
Post Reply
thesylph005
Posts: 3
Joined: 02 Dec 2004

HELP! - Sound Generator using 6502 & VIA

Post by thesylph005 »

Im working on a project that uses a 6522 VIA to create a square wave.

I have been told by setting the shift register on the via to 10101010 i can generate a continous square wave on the CB2 pin. However i am having trouble and my program does not seem to work.

i have come accross limited resources documenting this feature and all i have seen is that i should set bits 2-4 as 100 of ACR

Code: Select all

LDA #$08
STA $A00B
is this right?

Apparently, by changing the values in timer 2, the rate at which the shifting is done should change:

Code: Select all

LDA #$C6
STA $A008
LDA #$01
STA A009
Im not entirely certain if this is true or not!
Also do i need to set port B as an output or is this done automatically?

hopefuly then by connecting a speaker through a capacitor to CB2 this code will make a note

Any help really is GREATLY appreciated.




Code below!

Code: Select all

SETVAL:	SET TEMPY=$00
	SET TEMPX=$00
	SET PAUSEX=$50		;PAUSEX/Y set to 50k cycles
	SET PAUSEY=$C3
	SET NOTENUM=$00		;note number - next note to play - 00=start
	ORG $F800

SETCB2:	LDA #$FF
	STA $A002	; set port B (CB2) as output

SRSET:	LDA #$99		; 10101010 goes into 
	STA $A00A	; shift register to be looped = pulse

TMRSET:	LDA#$08         	; set shift reg to timer2 mode - timer 2 is then set to cont mode
	STA $A00B


PLAY :	LDY NOTENUM		; what note to play next
	LDX TUNE,Y		; loads note number X from NOTES
	INY			;increment note number
	STY NOTENUM	        ;store next note
	CPX #$01		; check value from memory and play corresponding note
	BEQ NOTEA
	CPX #$02
	BEQ NOTEB
	CPX #$03
	BEQ NOTEC
	CPX #$04
	BEQ NOTEC
	CPX #$05
	BEQ NOTED
	CPX #$06
	BEQ NOTEE
	CPX #$07
	BEQ NOTEF
	CPX #$08
	BEQ NOTEG
	CPX #$FF		; FF symbolises end of tune
	BEQ ENDJUMP



	JSR WAIT
	
	
	JMP PLAY	; jump back up to play next note
	
ENDJUMP: JMP TMRSTP


WAIT:   STX TEMPX
        STY TEMPY
        LDX #$00                ;WAITS FOR SPECIFIED DURATION BETWEEN EACH NOTE
WOL:	INX
	LDY #$00
WIL:	INY
	CPY PAUSEY		;WAIT MULTIPLIER
	BNE WIL
	CPX PAUSEX		;WAIT DURATION
	BNE WOL
	LDY TEMPY
        LDX TEMPX
	INC
	RTS   



NOTEA:	LDA #$C6  	; sets timer 2 to the corresponding note frequency
	STA $A008 	;lsb
	LDA #$01
	STA $A009 	;msb
	JMP PLAY		


NOTEB:	LDA #$94 
	STA $A008 	;lsb
	LDA #$01
	STA $A009 	;msb
	JMP PLAY	

NOTEC:	LDA #$7E 
	STA $A008 	;lsb
	LDA #$01
	STA $A009 	;msb
	JMP PLAY

NOTED:	LDA #$54 
	STA $A008 	;lsb
	LDA #$01
	STA $A009 	;msb
	JMP PLAY

NOTEE:	LDA #$2F 
	STA $A008 	;lsb
	LDA #$01
	STA $A009 	;msb
	JMP PLAY

NOTEF:	LDA #$0E 
	STA $A008 	;lsb
	LDA #$01
	STA $A009 	;msb
	JMP PLAY

NOTEG:	LDA #$FF 
	STA $A008 	;lsb
	LDA #$00
	STA $A009 	;msb
	JMP PLAY


TUNE :  BYT #$01        ;NOTES FOR TUNE
        BYT #$02
	BYT #$03
	BYT #$04
	BYT #$05
	BYT #$06
	BYT #$07
	BYT #$08
	BYT #$07
	BYT #$06
	BYT #$05
	BYT #$04
	BYT #$03
	BYT #$02
	BYT #$01
	BYT #$02
	BYT #$04
	BYT #$06
	BYT #$08
	BYT #$07
	BYT #$05
	BYT #$03
	BYT #$01
        BYT #$FF		;FF - end of tune




TMRSTP: LDA #$00        	; by setting shift reg to 000 it will
	STA $A00B     	; stop the timer and shift register

	ORG $FFFC
	WRD SETCB2

END			; the end
User avatar
GARTHWILSON
Forum Moderator
Posts: 8775
Joined: 30 Aug 2002
Location: Southern California
Contact:

Post by GARTHWILSON »

> i have come accross limited resources documenting this feature

You can find the data sheets on this website starting at http://www.6502.org/documents/datasheets/ See also my tips at http://www.6502.org/forum/viewtopic.php?t=342 There are four pages in that subject matter. What you're dealing with starts at about "Tip of the Day, #5".


> and all i have seen is that i should set bits 2-4 as 100 of ACR
>
> LDA #$08
> STA $A00B
>
> is this right?

No. You have it in bits 1-3. (Bit 0 is the 1's place.) You would want $10, not 8.

For clarity and having less confusion getting things working, I would recommend a couple of things here. In this case, the hex or even decimal value is irrelevant. To show individual bits, use LDA #00010000B. Also, use assembler labels wherever possible. A00B does not tell us humans much. You just have to remember what it is, or keep writing it in the comments. Near the top of the code there should be something like:

Code: Select all

VIA:      EQU  A000
VIAPB:    EQU  VIA+0
VIAPA:    EQU  VIA+1
VIADDRB:  EQU  VIA+2
VIADDRA:  EQU  VIA+3
 ...
VIAACR:   EQU  VIA+B
 ...
Beside making it clear where you're storing the number, you have the added benefit of not having to find and change hundreds of lines of your program if a hardware change requires changing the base address of the VIA for example. You'd just change the one line and the assembler takes care of the rest.

Then your lines above will say

Code: Select all

     LDA   #00010000B  ; Set VIA's SR to shift out
     STA   VIAACR      ; free-running under T2 control.
> Apparently, by changing the values in timer 2, the rate at which the
> shifting is done should change:
>
> LDA #$C6
> STA $A008
> LDA #$01
> STA A009

Unfortunately, T2CH will have no effect when using T2 for shift register timing. That means you're left with only 256 speeds, range from very, very high notes to ultrasonic. In fact, with a clock speed of about 10MHz or above, they'd all be ultrasonic. Not much good. Instead, use T1 free-running, toggling PB7. You'll get better resolution, and you can go down below 20Hz with a 2MHz clock speed. You say you wanted to use PB below. Can you use PA instead, so PB7 can be used for your square-wave output? Or do you have more than one VIA?

I should add here that there is something else related you can do with the shift register, which is to put an RC filter on CB2 and use it as a 9-level D/A converter. This is the equivalent of slightly more than 3 bits. Then you can play back a complex sampled waveform instead of just a square wave. Believe it or not, 9 levels is plenty for intelligible speech, DTMF, and other things. Some talking toys use only a 2-bit converter.


> Also do i need to set port B as an output or is this done automatically?

At reset, PA and PB are set to all inputs, not outputs. To make a bit an output, write a 1 to its corresponding bit in the data direction register. Making PB all outputs would require storing FF to VIADDRB.


> Any help really is GREATLY appreciated.

To simplify your code, why not have a note table instead of a routine for each note. Use the same routine to look up all the timer latch values from the table. That way it's easy to have the entire chromatic scale, as many octaves as you want.
thesylph005
Posts: 3
Joined: 02 Dec 2004

Post by thesylph005 »

Thanks for your help.

I have had a look into using timer 1 and PB7. The datasheed has the essential info but its not too well documented elsewhere.

i have written this code....

Code: Select all

ORG $F800
SETVAL:	SET TEMPY=$00
	SET TEMPX=$00
	SET PAUSEX=$50		;PAUSEX/Y set to 50k cycles
	SET PAUSEY=$C3
	SET NOTENUM=$00		;note number - next note to play - 00=start
	

SETCB2:	LDA #$FF
	STA $A002  	; set port B as output

TMRSET:	LDA#$50        	; set timer 1 to free run mode
	STA $A00B

NOTEA:	LDA #$C6  	;sets timer 1 to the corresponding note frequency
	STA $A004 	;lsb
	LDA #$01
	STA $A005 	;msb


WAIT:   STX TEMPX
        STY TEMPY
        LDX #$00                ;WAITS FOR SPECIFIED DURATION BETWEEN EACH NOTE
WOL:	INX
	LDY #$00
WIL:	INY
	CPY PAUSEY		;WAIT MULTIPLIER
	BNE WIL
	CPX PAUSEX		;WAIT DURATION
	BNE WOL
	LDY TEMPY
        LDX TEMPX
	INC

	JMP TMRSET	


END			; the end

should that be ok ?

Also, if i had a short loop which kept setting the timer and the timer time, would that result in no wave being generated on PB7 ?

(if the code above is not correct, what should i be doing?)

thanks again.
User avatar
GARTHWILSON
Forum Moderator
Posts: 8775
Joined: 30 Aug 2002
Location: Southern California
Contact:

Post by GARTHWILSON »

> SETVAL: SET TEMPY=$00
> SET TEMPX=$00
> SET PAUSEX=$50 ;PAUSEX/Y set to 50k cycles
> SET PAUSEY=$C3
> SET NOTENUM=$00 ;note number - next note to play - 00=start

These appear to be zero page variables since you're storing to them later. If that's the case, you'll want to fix it so TEMPX and TEMPY are not the same location.


> TMRSET: LDA#$50 ; set timer 1 to free run mode
> STA $A00B

You want

Code: Select all

      LDA  #11000000B  ; Set T1 for continuous timed inter-
      STA  VIA_ACR     ; rupts and sqare wave output on PB7
11000000B is $C0 but a hex number here wouldn't tell us anything useful because it's individual bits that matter here. If you had other things going that might be using the ACR and you didn't want to disturb those, you would OR-in bits 6 and 7 instead of blindly storing 0's to all the other bits.

Note that although the ACR is set to make T1 produce continuous timed interrupts, those interrupts will not be reflected at the VIA's IRQ output unless you also set the corresponding bit in the interrupt enable register (IER). IOW, it doesn't necessarily mean the processor will keep receiving interrupts.


> NOTEA: LDA #$C6 ;sets timer 1 to the corresponding note frequency
> STA $A004 ;lsb
> LDA #$01
> STA $A005 ;msb

How about using a single routine for the note:

Code: Select all

NOTE: LDA  NOTE_TBL_LO,X
      STA  VIAT1CL
      LDA  NOTE_TBL_HI,X
      STA  VIAT1CH
      RTS
 ;----------------------

NOTE_TBL_LO:  DFB  <byte, byte, byte, byte, elc..>
NOTE_TLB_HI:  DFB  <byte, byte, byte, byte, etc..>
Then you'd put the note number in X and do JSR NOTE (or straight-line it if you wish).

If you want to assign even-number-only values to notes, or even put the note in the accumulator first and do a single ASL A (to multiply by two), you could but both the low byte and the high byte of the note together so you have a list of 16-bit words in the table, and then get it all in one table. That would make the table a little more programmer-friendly.

You could use a table for the delays as well, so you only have one delay routine whose delay is determined by the value you've stored in a variable. You can of course change the variable as often as you wish for different lengths of note, but it could remain unchanged for long periods if you have a series of notes that all have the same timing value (sixteenth notes for example.)

Turning the note off for a rest could be done in several ways, including disabling the free-running of the counter, disabling PB7 output, or even giving the counter a number that would result in a frequency too high to hear. If you disable the counter, remember to set the two high bits of the ACR again before writing to the counter. Oh, and one more thing (this is not in the data sheet)-- You can write to the T1 latches, but you'll have to write directly to the T1 counters to get it going after setting up the ACR.

I will strongly recommend that in order to make better programming progress in the long run, you work toward getting more of the numbers out of the code (for reasons stated in the last post), shortening the code by using look-up tables and factoring out subroutines, and if your assembler supports macros, learning to use them too. Modularizing becomes more and more important as program size grows. A macro is a piece of assembly-language programming that you yourself write, but then its internal details hide behind the name when you call it as if it were just a very sophisticated assembly-language instruction. Unlike subroutines, macros don't have the JSR/RTS penalty in runtime delays, and they can use conditional assembly to assemble the final code different ways depending on the parameters you give them. They usually have no penalty in program size or execution speed, and yet they effectively raise the level of the language. The point is to more clearly see the big picture, keep control of the project, make better progress, and produce fewer bugs. None of this means giving up any control of internal details-- it just means you don't have to be distracted by them when it's not time to be.
Last edited by GARTHWILSON on Mon Dec 06, 2004 7:50 am, edited 1 time in total.
Mats
Posts: 111
Joined: 24 Aug 2003

Post by Mats »

http://www.6502.org/documents/datasheet ... r_2004.pdf

is really not complete for the description of the 6522! I think it is rather a starting point for experiments/tests!

thesylph005 asks if port B should be set to output mode in order to generate a continous square wave on the CB2 pin!

GARTHWILSON answers to this:

At reset, PA and PB are set to all inputs, not outputs. To make a bit an output, write a 1 to its corresponding bit in the data direction register. Making PB all outputs would require storing FF to VIADDRB.

Well this is true but not the answer to the question! This is how to set the the mode of the pins PA0-PA7 and PB0-PB7. But thesylph005 asks about the CB2 pin! For "handshake" data transfers CA1 and CB1 are control lines from the external device and CA2 and CB2 are control lines to the external device, i.e. CA1 and CB1 are in input mode and CA2 and CB2 in output mode. But CA2 and CB2 can also be in input mode. This is controlled by the bits 7 and 3 of PCR ($C). From the description of shift input in http://www.6502.org/documents/datasheet ... r_2004.pdf it is seen that CB1 is in output mode when "Shift In" is in operation. The only logical explanation would be that the modes of CB1 CB2 are controlled by bits 4,3,2 of of ACR ($B) and of bit7 of PCR ($C)as follows:

0 0 0 => CB1 input mode CB2 in the mode specified by bit7 of PCR ($C)
0 0 1 => CB1 output mode CB2 input mode
0 1 0 => CB1 output mode CB2 input mode
0 1 1 => CB1 output mode CB2 input mode
1 0 0 => CB1 output mode CB2 output mode
1 0 1 => CB1 output mode CB2 output mode
1 1 0 => CB1 output mode CB2 output mode
1 1 1 => CB1 output mode CB2 output mode

Is this true? I have not yet used the shift register functions! I always had bits 4,3,2 = 0 , CA1 and CB1 were control lines from the external device (input mode) and CA2 and CB2 were control lines to the external device (output mode).

If somebody (Garth?) knows exactly how the shift operations work why not describe it!

My guess:

When bits 4,3,2 of of ACR ($B) are 1 0 0 and data is written into the Shift Register ($A) the T2 Low-order latch is copied to the T2 low-order counter and the count down starts. When the T2 low-order counter reaches zero there is a shift (i.e. roll, SR7 is re-circulated back to SR0) ) and CB2 is set to the value of the bit(SR7 or SR0?). Then the T2 Low-order latch is again copied to the T2 low-order counter and the count down to next shift starts again etc.

Is this true? If yes, what happens if the T2 counter already was in use (by having written something to T2H ($09) ) ? And how is this "infinite loop" stopped? By writing to ACR ($B) setting the bits to something else then 1 0 0?


User avatar
GARTHWILSON
Forum Moderator
Posts: 8775
Joined: 30 Aug 2002
Location: Southern California
Contact:

Post by GARTHWILSON »

Quote:
http://www.6502.org/documents/datasheets/wdc/wdc_w65c22s_mar_2004.pdf

is really not complete for the description of the 6522! I think it is rather a starting point for experiments/tests!
Actually the data sheets are pretty complete-- not 100%, but close to it. Sometimes it seems like something's not there until you struggle over a problem for a long time, and when you finally get it figured out, you go back to the data sheet, and find the information was hiding there all along but there was no big red font calling your attention to the part that's easy to slip on. I've included a few things that aren't there, like that the proper sequence to get T1 running in free-run mode is to set up the ACR and then write directly to the counters, followed by writing to the latches later if you want to. That's something I got from an ap. note a Rockwell engineer faxed me years ago. The data sheets also won't tell you about the one bug with shifting under control of an external shift clock, but that's irrelevant when CB1 is outputting the shift clock.

I mostly use my Synertek data book for the VIA just because that's where I have all my notes, highlightings, etc.. Of course I keep in mind a few improvements in WDC's VIA, like that it has symmetrical totem-pole outputs and its inputs are high-impedance instead of presenting an LSTTL load. What the data sheets won't tell you is all the things you can do with the VIA. They just give you the sterile facts and it's up to the user to be creative in what can be done with the tools provided.

Quote:
thesylph005 asks if port B should be set to output mode in order to generate a continous square wave on the CB2 pin!

GARTHWILSON answers to this:

At reset, PA and PB are set to all inputs, not outputs. To make a bit an output, write a 1 to its corresponding bit in the data direction register. Making PB all outputs would require storing FF to VIADDRB.

Well this is true but not the answer to the question!
thesylph005 did not specifically link the PB output question with the CB1 & CB2 lines, so my assumption was that he wanted to use PB for something else in the project. I understand though that my answer could be confusing. The CA and CB pins are controlled by the ACR and PCR, not DDRA and DDRB. In my own many uses, I have always treated the ports' bit data direction as unrelated to the CA and CB pins. When they're used for the handshaking spoken of in the data sheet, it's mostly for things like feeding data to a parallel printer; but I've never used them that way even when I was feeding data to a printer with the VIA. There's more than one way to skin a cat. Anyway, the CB1 and CB2 data directions for shift register usage are set by the ACR bits that determine the shift register operation mode. One of our products flying in aircraft all over the world without problems uses the VIA's shift register without ever touching the PCR at all, anywhere in the program.

Although I have not specifically re-quoted all the earlier questions, this should provide answers. When questions seem to make wrong assumptions, it's sometimes more productive to change the approach.

Regarding CB1&2 data directions: When you're using the shift register (SR), the first of the three binary digits determining the SR mode tells the CB2 (data) direction. 0=input, 1=output, like bits in DDRA and DDRB. The next bit tells if T2 is used. 0=yes, 1=no. The last bit of the SR mode control is not quite as simple, but it will never be 0 when using an external shift clock source. I never really thought about it this way. I just look up the mode I want in the table in the data sheet.
Quote:
If somebody (Garth?) knows exactly how the shift operations work why not describe it!
I've used this IC a lot for scores of things. There's probably more that I'm forgetting to tell, but I'll be glad to keep answering questions as long as they come up. Sometimes the review is good for me anyway, and sometimes someone will ask something that drives me to try out something I'm not sure of.

In response to Mats' last two paragraphs: T2, when used separately from the shift register, is one-shot only. But when you use it with the SR, it does keep re-loading itself to start each new bit. Unless you're using SR mode 100, it will stop after the 8 bits are shifted in or out. If shifting out, the data on CB2 will retain the last bit's value until you give the SR another byte to shift out. T2 restarts itself for each byte. It is not necessary to keep writing to T2.

If you set up a SR mode that uses T2 even when T2 was already being used for something else first, it gets re-assigned to the SR. I have not tried this one, but the data sheet seems to be pretty clear on it, showing that T2 will count phase-2 cycles and not PB6 pulses for example. T2's high counter byte is ignored in SR use. (That information is hiding in the data sheet too.)
Mats
Posts: 111
Joined: 24 Aug 2003

Post by Mats »

Anyway this works:

Code: Select all

;GENERATE SQUARE WAVE WITH FREQUENCY OSCILLATOR/(4*256)
T2CL   = $F8     ; T2 LOW ORDER LATCHES
SR     = $FA     ; SHIFT REGISTER
ACR    = $FB     ; AUXILARY CONTROL REGISTER
     *=$6000
     LDA #$10    ;SHIFT OUT FREE RUNNING AT T2 RATE
     STA ACR
     LDA #$FF
     STA T2CL    ;SET "T2 RATE" (LOWEST POSSIBLE $FF)
     LDA #$F0
     STA SR      ;BIT PATTERN 11110000 TO SHIFT REGISTER; 
STO  BRA STO     ;READY!
I run it on my board.

But sometimes (very often) the W65C22S does not get properly initialised by power up and then nothing at all works!
User avatar
GARTHWILSON
Forum Moderator
Posts: 8775
Joined: 30 Aug 2002
Location: Southern California
Contact:

Post by GARTHWILSON »

> But sometimes (very often) the W65C22S does not get properly
> initialised by power up and then nothing at all works!

Having fast, clean edges on the reset pulse to the system (including to the VIAs) should take care of that; but actually I've never had any problem with a VIA failing to work because of a bad reset. All that the reset seems to do is to initialize the user-accessible registers, and you can set them up the way you want them by writing to them anyway. To do a software reset for the SR, you can write 000 to the three applicable bits in the ACR before proceeding.
Mats
Posts: 111
Joined: 24 Aug 2003

Post by Mats »

Just some final remarks about the shifting:

The basic "Shift out - PHI2 Control (110)" is done with half the PHI2 rate, see Fig 1-11 of the Data Sheet. With "T2 Control" (101) this rate is further reduced with a factor N+2, see Fig 1-10 of the Data Sheet. With a 1 MHz oscillator the minimal shift clock rate with N=255 is therefore 1000/(2*(255+2)) MHz. This rate is then on CB1 as squarewave. By writing 11110000 to the shift register the frequency on CB2 is then again reduced with a factor 4 to 1000/(4*2*(255+2)) MHz= 486 Hz.

The reset problem with at least some of the newly produced W65C22S is another story! All I want to say at this time is that if strange faults occure it could be due to that a W65C22S malfunctions after a bad reset!
Mats
Posts: 111
Joined: 24 Aug 2003

Post by Mats »

Finally a prototype program with the Garth recommended method with the output on PB7:

Code: Select all

;GENERATE A SQUARE WAVE WITH FREQUENCY 1/8000 OF THE OSCILLATOR FREQUENCY ON PB7
; 8 MHZ => 1000 HZ  8000/2 = $0FA0
DDRB   = $F2     ; DATA DIRECTION REGISTER B
T1CL   = $F4     ; T1 LOW ORDER COUNTER
T1CH   = $F5     ; T1 HIGH ORDER COUNTER
ACR    = $FB     ; AUXILARY CONTROL REGISTER
     *=$6000
     LDA #$80
     STA DDRB    ;PB7 TO OUTPUT MODE
     LDA #$C0
     STA ACR     ;TIMER 1 IN FREE-RUN MODE, SQUARE WAVE OUTPUT
     LDA #$A0
     STA T1CL    ;LOW PART OF COUNT
     LDA #$0F
     STA T1CH    ;HIGH PART OF COUNT, THE COUNT-DOWN STARTS
STO  BRA STO     ;READY!
Works fine! Lowest frequency is oscillator frequency/(2*65535)
Mats
Posts: 111
Joined: 24 Aug 2003

Post by Mats »

And just a very last comment about the code of my last post!

In the Data Sheet it is explained that also in the Free-Run mode the Interrupt Flag Register bit is set when the T1 counter has reached zero. As in this case (tone generation) one certainly does not want an interrupt here one should assure that either corresponding Interrupt Enable Register bit is unset or the Interrupt Disable is set (SEI)

To make sure that the Interrupt Enable Register bit is unset:

Code: Select all

  LDA #$40   ;01000000
  STA IER    ;Interrupt Enable Register
User avatar
GARTHWILSON
Forum Moderator
Posts: 8775
Joined: 30 Aug 2002
Location: Southern California
Contact:

Post by GARTHWILSON »

When the device comes out of reset, all IER and IFR bits will be clear.
thesylph005
Posts: 3
Joined: 02 Dec 2004

Post by thesylph005 »

hi guys. thanks for the help.

below if my full final code which seems to work.
i added 4 button sensors on A inputs 0-3 which choose a tune to play.

Code: Select all



SETVAL:	SET TEMPX=$00
	SET TEMPY=$01
	SET PAUSEX=$02		
	SET PAUSEY=$03
	SET NOTENUM=$04
	SET TUNENUM=$05

	SET VIAT1L=$4004  ; via timer 1 locations
	SET VIAT1M=$4005	
	SET VIAACR=$400B  ; ACR address
	SET VIADDRB=$4002 ; DDRB address 
	SET VIAIER=$400E  ; IER
	SET VIADDRA=$4003 ; A Data Direction Register
	SET VIADRA=$4001  ; A Data Register	
	
ORG $F800


        LDA #$00   	        ; initiate or reload values
        STA TEMPX
        STA TEMPY
        STA NOTENUM
        LDA #$50
        STA PAUSEX
        LDA #$C3
	STA PAUSEY


SETCB2:	LDA #$FF
	STA VIADDRB  	        ; set port B as output
	LDA #$00
	STA VIADDRA		; set port A as inputs



MENU:   LDA #$00   	        ; initiate or reload values
        STA TEMPX
        STA TEMPY
        STA NOTENUM
       
        LDX VIADRA		; LOAD x with VIA INPUT A
	STX TUNENUM		; save input value to remember what button pressed

        CPX %00000001		;check button 1
        BEQ PLAY		;if on, play 				
        CPX %00000010		;check button 2
        BEQ PLAY		;if on, play
        CPX %00000100		;check button 3
        BEQ PLAY		;if on, play
        CPX %00001000		;check button 4
        BEQ PLAY		;if on, play
        JMP MENU		;LOOP
        


TMRSET:	LDA  %11000000       	; set timer 1 to repeat   ($c0)
	STA VIAACR		; and interupt outputs on PB7

; ----------------NOT SURE IF THE 2 LINES BELOW ARE NEEDED-------------

INTSET: LDA #$40   		;01000000 Enable interupt for timer 1
 	STA VIAIER    		;Interrupt Enable Register 
; ---------------------------------------------------------------------






PLAY :	LDY NOTENUM		; what note to play next
	INY			; increment note number
	STY NOTENUM	        ; store next note

	LDA TUNENUM		; find out what tune we are playing

	CMP %00000001		; run subroutine to load corresponding tune
	JSR LOAD1		; x will be loaded with note Y from tunes table
	JSR TIMES1		; LOAD THE WAIT TIME AS WELL

	CMP %00000010
	JSR LOAD2
	JSR TIMES2

	CMP %00000100
	JSR LOAD3
	JSR TIMES3

	CMP %00001000
	JSR LOAD4
	JSR TIMES4	
	
	
	CPX #$FF		; FF symbolises end of tune
	BEQ ENDJUMP		; then jump to end - TMRSTP via ENDJUMP
				; otherwise

SETNOTE: LDA  NOTELSB,X 	; look up note X vlaues from notes tables and
      	STA  VIAT1L 		; set the values into timer 1
      	LDA  NOTEMSB,X 
      	STA  VIAT1M 
      	 
	JSR WAIT		; wait a while for the note to be played
	
	JMP PLAY		; jump back up to play next note
	




ENDJUMP: JMP MENU		; end of tune, go back to MENU





; --------------------- LOAD NOTE SUBROUTINES [accessed through PLAY method]----


LOAD1:	LDX TUNE1,Y		; loads note number Y from TUNE1
	RTS

LOAD2:	LDX TUNE2,Y		; loads note number Y from TUNE2
	RTS

LOAD3:	LDX TUNE3,Y		; loads note number Y from TUNE3
	RTS

LOAD4:	LDX TUNE4,Y		; loads note number Y from TUNE4
	RTS



TIMES1  LDA TIMES1,Y		; GET WAIT TIME FROM TIMES TABLE
	STA PAUSEY
	RTS

TIMES2  LDA TIMES2,Y		; GET WAIT TIME FROM TIMES TABLE
	STA PAUSEY
	RTS

TIMES3  LDA TIMES3,Y		; GET WAIT TIME FROM TIMES TABLE
	STA PAUSEY
	RTS

TIMES4  LDA TIMES4,Y		; GET WAIT TIME FROM TIMES TABLE
	STA PAUSEY
	RTS

;-------------------------------------------------------------------------------






WAIT:   STX TEMPX
        STY TEMPY
        LDX #$00                ;WAITS FOR SPECIFIED DURATION BETWEEN EACH NOTE
WOL:	INX
	LDY #$00
WIL:	INY
	CPY PAUSEY		;WAIT MULTIPLIER
	BNE WIL
	CPX PAUSEX		;WAIT DURATION
	BNE WOL
	LDY TEMPY
        LDX TEMPX
	INC
	RTS   






; --------------------- BELOW ARE TABLES FOR NOTES AND THE TUNES





TUNE1:  BYT #$01  ;   NOTES FOR TUNE ------ 
        BYT #$02
	BYT #$03
	BYT #$04
	BYT #$05
	BYT #$06
	BYT #$07
	BYT #$08
	BYT #$07
	BYT #$06
	BYT #$05
	BYT #$04
	BYT #$03
	BYT #$02
	BYT #$01
	BYT #$02
	BYT #$04
	BYT #$06
	BYT #$08
	BYT #$07
	BYT #$05
	BYT #$03
	BYT #$01
        BYT #$FF   ;FF - end of tune

TIMES1: BYT #$BF  ;   TIMING ------- [ 1/4 => 3F ][ 1/2 => 7F ][ 3/4 => BF ] [ 1/1 => FF ]  
        BYT #$BF
	BYT #$BF
	BYT #$BF
	BYT #$BF
	BYT #$BF
	BYT #$BF
	BYT #$BF
	BYT #$BF
	BYT #$BF
	BYT #$BF
	BYT #$BF
	BYT #$BF
	BYT #$BF
	BYT #$BF
	BYT #$BF
	BYT #$BF
	BYT #$BF
	BYT #$BF
	BYT #$BF
	BYT #$BF
	BYT #$BF
	BYT #$BF
        BYT #$BF   ; end of tune








TUNE2:  BYT #$01   ;   NOTES FOR TUNE
        BYT #$01
	BYT #$01
	BYT #$01
	BYT #$03
	BYT #$03
	BYT #$03
	BYT #$03
	BYT #$05
	BYT #$05
	BYT #$05
	BYT #$05
	BYT #$02
	BYT #$05
	BYT #$06
	BYT #$06
	BYT #$06
	BYT #$01
	BYT #$02
	BYT #$03
        BYT #$FF   ;FF - end of tune


TIMES2: BYT #$BF   ;     TIMING ------- [ 1/4 => 3F ][ 1/2 => 7F ][ 3/4 => BF ] [ 1/1 => FF ]  
        BYT #$BF
	BYT #$BF
	BYT #$BF
	BYT #$BF
	BYT #$BF
	BYT #$BF
	BYT #$BF
	BYT #$BF
	BYT #$BF
	BYT #$BF
	BYT #$BF
	BYT #$BF
	BYT #$BF
	BYT #$BF
	BYT #$BF
	BYT #$BF
	BYT #$BF
	BYT #$BF
	BYT #$BF
        BYT #$FF   ;FF - end of tune









;--------------------------------- OCTAVES DEMO --------------------------------


TUNE3:  BYT #$00   ;C0   
        BYT #$04   ;E0
	BYT #$09   ;A0
	BYT #$0C   ;C1
	BYT #$10   ;E1
	BYT #$15   ;A1
	BYT #$18   ;C2
	BYT #$1C   ;E2
	BYT #$21   ;A2
	BYT #$24   ;C3
	BYT #$28   ;E3
	BYT #$2D   ;A3
	BYT #$30   ;C4
	BYT #$34   ;E4
	BYT #$39   ;A4
	BYT #$3C   ;C5
	BYT #$40   ;E5
	BYT #$45   ;A5
	BYT #$48   ;C6
	BYT #$4C   ;E6
	BYT #$51   ;A6
	BYT #$54   ;C7
	BYT #$59   ;E7
	BYT #$5D   ;A7
	BYT #$60   ;C8  TOP
	BYT #$64   ;PAUSE
	BYT #$30   ; LITTLE TUNEY THING BELOW
	BYT #$34
	BYT #$39
	BYT #$34
	BYT #$30
	BYT #$34
	BYT #$39
	BYT #$34   
	BYT #$39
	BYT #$3C
	BYT #$40
	BYT #$3C
	BYT #$39
	BYT #$3C
	BYT #$40
	BYT #$3C
	BYT #$30   
	BYT #$34
	BYT #$2D
	BYT #$24
	BYT #$24
	BYT #$24
	BYT #$24
	BYT #$48
        BYT #$FF   ;FF - end of tune



TIMES3: BYT #$BF   ;TIMING ------- [ 1/4 => 3F ][ 1/2 => 7F ][ 3/4 => BF ] [ 1/1 => FF ]  
	BYT #$BF   
	BYT #$BF
	BYT #$BF
	BYT #$BF
	BYT #$BF
	BYT #$BF
	BYT #$BF
	BYT #$BF
	BYT #$BF
	BYT #$BF
	BYT #$BF
	BYT #$BF
	BYT #$BF
	BYT #$BF
	BYT #$BF
	BYT #$BF
	BYT #$BF
	BYT #$BF
	BYT #$BF
	BYT #$BF
	BYT #$BF
	BYT #$BF
	BYT #$BF
	BYT #$BF
	BYT #$7F
	BYT #$7F
	BYT #$7F
	BYT #$7F
	BYT #$7F
	BYT #$7F
	BYT #$7F
	BYT #$7F
	BYT #$7F
	BYT #$7F
	BYT #$7F
	BYT #$3F
	BYT #$3F
	BYT #$3F
	BYT #$3F
	BYT #$3F
	BYT #$3F
	BYT #$3F
	BYT #$3F
	BYT #$3F
	BYT #$3F
	BYT #$3F
	BYT #$3F
	BYT #$FF
	BYT #$FF
	BYT #$FF   ; END










;--------------------------------- HAPPY BIRTHDAY --------------------------------


TUNE4:  BYT #$37   ;G   
        BYT #$37   ;G
        BYT #$39   ;A
        BYT #$37   ;G
        BYT #$30   ;C
        BYT #$3B   ;B
        BYT #$64   ;NO NOTE REST
        BYT #$37   ;G
        BYT #$37   ;G
        BYT #$39   ;A
        BYT #$37   ;G
        BYT #$32   ;D
        BYT #$30   ;C
        BYT #$64   ;NO NOTE REST
        BYT #$37   ;G
        BYT #$37   ;G
        BYT #$42   ;G5
        BYT #$34   ;E
        BYT #$30   ;C
        BYT #$3B   ;B
        BYT #$39   ;A
        BYT #$64   ;NO NOTE REST
        BYT #$35   ;F
        BYT #$35   ;F
        BYT #$34   ;E
        BYT #$30   ;C
        BYT #$32   ;D
        BYT #$30   ;C
        BYT #$64   ;NO NOTE REST
	BYT #$FF   ; END

TIMES4: BYT #$BF   ;TIMING ------- [ 1/4 => 3F ][ 1/2 => 7F ][ 3/4 => BF ] [ 1/1 => FF ]  
	BYT #$BF   
	BYT #$BF
	BYT #$BF
	BYT #$FF
	BYT #$FF
	BYT #$BF
	BYT #$BF
	BYT #$BF
	BYT #$BF
	BYT #$BF
	BYT #$FF
	BYT #$FF
	BYT #$BF
	BYT #$BF
	BYT #$BF
	BYT #$FF
	BYT #$FF
	BYT #$BF
	BYT #$BF
	BYT #$FF
	BYT #$BF
	BYT #$BF
	BYT #$BF
	BYT #$BF
	BYT #$FF
	BYT #$BF
	BYT #$BF
	BYT #$BF
	BYT #$FF   ; END





; -------------------NOTE TABLES---------------------------------



;------------------ TIMER 1 LSB VALUES-----------------------------

;------------------ROW NUMBER---NOTE-----

NOTELSB: BYT #$E4	;00	C0
	BYT #$8D	;01	C#0/Db0
	BYT #$49	;02	D0
	BYT #$15	;03	D#0/Eb0
	BYT #$F6	;04	E0
	BYT #$E4	;05	F0
	BYT #$E5	;06	F#0/Gb0
	BYT #$F1	;07	G0
	BYT #$0C	;08	G#0/Ab0
	BYT #$34	;09	A0
	BYT #$67	;0A	A#0/Bb0
	BYT #$A7	;0B	B0
	BYT #$F2	;0C	C1
	BYT #$46	;0D	C#1/Db1
	BYT #$A4	;0E	D1
	BYT #$0B	;0F	D#1/Eb1
	BYT #$7B	;10	E1
	BYT #$F2	;11	F1
	BYT #$72	;12	F#1/Gb1
	BYT #$F8	;13	G1
	BYT #$86	;14	G#1/Ab1
	BYT #$1A	;15	A1
	BYT #$B4	;16	A#1/Bb1
	BYT #$53	;17	B1
	BYT #$F8	;18	C2
	BYT #$A3	;19	C#2/Db2
	BYT #$52	;1A	D2
	BYT #$05	;1B	D#2/Eb2
	BYT #$BD	;1C	E2
	BYT #$79	;1D	F2
	BYT #$39	;1E	F#2/Gb2
	BYT #$FC	;1F	G2
	BYT #$C3	;20	G#2/Ab2
	BYT #$8D	;21	A2
	BYT #$5A	;22	A#2/Bb2
	BYT #$29	;23	B2
	BYT #$FC	;24	C3
	BYT #$D1	;25	C#3/Db3
	BYT #$A9	;26	D3
	BYT #$82	;27	D#3/Eb3
	BYT #$5E	;28	E3
	BYT #$3C	;29	F3
	BYT #$1C	;2A	F#3/Gb3
	BYT #$FE	;2B	G3
	BYT #$E1	;2C	G#3/Ab3
	BYT #$C6	;2D	A3
	BYT #$AD	;2E	A#3/Bb3
	BYT #$94	;2F	B3
	BYT #$7E	;30	C4
	BYT #$68	;31	C#4/Db4
	BYT #$54	;32	D4
	BYT #$41	;33	D#4/Eb4
	BYT #$2F	;34	E4
	BYT #$1E	;35	F4
	BYT #$0E	;36	F#4/Gb4
	BYT #$FF	;37	G4
	BYT #$F0	;38	G#4/Ab4
	BYT #$E3	;39	A4
	BYT #$D6	;3A	A#4/Bb4
	BYT #$CA	;3B	B4
	BYT #$BF	;3C	C5
	BYT #$B4	;3D	C#5/Db5
	BYT #$AA	;3E	D5
	BYT #$A0	;3F	D#5/Eb5
	BYT #$97	;40	E5
	BYT #$8F	;41	F5
	BYT #$87	;42	F#5/Gb5
	BYT #$7F	;43	G5
	BYT #$78	;44	G#5/Ab5
	BYT #$71	;45	A5
	BYT #$6B	;46	A#5/Bb5
	BYT #$65	;47	B5
	BYT #$5F	;48	C6
	BYT #$5A	;49	C#6/Db6
	BYT #$55	;4A	D6
	BYT #$50	;4B	D#6/Eb6
	BYT #$4B	;4C	E6
	BYT #$47	;4D	F6
	BYT #$43	;4E	F#6/Gb6
	BYT #$3F	;4F	G6
	BYT #$3C	;50	G#6/Ab6
	BYT #$38	;51	A6
	BYT #$35	;52	A#6/Bb6
	BYT #$32	;53	B6
	BYT #$2F	;54	C7
	BYT #$2D	;55	C#7/Db7
	BYT #$2A	;56	D7
	BYT #$28	;57	D#7/Eb7
	BYT #$25	;58	E7
	BYT #$23	;59	F7
	BYT #$21	;5A	F#7/Gb7
	BYT #$1F	;5B	G7
	BYT #$1E	;5C	G#7/Ab7
	BYT #$1C	;5D	A7
	BYT #$1A	;5E	A#7/Bb7
	BYT #$19	;5F	B7
	BYT #$17	;60	C8
	BYT #$16	;61	C#8/Db8
	BYT #$15	;62	D8
	BYT #$14	;63	D#8/Eb8
	BYT #$FF	;64	NO NOTE


;----------------------TIMER 1 MSB VALUES-----------------------


;------------------ROW NUMBER---NOTE-----

NOTEMSB: BYT #$17	;00	C0
	BYT #$16	;01	C#0/Db0
	BYT #$15	;02	D0
	BYT #$14	;03	D#0/Eb0
	BYT #$12	;04	E0
	BYT #$11	;05	F0
	BYT #$10	;06	F#0/Gb0
	BYT #$0F	;07	G0
	BYT #$0F	;08	G#0/Ab0
	BYT #$0E	;09	A0
	BYT #$0D	;0A	A#0/Bb0
	BYT #$0C	;0B	B0
	BYT #$0B	;0C	C1
	BYT #$0B	;0D	C#1/Db1
	BYT #$0A	;0E	D1
	BYT #$0A	;0F	D#1/Eb1
	BYT #$09	;10	E1
	BYT #$08	;11	F1
	BYT #$08	;12	F#1/Gb1
	BYT #$07	;13	G1
	BYT #$07	;14	G#1/Ab1
	BYT #$07	;15	A1
	BYT #$06	;16	A#1/Bb1
	BYT #$06	;17	B1
	BYT #$05	;18	C2
	BYT #$05	;19	C#2/Db2
	BYT #$05	;1A	D2
	BYT #$05	;1B	D#2/Eb2
	BYT #$04	;1C	E2
	BYT #$04	;1D	F2
	BYT #$04	;1E	F#2/Gb2
	BYT #$03	;1F	G2
	BYT #$03	;20	G#2/Ab2
	BYT #$03	;21	A2
	BYT #$03	;22	A#2/Bb2
	BYT #$03	;23	B2
	BYT #$02	;24	C3
	BYT #$02	;25	C#3/Db3
	BYT #$02	;26	D3
	BYT #$02	;27	D#3/Eb3
	BYT #$02	;28	E3
	BYT #$02	;29	F3
	BYT #$02	;2A	F#3/Gb3
	BYT #$01	;2B	G3
	BYT #$01	;2C	G#3/Ab3
	BYT #$01	;2D	A3
	BYT #$01	;2E	A#3/Bb3
	BYT #$01	;2F	B3
	BYT #$01	;30	C4
	BYT #$01	;31	C#4/Db4
	BYT #$01	;32	D4
	BYT #$01	;33	D#4/Eb4
	BYT #$01	;34	E4
	BYT #$01	;35	F4
	BYT #$01	;36	F#4/Gb4
	BYT #$00	;37	G4
	BYT #$00	;38	G#4/Ab4
	BYT #$00	;39	A4
	BYT #$00	;3A	A#4/Bb4
	BYT #$00	;3B	B4
	BYT #$00	;3C	C5
	BYT #$00	;3D	C#5/Db5
	BYT #$00	;3E	D5
	BYT #$00	;3F	D#5/Eb5
	BYT #$00	;40	E5
	BYT #$00	;41	F5
	BYT #$00	;42	F#5/Gb5
	BYT #$00	;43	G5
	BYT #$00	;44	G#5/Ab5
	BYT #$00	;45	A5
	BYT #$00	;46	A#5/Bb5
	BYT #$00	;47	B5
	BYT #$00	;48	C6
	BYT #$00	;49	C#6/Db6
	BYT #$00	;4A	D6
	BYT #$00	;4B	D#6/Eb6
	BYT #$00	;4C	E6
	BYT #$00	;4D	F6
	BYT #$00	;4E	F#6/Gb6
	BYT #$00	;4F	G6
	BYT #$00	;50	G#6/Ab6
	BYT #$00	;51	A6
	BYT #$00	;52	A#6/Bb6
	BYT #$00	;53	B6
	BYT #$00	;54	C7
	BYT #$00	;55	C#7/Db7
	BYT #$00	;56	D7
	BYT #$00	;57	D#7/Eb7
	BYT #$00	;58	E7
	BYT #$00	;59	F7
	BYT #$00	;5A	F#7/Gb7
	BYT #$00	;5B	G7
	BYT #$00	;5C	G#7/Ab7
	BYT #$00	;5D	A7
	BYT #$00	;5E	A#7/Bb7
	BYT #$00	;5F	B7
	BYT #$00	;60	C8
	BYT #$00	;61	C#8/Db8
	BYT #$00	;62	D8
	BYT #$00	;63	D#8/Eb8
	BYT #$FF	;64	NO NOTE

	


;--------------------------------------END OF NOTES TABLES---------




TMRSTP: LDA #$00        ; by setting shift reg to 000 it will
	STA VIAACR     	; stop the timer
	RTS

	ORG $FFFC
	WRD SETCB2

END			; the end



this latest version has not been fully tested. but the main parts of the code seem to be ok.

thanks again for the help
Post Reply