User Tools

Site Tools


base:how_real_programmers_acknowledge_interrupts

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

base:how_real_programmers_acknowledge_interrupts [2015-04-17 04:32] (current)
Line 1: Line 1:
 +====== How Real Programmers Acknowledge Interrupts ======
 +
 +\\
 +\\
 +\\
 +\\
 +\\
 +\\
 +\\
 +
 +===== With RMW instructions =====
 +
 +<​code>​
 +
 +        ; beginning of combined raster/​timer interrupt routine
 +        LSR $D019       ; clear VIC interrupts, read raster interrupt flag to C
 +        BCS raster ​     ; jump if VIC caused an interrupt
 +        ...             ; timer interrupt routine
 +
 +        Operational diagram of LSR $D019:
 +
 +          #  data  address ​ R/W
 +         --- ----  ------- ​ ---  ---------------------------------
 +          1   ​4E ​    ​PC ​     R   fetch opcode
 +          2   ​19 ​   PC+1     ​R ​  fetch address low
 +          3   ​D0 ​   PC+2     ​R ​  fetch address high
 +          4   ​xx ​   $D019    R   read memory
 +          5   ​xx ​   $D019    W   write the value back, rotate right
 +          6  xx/2   ​$D019 ​   W   write the new value back
 +
 +        The 5th cycle acknowledges the interrupt by writing the same
 +        value back. If only raster interrupts are used, the 6th cycle
 +        has no effect on the VIC. (It might acknowledge also some
 +        other interrupts.)
 +</​code>​
 +
 +===== With indexed addressing =====
 +
 +<​code>​
 +        ; acknowledge interrupts to both CIAs
 +        LDX #$10
 +        LDA $DCFD,X
 +
 +        Operational diagram of LDA $DCFD,X:
 +
 +          #  data  address ​ R/W  description
 +         --- ----  ------- ​ ---  ---------------------------------
 +          1   ​BD ​    ​PC ​     R   fetch opcode
 +          2   ​FD ​   PC+1     ​R ​  fetch address low
 +          3   ​DC ​   PC+2     ​R ​  fetch address high, add X to address low
 +          4   ​xx ​   $DC0D    R   read from address, fix high byte of address
 +          5   ​yy ​   $DD0D    R   read from right address
 +
 +
 +        ; acknowledge interrupts to CIA 2
 +        LDX #$10
 +        STA $DDFD,X
 +
 +        Operational diagram of STA $DDFD,X:
 +
 +          #  data  address ​ R/W  description
 +         --- ----  ------- ​ ---  ---------------------------------
 +          1   ​9D ​    ​PC ​     R   fetch opcode
 +          2   ​FD ​   PC+1     ​R ​  fetch address low
 +          3   ​DC ​   PC+2     ​R ​  fetch address high, add X to address low
 +          4   ​xx ​   $DD0D    R   read from address, fix high byte of address
 +          5   ​ac ​   $DE0D    W   write to right address
 +</​code>​
 +
 +===== With branch instructions =====
 +
 +<​code>​
 +        ; acknowledge interrupts to CIA 2
 +                LDA #$00  ; clear N flag
 +                JMP $DD0A
 +        DD0A    BPL $DC9D ; branch
 +        DC9D    BRK       ; return
 +
 +        You need the following preparations to initialize the CIA registers:
 +
 +                LDA #$91  ; argument of BPL
 +                STA $DD0B
 +                LDA #$10  ; BPL
 +                STA $DD0A
 +                STA $DD08 ; load the ToD values from the latches
 +                LDA $DD0B ; freeze the ToD display
 +                LDA #$7F
 +                STA $DC0D ; assure that $DC0D is $00
 +
 +        Operational diagram of BPL $DC9D:
 +
 +          #  data  address ​ R/W  description
 +         --- ----  ------- ​ ---  ---------------------------------
 +          1   ​10 ​   $DD0A    R   fetch opcode
 +          2   ​91 ​   $DD0B    R   fetch argument
 +          3   ​xx ​   $DD0C    R   fetch opcode, add argument to PCL
 +          4   ​yy ​   $DD9D    R   fetch opcode, fix PCH
 +        ( 5   ​00 ​   $DC9D    R   fetch opcode )
 +</​code>​
 +
 +<​code>​
 +        ; acknowledge interrupts to CIA 1
 +                LSR       ; clear N flag
 +                JMP $DCFA
 +        DCFA    BPL $DD0D
 +        DD0D    BRK
 +
 +        ; Again you need to set the ToD registers of CIA 1 and the
 +        ; Interrupt Control Register of CIA 2 first.
 +
 +        Operational diagram of BPL $DD0D:
 +
 +          #  data  address ​ R/W  description
 +         --- ----  ------- ​ ---  ---------------------------------
 +          1   ​10 ​   $DCFA    R   fetch opcode
 +          2   ​11 ​   $DCFB    R   fetch argument
 +          3   ​xx ​   $DCFC    R   fetch opcode, add argument to PCL
 +          4   ​yy ​   $DC0D    R   fetch opcode, fix PCH
 +        ( 5   ​00 ​   $DD0D    R   fetch opcode )
 +</​code>​
 +
 +<​code>​
 +        ; acknowledge interrupts to CIA 2 automagically
 +                ; preparations
 +                LDA #$7F
 +                STA $DD0D       ; disable all interrupt sources of CIA2
 +                LDA $DD0E
 +                AND #$BE        ; ensure that $DD0C remains constant
 +                STA $DD0E       ; and stop the timer
 +                LDA #$FD
 +                STA $DD0C       ; parameter of BPL
 +                LDA #$10
 +                STA $DD0B       ; BPL
 +                LDA #$40
 +                STA $DD0A       ; RTI/​parameter of LSR
 +                LDA #$46
 +                STA $DD09       ; LSR
 +                STA $DD08       ; load the ToD values from the latches
 +                LDA $DD0B       ; freeze the ToD display
 +                LDA #$09
 +                STA $0318
 +                LDA #$DD
 +                STA $0319       ; change NMI vector to $DD09
 +                LDA #$FF        ; Try changing this instruction'​s operand
 +                STA $DD05       ; (see comment below).
 +                LDA #$FF
 +                STA $DD04       ; set interrupt frequency to 1/65536 cycles
 +                LDA $DD0E
 +                AND #$80
 +                ORA #$11
 +                LDX #$81
 +                STX $DD0D       ; enable timer interrupt
 +                STA $DD0E       ; start timer
 +
 +                LDA #$00        ; To see that the interrupts really occur,
 +                STA $D011       ; use something like this and see how
 +        LOOP    DEC $D020       ; changing the byte loaded to $DD05 from
 +                BNE LOOP        ; #$FF to #$0F changes the image.
 +
 +        When an NMI occurs, the processor jumps to Kernal code, which jumps to
 +        ($0318), which points to the following routine:
 +
 +        DD09    LSR $40         ; clear N flag
 +                BPL $DD0A       ; Note: $DD0A contains RTI.
 +
 +        Operational diagram of BPL $DD0A:
 +
 +          #  data  address ​ R/W  description
 +         --- ----  ------- ​ ---  ---------------------------------
 +          1   ​10 ​   $DD0B    R   fetch opcode
 +          2   ​11 ​   $DD0C    R   fetch argument
 +          3   ​xx ​   $DD0D    R   fetch opcode, add argument to PCL
 +          4   ​40 ​   $DD0A    R   fetch opcode, (fix PCH)
 +</​code>​
 +
 +===== With RTI =====
 +
 +<​code>​
 +        ; the fastest possible interrupt handler in the 6500 family
 +                ; preparations
 +                SEI
 +                LDA $01         ; disable ROM and enable I/O
 +                AND #$FD
 +                ORA #$05
 +                STA $01
 +                LDA #$7F
 +                STA $DD0D       ; disable CIA 2's all interrupt sources
 +                LDA $DD0E
 +                AND #$BE        ; ensure that $DD0C remains constant
 +                STA $DD0E       ; and stop the timer
 +                LDA #$40
 +                STA $DD0C       ; store RTI to $DD0C
 +                LDA #$0C
 +                STA $FFFA
 +                LDA #$DD
 +                STA $FFFB       ; change NMI vector to $DD0C
 +                LDA #$FF        ; Try changing this instruction'​s operand
 +                STA $DD05       ; (see comment below).
 +                LDA #$FF
 +                STA $DD04       ; set interrupt frequency to 1/65536 cycles
 +                LDA $DD0E
 +                AND #$80
 +                ORA #$11
 +                LDX #$81
 +                STX $DD0D       ; enable timer interrupt
 +                STA $DD0E       ; start timer
 +
 +                LDA #$00        ; To see that the interrupts really occur,
 +                STA $D011       ; use something like this and see how
 +        LOOP    DEC $D020       ; changing the byte loaded to $DD05 from
 +                BNE LOOP        ; #$FF to #$0F changes the image.
 +
 +        When an NMI occurs, the processor jumps to Kernal code, which
 +        jumps to ($0318), which points to the following routine:
 +
 +        DD0C    RTI
 +
 +        How on earth can this clear the interrupts? Remember, the
 +        processor always fetches two successive bytes for each
 +        instruction.
 +
 +        A little more practical version of this is redirecting the NMI
 +        (or IRQ) to your own routine, whose last instruction is JMP
 +        $DD0C or JMP $DC0C. ​ If you want to confuse more, change the 0
 +        in the address to a hexadecimal digit different from the one
 +        you used when writing the RTI.
 +
 +        Or you can combine the latter two methods:
 +
 +        DD09    LSR $xx  ; xx is any appropriate BCD value 00-59.
 +                BPL $DCFC
 +        DCFC    RTI
 +
 +        This example acknowledges interrupts to both CIAs.
 +</​code>​
 +
 +If you want to confuse the examiners of your code, you can use any of these techniques. Although these examples use no undefined opcodes, they do not necessarily run correctly on CMOS processors. However, the RTI example should run on 65C02 and 65C816, and the latter branch instruction example might work as well.
 +
 +The RMW instruction method has been used in some demos, others were developed by Marko Mäkelä. His favourite is the automagical RTI method, although it does not have any practical applications,​ except for some time dependent data decryption routines for very complicated copy protections.
  
base/how_real_programmers_acknowledge_interrupts.txt · Last modified: 2015-04-17 04:32 (external edit)