English Amiga Board


Go Back   English Amiga Board > Coders > Coders. General

 
 
Thread Tools
Old 26 September 2019, 13:24   #1
deimos
Registered User

 
Join Date: Jul 2018
Location: Londonish / UK
Posts: 437
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
deimos is offline  
Old 26 September 2019, 14:08   #2
ross
Per aspera ad astra

ross's Avatar
 
Join Date: Mar 2017
Location: Crossing the Rubicon
Age: 49
Posts: 2,166
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.
ross is offline  
Old 26 September 2019, 14:30   #3
deimos
Registered User

 
Join Date: Jul 2018
Location: Londonish / UK
Posts: 437
Quote:
Originally Posted by ross View Post
I launch a time-out with a timer in one-shot mode and close the handshake in next IRQ.
I was wondering if something like this would be possible.

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.
deimos is offline  
Old 26 September 2019, 15:57   #4
ross
Per aspera ad astra

ross's Avatar
 
Join Date: Mar 2017
Location: Crossing the Rubicon
Age: 49
Posts: 2,166
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
EDIT: I hope that the various base displacement is not confusing you, tell me if something is not clear.
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.
ross is offline  
Old 26 September 2019, 16:29   #5
deimos
Registered User

 
Join Date: Jul 2018
Location: Londonish / UK
Posts: 437
Quote:
Originally Posted by ross View Post
EDIT: I hope that the various base displacement is not confusing you, tell me if something is not clear.
I can understand each individual line, but I'm not getting the overall picture yet. I'll keep working on it, but the maths itself is giving me a headache.
deimos is offline  
Old 26 September 2019, 17:18   #6
deimos
Registered User

 
Join Date: Jul 2018
Location: Londonish / UK
Posts: 437
Quote:
Originally Posted by ross View Post
A very simplified working skeleton (sorry, asm):
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
I'm definitely not doing everything you are, so I'm now going to try and change my C code to match your assembly.
deimos is offline  
Old 26 September 2019, 17:51   #7
ross
Per aspera ad astra

ross's Avatar
 
Join Date: Mar 2017
Location: Crossing the Rubicon
Age: 49
Posts: 2,166
Yep, all ok

Answers to your doubts points:
Quote:
Originally Posted by deimos View Post
; tst.b (a5) ;;; read CIAA_ICR - for why? [do not use it ]
;;; clear all CIAA pending interrupts
Quote:
;if you do not use TimerB you can:
.chktb
;comment this:
;;; 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
;and
.chksp
lsr.w #3,d0
;;; ...
EDIT: ops, you didn't ask me anything about TimerB

Quote:
Code:
	move.w	d0,(a0)
    ;;; twice ? ; 2CCK delay (propagate IPL from Paula to CPU)
In very fast machines the RTE (so a reset of IPM SR bits) can happen before Paula (rightly cleared through a write to INTREQ) signals propagate to 680xx IPLs.
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:
I'm definitely not doing everything you are, so I'm now going to try and change my C code to match your assembly.


Cheers.

Last edited by ross; 26 September 2019 at 20:32.
ross is offline  
Old 26 September 2019, 19:25   #8
ross
Per aspera ad astra

ross's Avatar
 
Join Date: Mar 2017
Location: Crossing the Rubicon
Age: 49
Posts: 2,166
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.
ross is offline  
Old 26 September 2019, 19:31   #9
phx
Natteravn

phx's Avatar
 
Join Date: Nov 2009
Location: Herford / Germany
Posts: 1,479
Quote:
Originally Posted by deimos View Post
Which will be the same level 2 as before, so I'll have to differentiate it?
Yes. Read the CIA's ICR register. TA will be set for a Timer-A interrupt. SP will be set for a Serial Port (keyboard) interrupt. Note, that reading ICR resets all bits, so you have remember them.

Quote:
Originally Posted by deimos View Post
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.
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.
phx is offline  
Old 26 September 2019, 19:43   #10
phx
Natteravn

phx's Avatar
 
Join Date: Nov 2009
Location: Herford / Germany
Posts: 1,479
Quote:
Originally Posted by ross View Post
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..
Hmm. Is this really a problem in real life? When the system or a low-level trackloader-game is booting, there is often no keyboard interrupt handler assigned, still I never experienced any problem if you are hammering on the keyboard during that phase.

Doesn't it automatically resync after one or two key presses?
phx is offline  
Old 26 September 2019, 19:53   #11
ross
Per aspera ad astra

ross's Avatar
 
Join Date: Mar 2017
Location: Crossing the Rubicon
Age: 49
Posts: 2,166
Quote:
Originally Posted by phx View Post
Hmm. Is this really a problem in real life? When the system or a low-level trackloader-game is booting, there is often no keyboard interrupt handler assigned, still I never experienced any problem if you are hammering on the keyboard during that phase.

Doesn't it automatically resync after one or two key presses?
No, actually it's not a problem I never also experienced any problem.
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
ross is offline  
Old 26 September 2019, 20:26   #12
ross
Per aspera ad astra

ross's Avatar
 
Join Date: Mar 2017
Location: Crossing the Rubicon
Age: 49
Posts: 2,166
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
case
statement without
break
between 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.
ross is offline  
Old 26 September 2019, 20:34   #13
Toni Wilen
WinUAE developer
 
Join Date: Aug 2001
Location: Hämeenlinna/Finland
Age: 44
Posts: 23,258
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.
Toni Wilen is online now  
Old 26 September 2019, 20:45   #14
ross
Per aspera ad astra

ross's Avatar
 
Join Date: Mar 2017
Location: Crossing the Rubicon
Age: 49
Posts: 2,166
Quote:
Originally Posted by Toni Wilen View Post
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.
Yep, you have to be very carefully and immediately plan how to use the CIAs.
I never encountered any problem doing a unique routine and leaving it always active.

Quote:
Keyboard starts sending "fake" key releases periodically if it does not get handshake quickly enough.
This explains why even cleaning ICR works just the same.
Thanks
ross is offline  
Old 26 September 2019, 21:06   #15
ross
Per aspera ad astra

ross's Avatar
 
Join Date: Mar 2017
Location: Crossing the Rubicon
Age: 49
Posts: 2,166
Quote:
Originally Posted by Toni Wilen View Post
EDIT: Correction: it starts sending one bits periodically so that whatever the final key code is, it is considered key release.
There is a standard/definite time before these one bits are forming a full key (release) raising SP for an IRQ,
or every keyboard does what it wants according to the code used by the internal micro?
ross is offline  
Old 26 September 2019, 21:49   #16
Toni Wilen
WinUAE developer
 
Join Date: Aug 2001
Location: Hämeenlinna/Finland
Age: 44
Posts: 23,258
Quote:
Originally Posted by ross View Post
There is a standard/definite time before these one bits are forming a full key (release) raising SP for an IRQ,
or every keyboard does what it wants according to the code used by the internal micro?
Keyboard microcontroller. CIA serial port generates irq when all 8 bits have been received.
Toni Wilen is online now  
Old 26 September 2019, 23:05   #17
deimos
Registered User

 
Join Date: Jul 2018
Location: Londonish / UK
Posts: 437
Quote:
Originally Posted by Toni Wilen View Post
when you implement new features that use CIA and forgot existence of keyboard handler that also uses interrupts.
Dumb question time, what find of features might fall into this category? What other uses for CIA stuff would, say, a typical game have?
deimos is offline  
Old 26 September 2019, 23:25   #18
ross
Per aspera ad astra

ross's Avatar
 
Join Date: Mar 2017
Location: Crossing the Rubicon
Age: 49
Posts: 2,166
Quote:
Originally Posted by deimos View Post
Dumb question time, what find of features might fall into this category? What other uses for CIA stuff would, say, a typical game have?
Trackloader, all timing related events (not VBI synced), too many to list.
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.
ross is offline  
 


Currently Active Users Viewing This Thread: 1 (0 members and 1 guests)
 
Thread Tools

Similar Threads
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

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT +2. The time now is 23:01.


Powered by vBulletin® Version 3.8.11
Copyright ©2000 - 2019, vBulletin Solutions Inc.
Page generated in 0.09307 seconds with 15 queries