![]() |
![]() |
#1 |
Registered User
![]() Join Date: Jul 2018
Location: Londonish / UK
Posts: 489
|
CIA timer for keyboard handshake
My keyboard reading code within my interrupt handler (listed below) currently waits for 3 scanlines to pass for the handshake.
I'd like to make it better. Just because. I've looked at several examples of using CIA timers for the handshake, but none of them quite match the other, and I'm not sure which is correct or best. Could someone spell it out to me? [CODE] extern struct Custom * custom; extern struct CIA * ciaa; if (ciaa->ciaicr & CIAICRF_SP) { UBYTE keycode = ciaa->ciasdr; ciaa->ciacra |= CIACRAF_SPMODE; if (DEBUG) KPrintF("In _GameInterruptHandler_processPORTS - keycode = %02lx.\n", (ULONG) keycode); UBYTE vhposr = custom->vhposr; for (int i = 0; i < 3; i++) { while (custom->vhposr == vhposr) ; } ciaa->ciacra &= ~CIACRAF_SPMODE; } [/CODE |
![]() |
![]() |
#2 |
Per aspera ad astra
![]() Join Date: Mar 2017
Location: Crossing the Rubicon
Age: 49
Posts: 2,230
|
I tell you immediately that I don't like scanlines wait at all.
In my keyboard handlers I almost use always a CIA timer. And in most cases I don't wait but I launch a time-out with a timer in one-shot mode and close the handshake in next IRQ. But now for the scanlines wait mode. The HRM says that the handshake must last at least 85us, so if you set a wait of 3 waitlines (so a time between 128us and 192us) you remain in the specifications. Unfortunately, some keyboards do not meet the specifications and require ~200us. In that case the wait would be better at least 4 scanlines. |
![]() |
![]() |
#3 | |
Registered User
![]() Join Date: Jul 2018
Location: Londonish / UK
Posts: 489
|
Quote:
I'm looking in the Hardware Reference Manual... "A control bit selects either timer mode. In one-shot mode, the timer will count down from the latched value to zero, generate an interrupt, reload the latched value, then stop." "In one-shot mode, a write to timer-high (register 5 for timer A, register 7 for Timer B) will transfer the timer latch to the counter and initiate counting regardless of the start bit." So, I write CIACRAF_RUNMODE to ciaa->ciacra, then the low byte to ciaa->ciatalo then finally the high byte ciaa->ciatahi, then sit back and wait for an exception? Which will be the same level 2 as before, so I'll have to differentiate it? And I need to wait for 200 * 0.709379 = 142 CIA ticks? So 142 in the low byte and 0 in the high byte? EDIT: So, I've tried this, and I'm not getting a new interrupt. The simplest explanation is that I don't know what I'm doing. My exception handler now looks like this: Code:
extern struct CIA * ciaa; if (ciaa->ciaicr & CIAICRF_SP) { UBYTE keycode = ciaa->ciasdr; ciaa->ciacra |= CIACRAF_SPMODE; ciaa->ciacra = CIACRAF_RUNMODE; // one shot ciaa->ciatalo = 142; ciaa->ciatahi = 0; if (DEBUG) KPrintF("In _GameInterruptHandler_processPORTS - keycode = %02lx.\n", (ULONG) keycode); } else { if (DEBUG) KPrintF("In _GameInterruptHandler_processPORTS, new interrupt!\n"); ciaa->ciacra &= ~CIACRAF_SPMODE; } Last edited by deimos; 26 September 2019 at 15:24. |
|
![]() |
![]() |
#4 |
Per aspera ad astra
![]() Join Date: Mar 2017
Location: Crossing the Rubicon
Age: 49
Posts: 2,230
|
A very simplified working skeleton (sorry, asm):
Code:
_setup lea $dff09a,a6 move.w #$7fff,(a6)+ lea $bfed01,a5 clr.b ($e01-$d01,a5) move.b #$8e,($401-$d01,a5) ;~200us in prescalar clr.b ($501-$d01,a5) ; (keyb ack) move.b #$7f,(a5) ; tst.b (a5) ;do not use this line!, see successive messages move.b #%10001011,(a5) ;SP/TB/TA movea.l (_vbr,pc),a1 lea (_irq2,pc),a0 move.l a0,($68,a1) move.w #$7fff,(a6) move.w #$c008,-(a6) rts _irq2 movem.l d0/d1/a0/a1,-(sp) ; in this skeleton only d0/a0 used lea $bfee01,a0 move.b -$100(a0),d0 ; read ICR and clear CIAA IRQ ; bpl.s .i2exit ; there can be external source for IRQ2 (ie CD32) .chkta lsr.w #1,d0 bcc.s .chktb clr.b (a0) ; end ack (SPMODE in), next char .chktb lsr.w #1,d0 bcc.s .chksp ;TB handler nop ; if I also need TB else cut code and lsr.w #3 .chksp lsr.w #2,d0 bcc.b .i2exit move.b -$200(a0),d0 ; read keyboard move.b #$59,(a0) ; start ack (SPMODE out) and Timer A (one shot) not.b d0 ror.b #1,d0 move.b d0,(_rawkey) .i2exit moveq #8,d0 lea $dff09c,a0 move.w d0,(a0) move.w d0,(a0) ; 2CCK delay (propagate IPL from Paula to CPU) movem.l (sp)+,d0/d1/a0/a1 rte Both method to start the one-shot count are valid, I used what is most comfortable for this situation. Last edited by ross; 26 September 2019 at 20:30. |
![]() |
![]() |
#5 |
Registered User
![]() Join Date: Jul 2018
Location: Londonish / UK
Posts: 489
|
|
![]() |
![]() |
#6 |
Registered User
![]() Join Date: Jul 2018
Location: Londonish / UK
Posts: 489
|
I've added comments as I've gone through trying to understand, and posting them below for the benefit of others. Hopefully they're correct.
Code:
_setup lea $dff09a,a6 ;;; a6 points to INTENA move.w #$7fff,(a6)+ ;;; disable all interrupts, a6 now points to INTREQ lea $bfed01,a5 ;;; a5 points to CIAA_ICR (interrupt control register) clr.b ($e01-$d01,a5) ;;; clear BFEE01, CIAA_CRA (control register A) move.b #$8e,($401-$d01,a5) ;;; 142 -> CIAS_TALO ~200us in prescalar clr.b ($501-$d01,a5) ;;; 0 -> CIAS_TAHI (keyb ack) move.b #$7f,(a5) ;;; 0x7F -> CIAA_ICR - disable CIAA interrupts tst.b (a5) ;;; read CIAA_ICR - for why? move.b #%10001011,(a5) ;;; enable CIAA SP/TB/TA movea.l (_vbr,pc),a1 lea (_irq2,pc),a0 move.l a0,($68,a1) move.w #$7fff,(a6) ;;; clear all pending interrupts move.w #$c008,-(a6) ;;; enable CIA interrupts (INTF_SETCLR | INTF_INTEN | INTF_PORTS) rts _irq2 movem.l d0/d1/a0/a1,-(sp) ; in this skeleton only d0/a0 used lea $bfee01,a0 ;;; a0 points to CIAA control register A move.b -$100(a0),d0 ;;; BFED01, CIAA_ICR (interrupt control register) to d0 ; read ICR and clear CIAA IRQ ; bpl.s .i2exit ; there can be external source for IRQ2 (ie CD32) .chkta lsr.w #1,d0 bcc.s .chktb ;;; if CIAA_ICR - TA was not set then skip clr.b (a0) ; end ack (SPMODE in), next char .chktb lsr.w #1,d0 ;;; if CIAA_ICR/TB was not set then skip bcc.s .chksp ;TB handler nop ; if I also need TB else cut code and lsr.w #3 .chksp lsr.w #2,d0 bcc.b .i2exit ;;; if CIAA_ICR/SP was not set then skip move.b -$200(a0),d0 ;;; BFEC01, CIAA_SDR to d0 ; read keyboard move.b #$59,(a0) ;;; SP_MODE | LOAD | RUN_MODE | START -> CIAA_CRA ; start ack (SPMODE out) and Timer A (one shot) not.b d0 ror.b #1,d0 move.b d0,(_rawkey) .i2exit moveq #8,d0 lea $dff09c,a0 ;;; a0 points to INTREQ move.w d0,(a0) ;;; clear PORTS in INTREQ move.w d0,(a0) ;;; twice ? ; 2CCK delay (propagate IPL from Paula to CPU) movem.l (sp)+,d0/d1/a0/a1 rte |
![]() |
![]() |
#7 | ||||
Per aspera ad astra
![]() Join Date: Mar 2017
Location: Crossing the Rubicon
Age: 49
Posts: 2,230
|
Yep, all ok
![]() Answers to your doubts points: Quote:
Quote:
![]() Quote:
And a new IRQ immediately could trigger... With a supplementar internal bus access you introduce a 2CCK delay that guarantees that CPU IPLs is properly updated. Quote:
![]() Cheers. Last edited by ross; 26 September 2019 at 20:32. |
||||
![]() |
![]() |
#8 |
Per aspera ad astra
![]() Join Date: Mar 2017
Location: Crossing the Rubicon
Age: 49
Posts: 2,230
|
I thought carefully about that line that clean the pending CIAA IRQs.
I've to do it to because I need a proper timerA (and others in case) scheduling. But you may have a (incredibly rare) condition of a key press exactly between the deactivation of Paula IRQs and the cleaning of the CIA ones. And this keypress miss an ack.. Unfortunately it is not possible to choose to do a partial clean up as for Paula but only full (reading the ICR). I thought of a simple solution: change tst.b (a5) ;;; read CIAA_ICR to btst #3,(a5) beq.b .continue ...force an ack... .continue If anyone has a better idea let me know ![]() EDIT: yes there is a better way, see my successive message Last edited by ross; 26 September 2019 at 20:33. |
![]() |
![]() |
#9 | |
Natteravn
![]() Join Date: Nov 2009
Location: Herford / Germany
Posts: 1,485
|
Quote:
When I would have to guess, I would say that you didn't enable CIA Timer-A interrupts in the ICR. So no level-2 interrupt is generated by the timer. |
|
![]() |
![]() |
#10 | |
Natteravn
![]() Join Date: Nov 2009
Location: Herford / Germany
Posts: 1,485
|
Quote:
Doesn't it automatically resync after one or two key presses? |
|
![]() |
![]() |
#11 | |
Per aspera ad astra
![]() Join Date: Mar 2017
Location: Crossing the Rubicon
Age: 49
Posts: 2,230
|
Quote:
![]() I've to read HRM if the keyboard resync after a keypress. BUT I hadn't thought of the simplest solution of all and that works for how my IRQ routine is structured. Simply removing that line ![]() Later for an explanation, now I'm in road traffic ![]() |
|
![]() |
![]() |
#12 |
Per aspera ad astra
![]() Join Date: Mar 2017
Location: Crossing the Rubicon
Age: 49
Posts: 2,230
|
Ok explanation time.
If I do not clean CIAA ICR some IRQ can be pending and executed immediately after PORTS setup. If I clean ICR there could theoretically be a lost SP. BUT my IRQ2 routine is like a C switch casestatement without breakbetween cases, so I have a fixed consecutive flux for the tests: TA->TB->TOD->SP. Assuming the worst case of all I could have all the previous interrupts pending. But since the TA is what I control from the inside, and my first check, at most I do an innocuous CRA cleaning operation (by setting the SPMODE values to default) and in the same routine I'll do a (right) restart for TA and a SPMODE change. If I've only pending SP not problem arise, I'll do a standard ACK, for a key pressed during the init phase. If I've TB/TOD pending I'm usually prepared for a "random" activation (because I use these timer for some controlled/semaphored checks), anyway these timer are not keyb handler related. So definitive solution: remove tst.b (a5) line, I edit my previous message containing code. |
![]() |
![]() |
#13 |
WinUAE developer
Join Date: Aug 2001
Location: Hämeenlinna/Finland
Age: 44
Posts: 23,348
|
In my opinion using interrupts in handshake routine will sooner or later cause very unexpected random problems (because reading interrupt status clears all interrupts) when you implement new features that use CIA and forgot existence of keyboard handler that also uses interrupts.
Keyboard starts sending "fake" key releases periodically if it does not get handshake quickly enough. EDIT: Correction: it starts sending one bits periodically so that whatever the final key code is, it is considered key release. Last edited by Toni Wilen; 26 September 2019 at 20:50. |
![]() |
![]() |
#14 | ||
Per aspera ad astra
![]() Join Date: Mar 2017
Location: Crossing the Rubicon
Age: 49
Posts: 2,230
|
Quote:
I never encountered any problem doing a unique routine and leaving it always active. Quote:
Thanks ![]() |
||
![]() |
![]() |
#15 | |
Per aspera ad astra
![]() Join Date: Mar 2017
Location: Crossing the Rubicon
Age: 49
Posts: 2,230
|
Quote:
or every keyboard does what it wants according to the code used by the internal micro? |
|
![]() |
![]() |
#16 |
WinUAE developer
Join Date: Aug 2001
Location: Hämeenlinna/Finland
Age: 44
Posts: 23,348
|
Keyboard microcontroller. CIA serial port generates irq when all 8 bits have been received.
|
![]() |
![]() |
#17 |
Registered User
![]() Join Date: Jul 2018
Location: Londonish / UK
Posts: 489
|
|
![]() |
![]() |
#18 | |
Per aspera ad astra
![]() Join Date: Mar 2017
Location: Crossing the Rubicon
Age: 49
Posts: 2,230
|
Quote:
I simply use CIAB for these. Surely Tony refers to those stupid code that use ICR and not CRA/B to check the underflow of a timer. Or code that reuse timers also used by IRQ code. |
|
![]() |
Currently Active Users Viewing This Thread: 1 (0 members and 1 guests) | |
Thread Tools | |
![]() |
||||
Thread | Thread Starter | Forum | Replies | Last Post |
Safely read 16 bit CIA timer value | matburton | Coders. Asm / Hardware | 5 | 27 June 2019 16:09 |
Timing code using a CIA timer | MickGyver | Coders. Blitz Basic | 12 | 10 March 2019 16:50 |
example of a CIA timer interrupt in assembler using cia.resource | Apollo | Coders. Asm / Hardware | 3 | 05 July 2013 09:40 |
[WinUAE/A500] CIA A - Timer A INT2 problem | leoha | Coders. Asm / Hardware | 2 | 22 October 2012 11:18 |
CIA timer interrupt handler called twice during mod playback | absence | Coders. General | 5 | 16 March 2009 19:55 |
|
|