Monday, 7 August 2023

6502 : BEN2 : Clock Timer

Basic timer

In addition to hardware interrupts the VIA (Versatile Interface Adapter) provides two timers which can generate interrupts and Ben Eater has a very good tutorial which explains their use.  In the first example a one shot timer counts down to 0 then sets a bit in the IFR (Interrupt Flag Register).  The counter is a 16-bit register and is decremented each clock pulse so the maximum count down is 65535, approximately 65mS.  If we toggle an LED and restart the counter each time the interrupt occurs we have an LED flashing at about 10Hz.

As you can see from the code below, a loop via_delay is used to check whether the IFR flag has been set by the counter.  Clearly it isn't very useful to sit in a loop waiting for a flag to be set although you could add more code to do other functions within that loop.


Tick Timer

Ben's second example shows a very good clock feature which we will be able to use regularly.  The timer is put into freerun mode so that it starts again as soon as it reaches zero.  In addition to setting the IFR a 6502 interrupt is generated.  The interrupt service routine (ISR) is invoked on the 6502 each time the counter hits zero, and simply updates a four-byte tick counter in zeropage memory.

The counter is setup to hit zero every 10ms so, if we want to toggle an LED every second, we simply check whether the tick counter has increased by 100.  The following program shows the various parts of the tick timer.  On startup init_timer is executed to zero the tick counter and the timer is set to $207e giving 10mS interrupts.  Irqvia is at a fixed address $1003 and is invoked to increase the tick count each time a timer interrupt occurs.  For this demo there is a loop which updates LED and LCD periodically.


Clock program

My VIAtest program is used to check out all the VIA functions so I need to add the timer function in.  As the VIA button interrupt, LED shift register and timer each use a selection of ACR (Auxiliary Control Register), PCR (Peripheral control register) and IER (Interrupt enable register) I first disabled the button and shift register temporarily.  I copied across the timer initialisatio routine and ISR (Interrupt Service Routine) code easily.  With these in place the tick count was automatically incremented, although it wasn't in use.

VIAtest displays a menu and waits for the user to enter a character to choose a function such as displaying an LCD message or turning on an LED.  I amended the program so that it checks to see whether a character has been entered rather using term_getc to wait for a character.  If no character has been entered, other routines are called to see whether the LCD or LED needs updating based on the tick count.  In fact this is a simple state machine which multi-tasks by looping round a number of functions.

To enable the shift register and button, I had to check the VIA initialisation routine carefully and ensure that all bits were set correctly.  The button and LED bar then worked as normal.

The LCD clock display was straightforward to setup.  I added some variables clock_time, hours, minutes and seconds to my zeropage variable list and arranged for the seconds counter to be incremented after 100 ticks.  As each tick is 10mS this gives me an update every second.  Minutes and hours variables were incremented after 60 seconds or 60 minutes were reached.
I could then display the time using the three variables as hh:mm:ss on the LCD.
The update works independently of other VIA test functions.



Saturday, 5 August 2023

6502 : BEN2 : Interruptions

Buttons

Ben Eater included a number of buttons in his original board specification.  I installed a couple of buttons on BEN2's daughter board AMI but haven't found a reason to make use of them yet.  Buttons are the most basic input method for a DIY computer and potentially you could be stuck with button input and LED or LCD output.  Thankfully by adding the ACIA I have the ability to keyboard / terminal input / output making buttons unnecessary.
However, a button can be very useful as an interrupt mechanism.  For example, my simple blinking LED test cant easily be terminated.  It would be good to be able to leave the LED blinking until a button is pressed to stop it.

Bens tutorials

Ben provides a couple of tutorials, "interrupts" and "interrupt handling basics" to familiarise use with their use.  The first one describes 6502 IRQB and NMIB pins which implement controllable and non-maskable interrupts respectively.  IRQB interrupts assume that the device at the other end should clear the interrupt situation; it has a tendency to report a lot (hundreds and thousands) of interrupts.  In fact Ben's "binary converting to decimal" (BCD) routine is very useful to count them.  Non-maskable interrupts are very dangerous as they can interrupt absolutely any code, including interrupt service routines or themselves, so they are rarely appropriate.
Ben's second video describes how the W65C22 VIA provides an enhanced interrupt capability and shows how to to set it up, so that was the one I implemented.

Hardware connections

The VIA has two pins CA1 and CA2 which can be setup for interrupts.  As I previously used CA2 for my shift register I used CA1.
I connected up a button with a pull-up resistor to the positive rail so that when the button is pressed the signal on CA1 changes from 5V to GND.
I also need VIA pin 21 (IRQB) connected to 6502 pin 4 (IRQB) but that was already in place in my original build for BEN2.

Reset Vector

The 6502 IRQ vector is fixed at memory address $FFFE-$FFFF.  I set the contents to be the address $1003.  This allows me to load my test programs at $1000 as usual and, by making the first instruction a "jmp start", the interrupt service routine will follow on immediately at $1003.

Test program

My test program was very simple.  At the start of the program I initialise the interrupt on CA1 by setting values in IER (Interrupt Enable Register) and PCR (Peripheral Control register)
Before that there is a simple call to a subroutine for interrupt handling (button_int) the subroutine below followed by a RTI (Return from Interrupt) opcode.


My interrupt handling routine was very basic, I just displayed a letter 'j' on the screen - very dull.
Still, this is a useful addition to my system.