Close

Shrinking the ISR

A project log for EXPS/2

External PS/2 keyboard for Gradiente Expert MSX computer

danjovicdanjovic 04/06/2021 at 02:370 Comments

it was necessary to shrink the pin change irq to fit within the timing necessary to attend the Z80.

Doing the Math I should be spending 1us to output the code, counting the IRQ latency, but I have seen some figures of up to 3.25us which is barely enough.

It should be possible to increase this margin by changing the crystal to 20MHz, but I am testing with an arduino board (at 16MHz).

The code uses some tricks to save clock cycles like saving the registers r26 and r27 on general purpose I/O registers available in ATMega328, and using the __zero_reg__ to store the flags

volatile uint8_t zero = 0;
ISR (PCINT1_vect, ISR_NAKED) {
  asm volatile (                     // 7 instrucoes de latencia até aqui
    "in __zero_reg__,__SREG__ \n\t"  // 1 Salva registrador de Status
    "out %[_GPIOR1],r26\n\t"         // 1 salva registro em 1 ciclo
    "out %[_GPIOR2],r27\n\t"         // 1 salva registro em 1 ciclo
    
    "ldi r27,hi8(Keymap)\n\t"        // 1 Ponteiro X = endereço de Keymap  
    "in r26,%[_PINC] \n\t"           // 1
    "subi r26, lo8(-(Keymap)) \n\t"  // 1 
    "ld 26,X \n\t"                   // 2 lê coluna correspondente do mapa de teclas
    "out %[_DDRD] ,r26 \n\t"         // 1 escreve na porta B da PPI
                                     // até aqui 16 instruções (1us @16Mhz)
    "in r26, %[_GPIOR1] \n\t"
    "in r27, %[_GPIOR2] \n\t"

    "sbi %[_PCIFR],1 \n\t"           // reset interrupt bit

    "out __SREG__,__zero_reg__ \n\t" // restaura registrador de Status
    "lds __zero_reg__, zero \n\t"
    "reti \n\t"

    ::[_PINC] "I" (_SFR_IO_ADDR(PINC)  ),
    [_DDRD]   "I" (_SFR_IO_ADDR(DDRD)  ),
    [_PORTB]  "I" (_SFR_IO_ADDR(PORTB) ),    
    [_GPIOR1] "I" (_SFR_IO_ADDR(GPIOR1)),
    [_GPIOR2] "I" (_SFR_IO_ADDR(GPIOR2)), 
    [_PCIFR]  "I" (_SFR_IO_ADDR(PCIFR) )
    );

}

Discussions