[A83] about interrupts and shutdown (83-)


[Prev][Next][Index][Thread]

[A83] about interrupts and shutdown (83-)




I just checked the 82 and 85 docs about the Z80's ports again and what I
said in previous mails about port 03h seems to be a misinterpretation.
Things are a bit more complex than I thought. I think that what follows is a
bit more correct even though VTI doesn't seem to fully support it.


There are two sorts of interrupts on the 83 series: timer interrupts and
on-key interrupts. The timer interrupt has a frequency of about 130Hz, if I
can trust what others have said on this list. On-key interrupts occur when
the on-key gets pressed. About what happens when both occur at the same
time, I don't know. I assume the on-key interrupt has a higher priority and
thus the on-key interrupt is accepted.

If you read port 3 from within an interrupt routine, bits 0 (on-key) and 2
(timer) indicate what kind of interrupt happened. If bit 0 is set then an
on-key interrupt happened and if bit 2 is set, it was a timer interrupt.
However, it is possible to turn off the ability to check for a specific
interrupt or in other words, to mask those bits out. The following table
indicates the values that can be outed to port 3 to turn on/off masking.

Bit 2   1: do not mask timer interrupts
        0: mask timer interrupts
Bit 0   1: do not mask on-key interrupts
        0: mask on-key interrupts

So, when an interrupt is accepted, the values of bit 0 and 2 of port 3 are
determined by what sort of interrupt happened ANDed with the value
previously outputted to those bits. This means that if a timer interrupt
happens, but timer interrupts have been set to 'mask out', then the values
of bit 0 and 2 will both be 0.

Before you quit an interrupt it is required to reset port 3. This is done by
outputting values to bit 0 and 2 which will be ANDed with the current values
of those bits, indicating the current sort of interrupt. If, lets say, an
on-key interrupt is accepted and on-key interrupts are not masked out and
instead of outputting 0 you output 1 to bit 0, then the calc seems to go in
a continious loop of on-key interrupts until you set the bit to zero.
Outputting 1 to bit 0 when it's value is 0 (=interrupt masked out or timer
interrupt occured) doesn't give problems so that's why I think the values
are ANDed.

So far bits 0 and 2 with bit 1 and 3 still left. The remaining bits (4-7) of
port 3 aren't used.

Bit 1 seems to be used by the calc to remember whether the calc is currently
on (1) or off (0). It seems you can set/clear it as you want without any
other effects on hardware (not sure about that though). It also doesn't seem
to be affected by the first output to port 3 previously mentioned.

Bit 3 has two functions. Reading its value gives you the current state of
the on-key. 0 indicates it's currently hold down, 1 means it isn't. Writing
to this bit on the other hand turns on (1) / off (0) LCD power. I don't
think the LCD driver is affected by this, which means that you can turn the
LCD off and still write to the driver. (Turning the driver on or off is done
by outputting 3 or 2 to port 10h.) The state of bit 3 also doesn't seem to
be affected by the first output to port 3 previously mentioned. However, the
calc always sets this bit when outputting that value.

After resetting port 3 you can output any other value to change the
interrupt masking or to turn the LCD on/off or change the status of bit 1.
This counts for outside an interrupt routine too where port 3 is always
reset. Reading port 3 at the moment it is reset will only give you the
on-key status (bit 3).


As example I'll explain how to handle a calculator shutdown with minimum
power consumed from within both a normal program and an interrupt routine.
I haven't tested these, so it might be I overlooked something.

This routine will wait for a keypress. It has APD and pressing [on] turns
the calc on/off.

getKey:
    res    4,(iy+9)        ; reset on-key interrupt flag
    ld     bc,1A00h        ; init apd counter
getKeyLoop:
    call   4014h           ; call _getcsc
    or     a               ; return key press
    ret    nz              ;
    bit    4,(iy+9)        ; check if on-key interrupt has happened
    jr     nz,turnOffCalc  ;
    halt                   ; less power consuming
    dec    bc              ; decrement apd counter
    ld     a,b             ; and check if it reached 0
    or     c               ;
    jr     nz,getKeyLoop   ;
turnOffCalc:
    res    4,(iy+9)        ; (*) reset on-key interrupt flag
    di                     ; (*)
    inc    a               ; a = 1
    out    (3),a           ; turn lcd off, mask timer interrupts,
                           ; reset calc status bit and don't
                           ; mask on-key interrupts
    inc    a               ; (*) a = 2
    out    (16),a          ; (*) turn lcd driver off
    ei                     ; (*)
calcTurnedOff:             ; (*)
    halt                   ; wait for next interrupt in low power
    bit    4,(iy+9)        ; (*) was it an on-key interrupt?
    jr     z,calcTurnedOff ; (*) no, then keep waiting
    inc    a               ; (*) a = 3
    out    (16),a          ; (*) turn lcd driver on
    jr     getKey          ;

Lines marked with a (*) can all be removed to reduce the size, but then the
routine is more power consuming. Instead of waiting until the user presses
on, the routine will immediately continue and cycle through the getKeyLoop.
However, it won't detect keypresses, because the interrupt handler only
scans the keyboard when bit 1 of port 3 is set and that bit has been
cleared.

The following example is an interrupt routine which handles apd and 2nd+on.

intStart:
    ex     af,af'          ;
    exx                    ;
    in     a,(3)           ; read port 3
    ld     b,a             ; save in B
    bit    1,b             ; if bit 1 is clear
    jp     z,003Ah         ; go to standard interrupt handler
    ld     a,($800A)       ; apd?
    cp     $11             ;
    jr     c,turnOffCalc   ;
    bit    0,b             ; if bit 0 is clear (no on-key int)
    jp     z,003Ah         ; go to standard interrupt handler
    bit    3,(iy+18)       ; check for 2nd
    jp     z,003Ah         ;
    res    3,(iy+18)       ;
turnOffCalc:
    ld a,$74               ; reset apd counter
    ld ($800A),a           ;
    ld     a,8             ; reset port 3
    out    (3),a           ;
    ld     a,2             ; turn lcd driver off
    out    (16),a          ;
    dec    a               ; a = 1
    out    (3),a           ; turn lcd off, mask timer interrupts,
                           ; reset calc status bit and don't
                           ; mask on-key interrupts
    ex     af,af'          ; enable interrupts again
    exx                    ;
    ei                     ;
calcTurnedOff:
    halt                   ; wait for next interrupt in low power
    bit    4,(iy+9)        ; was it an on-key interrupt?
    jr     z,calcTurnedOff ; no, then keep waiting
    res    4,(iy+9)        ; reset on-key interrupt flag again

[..here you can do what you want before returning to the TIOS..]

    ret                    ; return to TIOS
intEnd: