Jump to content
IGNORED

IRQ Handling


42bs

Recommended Posts

Hi,

diving into the 65C02 docs, I found out "CLD" is not needed in the interrupt handler.

After all these year, I did also re-work the interrupt code to save some cycles.

 

; Timer 7 => 3+2+4+3+2+12        = 26
; Timer 6 => 3+2+4+2+3+12        = 28
; UART    => 3+2+4+3+6           = 18
; pre = 19
; Timer 5 => 19+4*(2+2)+12       = 47
; Timer 3 => 19+3*(2+2)+(2+3)+12 = 48
; Timer 2 => 19+2*(2+2)+(2+3)+12 = 44
; Timer 1 => 19+1*(2+2)+(2+3)+12 = 40
; Timer 0 => 19+(2+3)+12         = 36

; Timer 0 => HBL
; Timer 2 => VBL

irq::
	pha		; 3
	lda	#$10	; 2
	bit	$fd81	; 4
	bne	_4	; 2,3
	bmi	_7	; 2,3
	bvs	_6	; 2,3
	lda	$fd81	; 4
 IFD BRKuser
	beq	dummy_irq
 ENDIF
	lsr		; 2
	bcs	_0	; 2,3
	lsr		; 2
	bcs	_1	; 2,3
	lsr		; 2
	bcs	_2	; 2,3
	lsr		; 2
	bcs	_3	; 2,3
_5:
	lda	#$20	; 2
	sta	$fd80	; 4
	jmp	(irq_vecs+5*2) ;6
_4:
	jmp	(irq_vecs+4*2) ; 6
_3:
	lda	#$08
	sta	$fd80
	jmp	(irq_vecs+3*2)
_2:
	lda	#$04
	sta	$fd80
	jmp	(irq_vecs+2*2)
_1:
	lda	#$2
	sta	$fd80
	jmp	(irq_vecs+2*2)
_0:
	lda	#$1
	sta	$fd80
	jmp	(irq_vecs+0*2)
_6:
	lda	#$40
	sta	$fd80
	jmp	(irq_vecs+6*2)
_7:
	lda	#$80
	sta	$fd80
	jmp	(irq_vecs+7*2)

dummy_irq
	END_IRQ

Compared to the old one, the worst case cycles are heavily reduced:

;---------------
;- Interrupt-Handler
;---------------
; pre = 17 , post = 14
; Timer 0 => 17+4+2+2+14         = 39
; Timer 1 => 17+4+2+3+2+2+14     = 44
; Timer 2 => 17+4+2+2*(3+2)+2+14 = 49
; Timer 3 => 17+4+2+3*(3+2)+2+14 = 54
; UART    =>                       22
; Timer 5 => 17+4+2+5*(3+2)+2+14 = 64
; Timer 6 => 17+4+2+6*(3+2)+2+14 = 69
; Timer 7 => 17+4+2+7*(3+2)+2+14 = 74

 

Edited by 42bs
  • Like 1
Link to comment
Share on other sites

22 minutes ago, karri said:

The BLL IRQ handling is more efficient than cc65. I would like to use this technique instead of having the comparison in every IRQ handler.

I thought the cc65 irq code was derived from the original BLL code. But yes, this code is ugly.
Where is "callirq"?

Link to comment
Share on other sites

1 hour ago, 42bs said:

I thought the cc65 irq code was derived from the original BLL code. But yes, this code is ugly.
Where is "callirq"?

At cc65/libsrc/runtime/callirq.s

They had some great idea to hide the interrupts from confusing programmers. So now we need to use .interruptor everywhere.

Fortunately there is a possibility to add higher levels to interrupts.

 

Example:

 

        .interruptor    _HandyMusic_Main
        .interruptor    _HandyMusic_PCMMain,15

Normal HandyMusic just ticks on while PSC samples are served at highest priority. This was a must to get the correct pitch for the samples.

 

HandyMusic_PCMMain:
        lda     INTSET
        and     #TIMER3_INTERRUPT
        beq     @L0
        jsr     PCMSample_IRQ
        sec
        rts
@L0:
        clc
        rts

As you can see setting CARRY bit tells the IRQ handler that there are no more IRQ handlers to be called for this interrupt. The CLC is the normal flag that lets you hook more handlers to this IRQ.

 

A typical example would be to use VBL for the screen and system time.

 

        .interruptor    update_clock



update_clock:
        lda     INTSET
        and     #%00000100
        beq     @NotVBlank      ; Not vertical-blank interrupt

        inc     clock_count
        bne     @L1
        inc     clock_count+1
        bne     @L1
        inc     clock_count+2
@L1:    ;clc                    ; General interrupt was not reset
@NotVBlank:
        rts

 

Link to comment
Share on other sites

5 minutes ago, karri said:

At cc65/libsrc/runtime/callirq.s

They had some great idea to hide the interrupts from confusing programmers. So now we need to use .interruptor everywhere.

Fortunately there is a possibility to add higher levels to interrupts.

 

Example:

 


        .interruptor    _HandyMusic_Main
        .interruptor    _HandyMusic_PCMMain,15

Normal HandyMusic just ticks on while PSC samples are served at highest priority. This was a must to get the correct pitch for the samples.

 


HandyMusic_PCMMain:
        lda     INTSET
        and     #TIMER3_INTERRUPT
        beq     @L0
        jsr     PCMSample_IRQ
        sec
        rts
@L0:
        clc
        rts

 

Ok, w/o changing too much:

 

irq.s:

pha

phx

phy

lda INTSET

        sta INTSET_ZP

     jsr callirq

ply

plx

pla

rti

 

 

...

HandyMusic_PCMMain:

   bbs3  INTSET_ZP, sample_irq

   clc

   rts

sample_irq:

  lda #$8

sta INTCLR

   jsr PCMSample_IRQ

   sec

  rts

  • Thanks 1
Link to comment
Share on other sites

I had no idea that there is a bbs3 instruction available.

 

Oliver's cc65 has already changed a lot by removing IRQ's from all drivers and changing the joypad syntax. So speeding up the IRQ's a little would not  hurt further cc65 use for me...

 

It would be nice to change the CPU directive to use all the special op-codes available for Lynx.

 

/* CPUs */
typedef enum {
    CPU_UNKNOWN = -1,           /* Not specified or invalid target */
    CPU_NONE,                   /* No CPU - for assembler */
    CPU_6502,
    CPU_6502X,                  /* "Extended", that is: with illegal opcodes */
    CPU_65SC02,
    CPU_65C02,
    CPU_65816,
    CPU_SUNPLUS,            /* Not in the freeware version - sorry */
    CPU_SWEET16,
    CPU_HUC6280,                /* Used in PC engine */
    CPU_M740,                   /* Mitsubishi 740 series MCUs */
    CPU_COUNT                      /* Number of different CPUs */
} cpu_t;

Perhaps creating a new CPU type 65SC02X /* All Lynx op-codes plus usable illegal op-codes */

 

Then we could create cute mnemonics for SKIP1 opcode and who knows what else.

 

The da65 disassembler seems to give mnemonics to 256 different opcodes when you choose 6502X as the CPU type.

 

Link to comment
Share on other sites

2 hours ago, karri said:

At cc65/libsrc/runtime/callirq.s

 

:( grep did not find it?!!

 

Anyway, from what I see, there is no problem to use my routine and skip callirq.s completely.

Of course, one has to pay attention that currently only A is saved.

If cycle count matters ;-)

 

Edited by 42bs
Link to comment
Share on other sites

2 hours ago, karri said:

 

 

 

A typical example would be to use VBL for the screen and system time.

 


        .interruptor    update_clock



update_clock:
        lda     INTSET
        and     #%00000100
        beq     @NotVBlank      ; Not vertical-blank interrupt

        inc     clock_count
        bne     @L1
        inc     clock_count+1
        bne     @L1
        inc     clock_count+2
@L1:    ;clc                    ; General interrupt was not reset
@NotVBlank:
        rts

        .interruptor    update_clock
update_clock:
        lda     INTSET
        and     #%00000100
        beq     @NotVBlank      ; Not vertical-blank interrupt

        inc     clock_count
        beq	@l1
        rts
@l1:
        inc     clock_count+1
        beq	@l2
        rts
@l2        
        inc     clock_count+2
    ;clc                    ; General interrupt was not reset
@NotVBlank:
        rts

Little optimization: It is more likely that Z is not set (only once all 256 VBLs) ;-)

 

Link to comment
Share on other sites

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...