OSBYTE 17; IRQ entry point; BRK handler; IRQ1, IRQ2 handlers; Display string - 681 bytes (4.1%)


§1. IRQ entry point.

 On Entry:
       The stack contains status register (flags); high and low bytes of previous program
       counter.

 IRQ and BRK causes execution here. If it was a BRK instruction we branch (see .brkRoutine)
 otherwise we call (indirectly via IRQ1V) the default IRQ1 handler (see .irq1Handler)
.irqEntryPoint = $dc1c
    STA .interruptAccumulator                           save A
    PLA                                                 read flags
    PHA                                                 store flags again
    AND #%00010000                                      check BRK flag
    BNE .brkRoutine                                     if (BRK flag set) then branch (to
                                                        BRK handler)
    JMP (.vectorIRQ1V)                                  jump to the IRQ1 vector (by default
                                                        this will jump to .irq1Handler)

§2. brkRoutine.

.brkRoutine = $dc27
    TXA                                                 }
    PHA                                                 } save X on stack
    TSX                                                 get stack pointer
    LDA .stackPage + 3,X                                get program counter low
    CLD                                                 
    SEC                                                 set carry
    SBC #1                                              subtract 1
    STA .brkAddressLow                                  and store
    LDA .stackPage + 4,X                                get high byte
    SBC #0                                              subtract 1 if necessary
    STA .brkAddressHigh                                 and store
    LDA .currentlySelectedROM                           get currently active ROM
    STA .romNumberActiveLastBRK                         and store it
    STX .stackPointerLastBRK                            store stack pointer
    LDX #.romServiceCallBreakInstruction                }
    JSR .osbyte143EntryPoint                            } issue ROM service call 6 (Break)
                                                        } to ROMs so they get a chance to
                                                        } respond.
                                                        at this point .brkAddressLow/High
                                                        points to the byte after the BRK
                                                        instruction.
                                                        ROMS may use BRK for their own
                                                        purposes

    LDX .languageROMNumber                              get current language
    JSR .selectROM                                      and activate it
    PLA                                                 get back original value of X
    TAX                                                 
    LDA .interruptAccumulator                           get back original value of A
    CLI                                                 allow interrupts
    JMP (.vectorBRKV)                                   and JUMP via BRKV (normally into
                                                        current language)

§3. Default BRK handler.

 This is the default BRK handler that is used at boot time. When BASIC or other language
 ROM starts it will set up it's own BRK handler instead.
.brkHandler = $dc54
    LDY #0                                              Y=0 to point to byte after BRK
    JSR .printMessage                                   print message

    LDA .startupMessageSuppressionAndBootOptions        } bit 0 set means: lock up machine
                                                        } if !BOOT errors from DISC (because
                                                        } e.g. no language found)
    ROR                                                 } rotate into carry
-
    BCS -                                               if (carry set) then branch (hang the
                                                        machine!)

    JSR .OSNEWL                                         print two newlines
    JSR .OSNEWL                                         
    JMP .setTapeFSAndFindLanguageROM                    set tape filing system before
                                                        entering current language

§4. rs423Handler.

.rs423Handler = $dc68
    SEC                                                 set carry
    ROR .rs423ReadyFlag                                 set RS-423 ready
    BIT .rs423ControlRegisterCopy                       check bit 7 of current ACIA control
                                                        register (aka 'CR7' - ACIA
                                                        interrupts enabled flag)
    BPL .rs423SetRequestToSendInactive                  if (ACIA interrupts disabled) then
                                                        branch
    JSR .getRS423InputBufferFreeBytes                   call subroutine to check if serial
                                                        buffer is full
    LDX #0                                              set X to the value (zero) which will
                                                        clear bit 5 and 6 of ACIA control
                                                        register
                                                        (aka 'CR5' and 'CR6' on the ACIA
                                                        6850 datasheet)
                                                        which means set the 'Request To
                                                        Send' pin low and transmit interrupt
                                                        disabled.
                                                        this is the active state.
    BCS .rs423BufferSpaceOK                             if (buffer has enough free space)
                                                        then branch

.rs423SetRequestToSendInactive = $dc78
    LDX #%01000000                                      set X to value which will clear bit
                                                        5 and set bit 6 of ACIA control
                                                        register
                                                        (aka CR5 and CR6 on the ACIA 6850
                                                        datasheet)
                                                        which means set the 'Request To
                                                        Send' pin high and transmit
                                                        interrupt disabled.
                                                        this is the inactive state.
.rs423BufferSpaceOK = $dc7a
    JMP .writeToACIARequestToSend                       write to 'Request To Send' value in
                                                        ACIA

§5. readFromRS423.

.readFromRS423 = $dc7d
    LDY .acia6850DataRegister                           read serial data from ACIA
    AND #$3A                                            AND %0011 1010
    BNE .rs423ErrorDetected                             if (not zero) then branch
    LDX .rs423InputSuppressionFlag                      read RS-423 input suppression flag
    BNE +                                               if (RS-423 input suppressed) then
                                                        branch (return)
    INX                                                 X=1
    JSR .osbyte153EntryPoint                            put byte in RS-423 input buffer
    JSR .getRS423InputBufferFreeBytes                   count buffer
    BCC .rs423SetRequestToSendInactive                  if (buffer is low on free space)
                                                        then branch back (to make 'Request
                                                        To Send' inactive)
+
    RTS                                                 

§6. IRQ1 default handler.

 When a hardware device generates an IRQ, this IRQ1 handler is executed (as the default
 handler, see .irqEntryPoint). After saving registers and a suitable return address on
 the stack, we check three hardware devices in turn, to see what needs attention:

  1. Check for ACIA interrupt (see .irq1CheckACIA)
       1a. updates RS-423 or Cassette then exit
  2. Check for System VIA interrupt (see .irq1CheckSystemVIA)
       2a. If VSYNC then update:
               vsync counter
               RS-423 counter
               flashing colours
               then exit
       2b. If Timer 2 times out (Speech timer) (see .irq1CheckSystemVIASpeech)
               update speech system (send speech data from speech buffer as needed)
               then exit
       2c. If Timer 1 times out (100Hz timer) (see .irq1CheckSystemVIA100HzTimer)
           then update:
               the clock
               the countdown timer event
               the INKEY timeout counter
               sound
               speech
               ACIA
               printer
               keyboard
               ADC conversion
               then exit
  3. Check for User VIA interrupt (see .irq1CheckUserVIA)
       3a. updates a parallel printer then exit

  Unrecognised interrupts are passed on to IRQ2V, allowing user code to process it.
.irq1Handler = $dc93
    CLD                                                 clear decimal flag
    LDA .interruptAccumulator                           }
    PHA                                                 }
    TXA                                                 } push original A,X,Y register onto
                                                        } stack
    PHA                                                 }
    TYA                                                 }
    PHA                                                 }

    LDA #>(.restoreRegistersAndReturnFromInterrupt-1)   }
    PHA                                                 } push return address onto stack
    LDA #<(.restoreRegistersAndReturnFromInterrupt-1)   } RTS will now exit to
                                                        }
                                                        } .restoreRegistersAndReturnFromInterrupt
    PHA                                                 }
    CLV                                                 clear V flag (meaning: check the
                                                        ACIA generated the interrupt)
    fall through...

§7. irq1CheckACIA.

 Check for ACIA Interrupt or Update ACIA

 Two modes of operation (V CLEAR and V SET)

 On Entry:
       V CLEAR means check that the ACIA generated the interrupt, and if so then update
               the RS-423 or cassette as needed
       V  SET  means just update the RS-423
.irq1CheckACIA = $dca2
    LDA .acia6850StatusRegister                         get the ACIA status register
    BVS +                                               if ('V SET') then branch (don't
                                                        check the ACIA generated the
                                                        interrupt)
    BPL .irq1CheckSystemVIA                             if (ACIA didn't generate the
                                                        interrupt) then branch (check the
                                                        next thing)
+
    LDX .rs423TimeoutCounter                            read RS-423 timeout counter
    DEX                                                 decrement it
    BMI .rs423HasControl                                if (timed out) then branch
    BVS .exit13                                         if ('V SET') then branch (return)
    JMP .updateACIA                                     update ACIA

§8. rs423ErrorDetectedSetAY.

.rs423ErrorDetectedSetAY = $dcb3
    LDY .acia6850DataRegister                           read ACIA data
    ROL                                                 
    ASL                                                 
    fall through...

§9. rs423ErrorDetected.

.rs423ErrorDetected = $dcb8
    TAX                                                 X=status byte shifted right once
    TYA                                                 A=character received
    LDY #.eventRS423ErrorDetected                       Y=7
    JMP .eventEntryPoint                                check and service EVENT 7 RS-423
                                                        error

§10. Send to RS-423 / Serial printer as needed.

 If we have a non-empty RS-423 output buffer, then send the next byte, reset the RS-423
 timeout counter and exit.

 If we have a serial printer, update the printer buffer empty flag. If not empty then
 send the byte to the printer (and also reset the RS-423 timeout counter).
.writeToACIA = $dcbf
    LDX #.bufferNumberRS423Output                       }
    JSR .osbyte145EntryPoint                            } read from RS-423 output buffer
    BCC +                                               if (buffer isn't empty) then branch
                                                        forward

    RS-423 buffer empty
    LDA .printerDestination                             read printer destination
    CMP #2                                              is it serial printer?
    BNE .rs423Handler                                   if (not a serial printer) then
                                                        branch (it must be RS-423)

    serial printer
    INX                                                 X=3 (.bufferNumberPrinter)
    JSR .osbyte145EntryPoint                            read printer buffer
    ROR .printerBufferEmptyFlag                         update the empty flag (put carry
                                                        into bit 7)
    BMI .rs423Handler                                   if (printer buffer is empty) then
                                                        branch
+
    STA .acia6850DataRegister                           pass either printer or RS-423 data
                                                        to ACIA
    LDA #$E7                                            } reset timeout counter to
    STA .rs423TimeoutCounter                            } count up 25 vsyncs (0.5 seconds)
.exit13 = $dcdd
    RTS                                                 

§11. Service RS-423 interrupt.

 See NAUG Section 8.4 - Serial system interrupts, Page 126

 On Entry:
       A = ACIA status byte

       Two modes of operation:
       V CLEAR - read from RS-423; handle DCD (Data Carrier Detect); write to RS-423;
                 handle unrecognised interrupt
       V SET   - handle DCD; write to RS-423
.rs423HasControl = $dcde
    AND .rs423IRQBitMask                                AND with ACIA bit mask (normally $FF)
    LSR                                                 shift right to put bit 0 of ACIA
                                                        status (receive interrupt) in carry
    BCC +                                               if (not a receive interrupt) then
                                                        branch (skip read)
    BVS +                                               if (V SET mode) then branch (skip
                                                        read)
    LDY .rs423ControlRegisterCopy                       read copy of ACIA control register
    BMI .readFromRS423                                  if (ACIA 6850 interrupts enabled)
                                                        then branch
+
    LSR                                                 shift,  put bit 1 of ACIA status
                                                        into carry
    ROR                                                 rotate, put bit 2 of ACIA status
                                                        into carry
    BCS .rs423ErrorDetectedSetAY                        if (Data Carrier Detected interrupt)
                                                        then branch
    BMI .writeToACIA                                    if (ACIA status bit 1 is set) then
                                                        branch (transmit interrupt)
    BVS .exit13                                         if (V SET mode) then branch (exit)
    fall through...

§12. Unrecognised Interrupt.

 When we have an interrupt that we don't understand, pass it to the ROMs to see if
 they can handle it. If not, then pass it on to the IRQ2 vector.
.unrecognisedInterrupt = $dcf3
    LDX #.romServiceCallUnrecognisedInterrupt           }
    JSR .osbyte143EntryPoint                            } issue paged ROM service call for
                                                        } 'unrecognised interrupt'
    BEQ .exit13                                         if (a ROM recognises it) then branch
                                                        (exit)
    PLA                                                 otherwise remove address from stack
    PLA                                                 
    PLA                                                 and restore Y, X, and A
    TAY                                                 
    PLA                                                 
    TAX                                                 
    PLA                                                 
    STA .interruptAccumulator                           .interruptAccumulator=A
    JMP (.vectorIRQ2V)                                  and offer to the user via IRQ2V

§13. Check for System VIA interrupt.

 Check if the System VIA triggered the interrupt and deal with it.
.irq1CheckSystemVIA = $dd06
    LDA .systemVIAInterruptFlagRegister                 read system VIA interrupt flag
                                                        register
    BPL .irq1CheckUserVIA                               if (the System VIA has not caused
                                                        interrupt) then branch (try the next
                                                        thing)

    AND .systemVIAIRQBitMask                            mask with VIA bit mask (normally $FF)
    AND .systemVIAInterruptEnableRegister               and interrupt enable register
    ROR                                                 }
    ROR                                                 } rotate right twice to check for
                                                        } IRQ 1 (vsync)
    BCC .irq1CheckSystemVIASpeech                       if (not vsync) then branch (to check
                                                        for speech)

    vsync interrupt
    DEC .verticalSyncCounter                            decrement vertical sync counter
    LDA .rs423TimeoutCounter                            A = RS-423 Timeout counter
    BPL +                                               if (positive) then branch
    INC .rs423TimeoutCounter                            increment counter
+
    LDA .videoULAFlashingColourIntervalCount            load flash counter
    BEQ .doneFlashingColours                            if (flashing system is not in use)
                                                        then branch
    DEC .videoULAFlashingColourIntervalCount            decrement counter
    BNE .doneFlashingColours                            if (not time for a colour to flash)
                                                        then branch (to continue processing)

    ready to flash colour
    LDX .videoULAFirstFlashingColourInterval            get mark period count in X
    LDA .videoULAVideoControlRegisterCopy               current Video ULA control setting in
                                                        A
    LSR                                                 shift bit 0 into C
    BCC .restoreAndFlipBit                              if (first colour is in effect) then
                                                        branch

    LDX .videoULASecondFlashingColourInterval           get second colour period count in X
.restoreAndFlipBit = $dd34
    ROL                                                 restore bit
    EOR #1                                              and invert it
    JSR .setVideoULA                                    then set colour
    STX .videoULAFlashingColourIntervalCount            reset the count until the next flash

.doneFlashingColours = $dd3d
    LDY #.eventStartOfVSync                             Y=event type: start of vsync
    JSR .eventEntryPoint                                call the event
    LDA #%00000010                                      A=bit set to clear the vsync
                                                        interrupt flag
    JMP .storeToSystemVIAIFR                            clear interrupt and exit

§14. Check for User VIA interrupt.

 Check if the User VIA triggered the interrupt and deal with it (check for parallel
 printer in particular).
.irq1CheckUserVIA = $dd47
    LDA .userVIAInterruptFlagRegister                   check User VIA interrupt flags
                                                        register
    BPL .unrecognisedInterrupt                          if (User VIA did not call interrupt)
                                                        then branch

    AND .userVIAIRQBitMask                              }
    AND .userVIAInterruptEnableRegister                 }
    ROR                                                 }
    ROR                                                 } check for User IRQ bit 1 set
                                                        } (printer interrupt)
    BCC .unrecognisedInterrupt                          if (not printer interrupt) then
                                                        branch
    LDY .printerDestination                             get printer type
    DEY                                                 decrement
    BNE .unrecognisedInterrupt                          if (not a parallel printer) then
                                                        branch

    deal with parallel printer
    LDA #%00000010                                      interrupt bit 1 (printer interrupt)
    STA .userVIAInterruptFlagRegister                   clear interrupt bit 1
    STA .userVIAInterruptEnableRegister                 enable interrupt bit 1
    LDX #.bufferNumberPrinter                           }
    JMP .openPrinterChannel                             } output data to parallel printer

§15. Check to see if the interrupt is a Speech interrupt and deal with it.

 The speech processor chip has an internal 16 byte buffer to hold incoming data.

 When speaking, this buffer will run out every 50 milliseconds. When the buffer becomes
 half full, an interrupt occurs. In fact, a speech interrupt is generated when any of the
 following occur:

   (1) End of speech processing
   (2) Speech hardware is low on data (half full, i.e. 8 bytes or less), indicating more
       phrase data needs to be supplied for the Speak External command.
   (3) Speech hardware is empty of data, indicating the CPU failed to supply phrase data
       fast enough for the Speak External command.
   (4) At the start of a Speak External instruction, if speech hardware is not empty of data.

 We call .updateSpeech in the 100Hz interrupt to keep the speech hardware regularly supplied
 with data.

 On Entry:
       A = System VIA interrupt flag shifted right twice
.irq1CheckSystemVIASpeech = $dd69
    ROL                                                 } rotate left twice to get back to
    ROL                                                 } original interrupt flag register
                                                        } alignment of bits

    ROL                                                 rotate left to get bit 5 into bit 6
    ROL                                                 rotate left to get bit 6 into bit 7
    BPL .irq1CheckSystemVIA100HzTimer                   if (bit 5 of system via IFR is
                                                        clear, i.e. it's not timer 2, used
                                                        for the speech interrupt)
                                                        then branch

    deal with speech
    LDA #%00100000                                      } clear bit 5, the timer 2
    LDX #0                                              } interrupt
    STA .systemVIAInterruptFlagRegister                 }
    STX .systemVIATimer2CounterHigh                     clear Timer 2 (write to high byte)

.updateSpeech = $dd79
    LDX #8                                              } loop counter (shifted right on
    STX .tempStoreFB                                    } each loop iteration)

    on each speech update we loop a maximum of 4 times to feed the speech chip with up to
    4 bytes of new data from the CPU's speech buffer.
.feedSpeechChipWithDataLoop = $dd7d
    check our speech buffer, see if there's more data
    JSR .osbyte152EntryPoint                            examine buffer status for buffer 8
                                                        (the speech buffer), C set if buffer
                                                        is empty, otherwise A is byte read.
    ROR .speechBufferEmptyFlag                          shift carry into bit 7 - store as
                                                        the 'speech buffer empty' bit
    BMI .exit14                                         if (top bit is set, i.e. set buffer
                                                        is empty) then branch (return)
    TAY                                                 Y = A = next byte to be removed
                                                        from speech buffer
    BEQ +                                               

    check if speech chip is ready?
    JSR .osbyte158EntryPoint                            read byte from speech chip
    BMI .exit14                                         if (top bit set) then branch (exit)

+
    read three bytes from the speech buffer: <ROM number> <2 byte address>
    JSR .osbyte145EntryPoint                            read a byte from CPU's speech buffer
    STA .currentSpeechPHROMOrROMNumber                  store ROM number
    JSR .osbyte145EntryPoint                            read another byte
    STA .romAddressHigh                                 store ROM address
    JSR .osbyte145EntryPoint                            read another byte
    STA .romAddressLow                                  store ROM address

    LDY .currentSpeechPHROMOrROMNumber                  Y=ROM / PHROM number
    BEQ .writeROMAddressToSpeechProcessor               if (current logical speech PHROM
                                                        or ROMFS Number is zero) then branch
    BPL .writeCommandAndAddressToSpeechProcessor        if (+ve) then branch
    BIT .currentSpeechPHROMOrROMNumber                  check bit 6 of PHROM or ROM number
    BVS +                                               if (PHROM or ROM number has bit 6
                                                        set) then branch
    JSR .writeAddressAndROMNumberToSpeechProcessor      continue for more speech processing
    BVC .enableOrDisableSpeech                          ALWAYS branch

+
    ASL .romAddressLow                                  double ROM address
    ROL .romAddressHigh                                 
    JSR .readFromPHROM                                  and call .readFromPHROM

.enableOrDisableSpeech = $ddb2
    LDY .speechSuppressionStatus                        get speech enable/disable flag
                                                        whose values equate to speech
                                                        processor commands:
                                                        $50 = 'speak'; $20 = 'nop'
    JMP .osbyte159EntryPoint                            write to speech processor

.writeCommandAndAddressToSpeechProcessor = $ddb8
    JSR .osbyte159EntryPoint                            write ROM number
.writeROMAddressToSpeechProcessor = $ddbb
    LDY .romAddressLow                                  get address pointer in Y
    JSR .osbyte159EntryPoint                            write address to speech chip
    LDY .romAddressHigh                                 get address pointer high in Y
    JSR .osbyte159EntryPoint                            write address to speech chip

    LSR .tempStoreFB                                    update loop counter
    BNE .feedSpeechChipWithDataLoop                     loop back

.exit14 = $ddc9
    RTS                                                 

§16. IRQ1 Interrupt - 100Hz interrupt.

.irq1CheckSystemVIA100HzTimer = $ddca
    BCC .irq1CheckSystemVIAADCEndConversion             bit 6 is in carry. if (there is no
                                                        bit 6 interrupt) then branch
    LDA #%01000000                                      }
    STA .systemVIAInterruptFlagRegister                 } clear interrupt 6

    Update the 5 byte clock. There are 2 clock stores, at .timeClockA and .timeClockB. These
    are updated by adding 1 to the current clock and storing the result in the other, the
    direction of transfer being changed each time of update.  This ensures that at least
    1 clock store is valid at any call as the current clock is only read.  Other methods
    would cause inaccuracies if a clock was read whilst being updated.
    LDA .timeClockSwitch                                get current system clock pointer (5
                                                        or 10)
    TAX                                                 put old system clock pointer in X
    EOR #$0F                                            invert bits so it toggles between 5
                                                        and 10
    PHA                                                 remember A
    TAY                                                 put new system clock pointer in Y
-
    LDA .timeClockA-1,X                                 get current system clock value
    ADC #0                                              increment it (carry is set on the
                                                        first iteration of the loop,
                                                            thereafter carry is set as
                                                        needed based on this increment)
    STA .timeClockA-1,Y                                 store result in new copy
    DEX                                                 decrement X
    BEQ +                                               if (finished) then branch (loop ends)
    DEY                                                 decrement Y
    BNE -                                               and go back and do next byte
+
    PLA                                                 get back new clock pointer
    STA .timeClockSwitch                                and store back in clock pointer

    Update countdown interval timer. An EVENT is generated when it times out.
    LDX #5                                              set loop counter
-
    INC .countdownIntervalTimer - 1,X                   increment byte for countdown timer
    BNE +                                               if (done updating timer bytes) then
                                                        branch
    DEX                                                 decrement loop counter
    BNE -                                               if (not finished looping) do it again

    LDY #.eventIntervalTimerCrossingZero                at this point all the interval timer
                                                        bytes are zero
    JSR .eventEntryPoint                                call EVENT 5 interval timer

    Update INKEY timeout. Used when reading a key within a time limit.
+
    LDA .inkeyTimeoutCounterLow                         get low byte of inkey countdown timer
    BNE +                                               if (inkey timer is non-zero) then
                                                        branch forward
    LDA .inkeyTimeoutCounterHigh                        get high byte of inkey countdown
                                                        timer
    BEQ ++                                              if (inkey timer is zero) then branch
                                                        forward
    DEC .inkeyTimeoutCounterHigh                        decrement high byte
+
    DEC .inkeyTimeoutCounterLow                         and decrement low byte

    Update sound.
++
    BIT .soundIsUpdatingFlag                            check bit 7 of sound flag
                                                        (this bit is set while processing
                                                        sound interrupt. check so we don't
                                                        try to process a second sound
                                                        interrupt while still processing
                                                        the first)
    BPL +                                               if (bit clear; still processing)
                                                        then branch (skip forward)
    INC .soundIsUpdatingFlag                            increment to 0 (to signify that
                                                        sound is being processed here)
    CLI                                                 allow interrupts briefly
    JSR .processSoundInterrupt                          process sound
    SEI                                                 disable interrupts
    DEC .soundIsUpdatingFlag                            decrement 'sound is updating' flag
                                                        back to $FF (this signifies that we
                                                        are done processing the sound)

    Update speech.
+
    BIT .speechBufferEmptyFlag                          check speech buffer empty flag
    BMI +                                               if (speech buffer is empty) then
                                                        branch (skip speech)
    JSR .osbyte158EntryPoint                            read speech processor status register
    EOR #%10100000                                      flip two bits
    CMP #%01100000                                      check
    BCC +                                               if (result < $60) then branch
    JSR .updateSpeech                                   process speech

    Update ACIA.
+
    BIT .allBitsSet                                     set V flag
    JSR .irq1CheckACIA                                  update ACIA

    Update keyboard.
    LDA .lastKeyPressedInternal                         } check if a key is currently
    ORA .firstKeyPressedInternal                        } pressed
    AND .enableKeyboardInterruptProcessingFlag          ignore key presses if the flag
                                                        is zero
    BEQ +                                               if (no key pressed or we should
                                                        ignore keys pressed) then branch
                                                        (ignore keyboard)
    SEC                                                 set carry
    JSR .keyboardInterruptRoutine                       handle keyboard interrupt

    Update printer.
+
    JSR .printerServiceCallIfNotEmpty                   check for data in printer buffer

    Update ADC conversion.
    BIT .adcDataStatusRegister                          check ADC status register
    BVS .handleADCConversionEnds                        if (bit 6 is set, i.e. ADC not
                                                        busy) then branch

    RTS                                                 

§17. IRQ1 Interrupt - Analogue to Digital Conversion (End of Conversion).

 See .osbyte17EntryPoint
.irq1CheckSystemVIAADCEndConversion = $de47
    ROL                                                 put original bit 4 from
                                                        .systemVIAInterruptFlagRegister into
                                                        bit 7 of A
    BPL .irq1CheckSystemVIAKeyboard                     if (not an ADC conversion interrupt)
                                                        then branch

.handleADCConversionEnds = $de4a
    LDX .adcCurrentChannel                              get current ADC channel
    BEQ .clearInterrupt4                                if (zero) then branch (clear
                                                        interrupt and exit)

    LDA .adcDataLowByte                                 read low data byte
    STA .lowByteLastByteFromADCChannel1 - 1,X           store it in memory
    LDA .adcDataHighByte                                get high data byte
    STA .highByteLastByteFromADCChannel1 - 1,X          and store it in high byte
    STX .adcLastChannelRead                             store last ADC channel read
    LDY #.eventADCConversionComplete                    handle event 3 conversion complete
    JSR .eventEntryPoint                                

    DEX                                                 decrement X
    BNE +                                               if (X != 0) then branch forward
    LDX .maximumADCChannelNumber                        get highest ADC channel present
+
    JSR .startADCConversion                             start new conversion
.clearInterrupt4 = $de6c
    LDA #%00010000                                      reset interrupt 4
.storeToSystemVIAIFR = $de6e
    STA .systemVIAInterruptFlagRegister                 
    RTS                                                 

§18. IRQ1 Interrupt - keyboard.

.irq1CheckSystemVIAKeyboard = $de72
    ROL                                                 get original bit 0 in bit 7 position
    ROL                                                 
    ROL                                                 
    ROL                                                 
    BPL +                                               if (not a keyboard interrupt) then
                                                        branch (skip keyboard processing)
    JSR .keyboardInterruptRoutine                       scan keyboard
    LDA #%00000001                                      }
    BNE .storeToSystemVIAIFR                            } reset interrupt 0 and return
+
    JMP .unrecognisedInterrupt                          

§19. restoreRegistersAndReturnFromInterrupt.

.restoreRegistersAndReturnFromInterrupt = $de82
    PLA                                                 restore AXY registers
    TAY                                                 
    PLA                                                 
    TAX                                                 
    PLA                                                 
    STA .interruptAccumulator                           store A
    fall through...

§20. Default IRQ2 handler - does nothing.

.irq2Handler = $de89
    LDA .interruptAccumulator                           get back original value of A
    RTI                                                 return

§21. OSBYTE 17 - Start ADC conversions.

7002.png

 Analogue to digital conversion is performed by the ADC 7002 chip. It has four channels
 of data that can be read. This is often used to read an analogue joystick. This starts
 the conversion happening. They will keep happening until OSBYTE 16 with X=0 is called.

 OSBYTE 16 selects the range of channels to convert (0-4). See .osbyte16EntryPoint.

 OSBYTE 190 selects the ADC conversion type (8 or 12 bits). See .adcConversionType.
 Twelve bit conversion is the default (which takes about 10ms). Eight bit conversions are
 faster at around 4ms.

 The ADC 7002 generates an interrupt when the conversion is complete, and the IRQ1 handler
 stores the two byte result (in .lowByteLastByteFromADCChannel1 and upwards). It also
 starts the next conversion. See .irq1CheckSystemVIAADCEndConversion.

 OSBYTE 128 reads the latest converted ADC value (X=ADC channel 1-4). See .osbyte128EntryPoint.
 OSBYTE 128 can also read the state of the fire buttons. (X=0). See .osbyte128EntryPoint.
 These are encapsulated in the BASIC ADVAL command.

 On Entry:
       X = ADC channel number (1-4)
       Y = 0
.osbyte17EntryPoint = $de8c
    STY .adcLastChannelRead                             set last channel to finish
                                                        conversion (to zero)
.startADCConversion = $de8f
    CPX #5                                              
    BCC +                                               if (X < 5) then branch (skip forward)
    LDX #4                                              X=4 (maximum)
+
    STX .adcCurrentChannel                              store it as current ADC channel
    LDY .adcConversionType                              get conversion type
    DEY                                                 decrement
    TYA                                                 A=Y
    AND #%00001000                                      clear to zero except for bit 3 set
                                                        indicates a 12 bit conversion
    CLC                                                 } add the ADC channel (1-4)
    ADC .adcCurrentChannel                              } then subtract one, effectively
    SBC #0                                              } adding channel (0-3) into bits 0-2
    STA .adcStartConversionRegister                     store value to start ADC conversion
    RTS                                                 

§22. Display string on screen.

 Display the boot message (e.g. 'BBC Computer 32K') and language ROM title (e.g. 'BASIC')

 On Entry:
       Y = offset from page of .vduBaseAddress to start of string -1
.displayString = $dea9
    LDA #>.vduBaseAddress                               Start of string area (high byte)
.displayStringAY = $deab
    STA .displayStringAddressHigh                       store it
    LDA #0                                              Low byte is zero (we use Y to index
                                                        to correct string)
    STA .displayStringAddressLow                        store it and start loop
    fall through...

§23. printMessage.

.printMessage = $deb1
    INY                                                 print character in string
    LDA (.displayStringAddressLow),Y                    pointed to by $FD/E
    JSR .OSASCI                                         print it expanding Carriage returns
    TAX                                                 store A in X
    BNE .printMessage                                   and loop again if not zero
    RTS                                                 

§24. osbyte129Timed.

 On Entry:
       X/Y holds the time limit
.osbyte129Timed = $debb
    STX .inkeyTimeoutCounterLow                         store time in INKEY countdown timer
    STY .inkeyTimeoutCounterHigh                        which is decremented every 10ms
    LDA #255                                            
    BNE .readCharacterTimed                             ALWAYS branch