; Rick's 6502 BIOS v0.01 for AMELIE (started 2007/08/06)
; 2007/09/06
;
; To be compiled with 6502asm
; http://www.heyrick.co.uk/amelie/
;
;


   ROM             ; base & org = &E000, top = &FFFF, inserts vector links


   ; ===============================================================
   ; =                                                             =
   ; =   P  A  G  E     Z  E  R  O     L  O  C  A  T  I  O  N  S   =
   ; =                                                             =
   ; ===============================================================
   ;
   ; Page Zero is defined thus:
   ;
   ;   &0000 - &0001 = Intentionally unallocated (2 bytes)
   ;                   (so errors causing writes to +0 do not muck things up)
   ;
   ;           &0002 = Watchdog counter (1 byte)
   ;
   ;   &0003 - &005F = BIOS locations (93 bytes)
   ;                   (all areas not specifically defined are "reserved")
   ;
   ;                     &0005 = Detected memory size (in 256 byte pages)
   ;
   ;                     &0008 = Junk byte used by delay routine
   ;
   ;                     &000A = 50Hz ticker value
   ;                     &000B = Seconds increment
   ;                     &000C = Minutes increment
   ;                     &000D = Hours increment
   ;                     &000E = Days increment
   ;
   ;                     &0010 = LED active (which LEDs are we controlling?)
   ;                     &0011 = LED on/off (current LED state)
   ;                     &0012 = LED actustate (current LED state copy)
   ;                     &0013 = LED #1 flash counter
   ;                     &0014 = LED #2 flash counter
   ;                     &0015 = LED #3 flash counter
   ;                     &0016 = LED #4 flash counter
   ;                     &0017 = LED #1 flash reload
   ;                     &0018 = LED #2 flash reload
   ;                     &0019 = LED #3 flash reload
   ;                     &001A = LED #4 flash reload
   ;
   ;                     &0020 = Serial INPUT buffer READ pointer
   ;                     &0021 = Serial INPUT buffer WRITE pointer
   ;                     &0022 = Serial OUTPUT buffer READ pointer
   ;                     &0023 = Serial OUTPUT buffer WRITE pointer
   ;                     &0024 = Serial output INHIBITED if non-zero
   ;                     (other serial status is in Page Two)
   ;
   ;   &0060 - &007F = Vector area (32 bytes)
   ;                     &0060 = IRQ vector (IRQV)
   ;                     &0062 = Ticker vector (TICKV)
   ;                     &0064 = VIA IRQ vector (VIAV)
   ;                     &0066 = <unused 1>
   ;                     &0068 = Serial buffer full vector (SFULLV)
   ;                     &006A = Serial RXByte vector (SRXV)
   ;                     &006C = Serial NewLine vector (SNEWLV)
   ;                     &006E = Serial TXByte vector (STXV)
   ;                     &0070 = Serial Event vector (SEVEV)
   ;                     &0072 = WatchFail vector (SWAFV)
   ;                     &0074 = <reserved 1 (NMNV)>
   ;                     &0076 = <reserved 2 (NHRV)>
   ;                     &0078 = <reserved 3 (NDYV)>
   ;                     &007A = <unused 2>
   ;                     &007C = <unused 3>
   ;                     &007E = System Abort vector (ABORTV)
   ;
   ;   &0080 - &00BF = Application area (64 bytes)
   ;                     (defined according to application code)
   ;
   ;   &00C0 - &00FF = BIOS reserved (64 bytes)
   ;                     (not currently used)
   ;
   ;


   ; ----------------------------------------------------------------
   ; WATCHDOG:
   ;   Counts UP at 50Hz. If it ever reaches 250, BIOS will force
   ;   an abort (reset hardware to default - should force motors off)
   ;   then fast-flash all LEDs, and either enter a recursive loop
   ;   or some form of debugger according to build of BIOS.
   ;   Application code MUST periodically reset this to zero; the
   ;   BIOS will do likewise *ONLY* in delay code.
   ORG  &0002
.watchdog
   DCW  0


   ; ----------------------------------------------------------------
   ; DETECTED MEMORY SIZE:
   ;   Not currently implemented. A later version of the BIOS will
   ;   probe memory to count the number of 256 byte 'pages' that
   ;   are available. For now, we will be hardwired to 2048 bytes
   ;   which is 8 pages.
   ORG  &0005
.memory_size
   DCB  0


   ; ----------------------------------------------------------------
   ; JUNK BYTE:
   ;   This is used to waste time in the delay routine.
   ORG  &0008
.delay_junk
   DCB  0


   ; ----------------------------------------------------------------
   ; TICKER / NEWSEC / NEWMIN / NEWHOUR / NEWDAY:
   ;   This is incremented at 50Hz. When it reaches 50 (1 second),
   ;   it is reset and NEWSEC is incremented.
   ;   NEWSEC = 60 := NEWSEC = 0 AND NEWMIN ++
   ;   NEWMIN = 60 := NEWMIN = 0 AND NEWHOUR ++
   ;   NEWHOUR = 24 := NEWHOUR = 0 AND NEWDAY ++
   ;   NEWDAY = 255 := WRAPS TO NEWDAY = 0
   ORG  &000A
.ticker
   DCB  0
.newsec
   DCB  0
.newmin
   DCB  0
.newhour
   DCB  0
.newday
   DCB  0


   ; ----------------------------------------------------------------
   ; LED STATUS:
   ;   Normally, the state of the LEDs (including flashing) are under
   ;   control of the BIOS. These locations specify which 
   ;   LED active - Bits 0 to 3 denote which LEDs we are controlling.
   ;                If bit is zero, ignore the state of this LED
   ;   LED onoff  - The value to write to the latch, as the latch is
   ;                a write-only device. If you have a need to set
   ;                the LEDs other than that provided by the BIOS,
   ;                then mask OUT the LED bit in "LED active" and
   ;                mask IN the LED bit as applicable in "LED onoff"
   ;                and call BIOS to write LED status to the latch.
   ;   LED actustate - As soon as the "LED onoff" is written to the
   ;                latch, the value of it is written to this byte.
   ;                In the future we can, under the ticker event,
   ;                set the status of the four LEDs according to
   ;                their flash counter. Once performed, we can then
   ;                compare "LED onoff" ('pending') with this the
   ;                actual state to see if the LED status has in
   ;                fact changed. DO NOT WRITE THIS YOURSELF.
   ;   LED flash counter #1 to #4 - counts down at 50Hz. When it
   ;                reaches zero, the state of the LED is changed.
   ;                If it is already AT zero, the LED state is not
   ;                altered.
   ;   LED flash reload #1 to #4 - when the counter reaches zero
   ;                it is automatically reloaded with the timeout
   ;                value. This holds the value to reload into the
   ;                counter.
   ORG  &0010
.led_active
   DCB  0
.led_onoff
.led_status        ; alias
   DCB  0
.led_actustate
   DCB  0
.led_counter_1
   DCB  0
.led_counter_2
   DCB  0
.led_counter_3
   DCB  0
.led_counter_4
   DCB  0
.led_reload_1      ; The ticker is 50Hz, therefore:
   DCB  0          ;
.led_reload_2      ; Reload = 50 = 1 second on, 1 second off
   DCB  0          ;          25 = 1/2 sec on, 1/2 sec off ("slow") 
.led_reload_3      ;          13 = ~1/4 sec on, ~1/4 sec off ("fast")
   DCB  0          ;          10 = 1/5 sec on, 1/5 sec off; used ONLY
.led_reload_4      ;               by the BIOS when abort failure.
   DCB  0          ; It isn't possible to have differing on/off times.


   ; ----------------------------------------------------------------
   ; SERIAL STATUS:
   ;   This holds the rapid-access serial status information.
   ;   The serial buffers are a 96 byte OUTPUT ring buffer and a 128
   ;   byte INPUT ring buffer.
   ;   Incoming data is written to the INPUT buffer WRITE pointer.
   ;   Serial RXByte reads from the INPUT buffer READ pointer.
   ;   If the two equal, the buffer is empty.
   ;   Outgoing data is read from the OUTPUT buffer READ pointer.
   ;   Serial TXByte writes to the OUTPUT buffer WRITE pointer.
   ;   If the two equal, the buffer is empty.
   ;   If INHIBIT is NON-ZERO, serial transmission is disabled. 
   ;
   ;   Note that the pointers start at ZERO, so the effective address
   ;   is [<buffer> + <pointer>].
   ;
   ORG  &0020
.serial_input_read
   DCB  0
.serial_input_write
   DCB  0
.serial_output_read
   DCB  0
.serial_output_write
   DCB  0
.serial_output_inhibit
   DCB  0


   ; ----------------------------------------------------------------
   ; VECTORS:
   ORG  &0060
.irqv
   DCW  0
.tickv
   DCW  0
.viav
   DCW  0
; unused at &0066
   DCW  0
.sfullv
   DCW  0
.srxv
   DCW  0
.snewlv
   DCW  0
.stxv
   DCW  0
.sevev
   DCW  0
.swafv
   DCW  0
; reserved at &0074
   DCW  0
; reserved at &0076
   DCW  0
; reserved at &0078
   DCW  0
; unused at &007A
   DCW  0
; unused at &007C
   DCW  0
.abortv
   DCW  0


   ; All other page zero memory locations are reserved, unused, or
   ; available according to address. 


   ; ----------------------------------------------------------------
   ;  E N D   O F   P A G E   Z E R O   A L L O C A T I O N S . . .
   ; ----------------------------------------------------------------




   ; ============================================================
   ; =                                                          =
   ; =   H  A  R  D  W  A  R  E     L  O  C  A  T  I  O  N  S   =
   ; =                                                          =
   ; ============================================================
   ;


   ; -------------
   ; VIA LOCATIONS
   ; -------------
   ORG  &A000
.via_iorb              ; In/Out port B
   DCB  0
.via_iora_handshake    ; In/Out port A *with* *handshake*
   DCB  0
.via_ddrb              ; Port B data direction
   DCB  0
.via_ddra              ; Port A data direction
   DCB  0
.via_t1cl              ; Timer1 Low-order Counter/Latch
   DCB  0
.via_t1ch              ; Timer1 High-order Counter
   DCB  0
.via_t1ll              ; Timer1 Low-order Latch
   DCB  0
.via_t1lh              ; Timer1 High-order Latch
   DCB  0

   DCB  0              ; Timer2 (not supported in AmélieEm
   DCB  0              ;         and not used in Amélie)
   DCB  0              ; Shift Register (not supported or used)

.via_acr               ; Auxiliary Control Register
   DCB  0
.via_pcr               ; Peripheral Control Register
   DCB  0
.via_ifr               ; Interrupt Flag Register
   DCB  0
.via_ier               ; Interrupt Enable Register
   DCB  0
.via_iora              ; In/Out port A (without handshake)
   DCB  0


   ; --------------
   ; ACIA LOCATIONS
   ; --------------      READ:       WRITE:
   ORG  &A100
.acia_txrx             ; Receive     Transmit
   DCB  0
.acia_status           ; Status
.acia_reset            ;             Reset
   DCB  0
.acia_command          ; Command     Command
   DCB  0
.acia_control          ; Control     Control
   DCB  0


   ; ------------------------
   ; OPTION SELECTOR SWITCHES
   ; ------------------------
   ORG  &A200
.optselector
   DCB  0


   ; --------------
   ; LATCH LOCATION
   ; --------------
   ORG  &A300
.latch
   DCB  0


   ; -------------------------------------------------------------
   ;  E N D   O F   H A R D W A R E   A L L O C A T I O N S . . .
   ; -------------------------------------------------------------



   BAS  &E000 FORCE; force low address to be &E000

   ; The above is important!
   ; Because we are assembling ROM code, we want only &E000 to &FFFF to be
   ; saved, but we also want to set up locations in page zero so we can
   ; refer to it by label.
   ; No problems - we'll just FORCE the base address back to &E000. This is
   ; one of the more advanced (read 'sneaky') features of 6502asm!




   ; =========================================================
   ; =                                                       =
   ; =   D  E  B  U  G  G  E  R     S  O  F  T  W  A  R  E   =
   ; =                                                       =
   ; =========================================================
   ;

   ORG  &F000

   ; In a 'full' build, the EPROM is expected to be allocated
   ; as follows:
   ;
   ;   &E000 - &F000  4096 bytes for the application code
   ;
   ;   &F000 - &F3FF  1024 bytes for the debugger
   ;   &F400 - &FFFF  3096 bytes for the BIOS and hardware vectors
   ;
   ;   Don't know if 1K will be enough for a debugger; may stash
   ;   some generic support routines (get input, print hex, etc) in
   ;   the BIOS. May shift allocations. See how it goes...
   ;

   ; Output an ID string into the EPROM
   DCS  "\r\n\r\n"
   DCS  "##############################\r\n"
   DCS  "#  Amélie DEBUGGER v0.01     #\r\n"
   DCS  "#  dev. version - 2007/09/06 #\r\n"
   DCS  "#  (c) 2007 Rick Murray      #\r\n"
   DCS  "#  www.heyrick.co.uk/amelie/ #\r\n"
   DCS  "##############################\r\n\r\n"

   ; However, there is NO debugger as yet.
.debugger_entry
   RTS

   ; -------------------------------
   ;  E N D   O F   D E B U G G E R 
   ; -------------------------------




   ; ======================================================================
   ; =                                                                    =
   ; =   T  H  E     B  I  O  S     C  O  D  E     F  O  L  L  O  W  S    =
   ; =                                                                    =
   ; ======================================================================
   ;

   ORG  &F400

   ; Output an ID string into the EPROM
   DCS  "\r\n\r\n"
   DCS  "##############################\r\n"
   DCS  "#  Amélie BIOS v0.01         #\r\n"
   DCS  "#  dev. version - 2007/09/06 #\r\n"
   DCS  "#  (c) 2007 Rick Murray      #\r\n"
   DCS  "#  www.heyrick.co.uk/amelie/ #\r\n"
   DCS  "##############################\r\n\r\n"


; R E S E T   V E C T O R
; -----------------------

   DCS  "#RSTVEC#" ; marker
.reset_vector      ; label is required by 6502asm's "ROM" command
   NOP
   SEI             ; should be already, but no guarantees
   CLD	           ; decimal mode undefined at NMOS 6502 init

   LDX  #&FF       ; reset stack pointer
   TXS

   ; *** MARKER POINT ZERO: RESET INVOKED
   ; -> Poke latch to switch *ALL* of the LEDs on:  0 0 0 0
   LDX  #%00001111
   STX  latch


   ; Blank the first kilobyte of memory, in parallel
   ; (second kilobyte is unallocated, it is user's responsibility to clear)
   ; == TO BE WRITTEN ==


   ; Set up the vector table
   ; Also sets 'slamdunk' interrupt branch point
   LDX  #0
.bios_vecloop
   LDA  bios_vectable, X
   STA  irqv, X
   INX
   CPX  #32
   BNE  bios_vecloop
      

   ; *** MARKER POINT ONE: MEMORY INITIALISED OKAY
   ; -> Poke latch to switch *FIRST* LED *OFF*:  x 0 0 0
   LDX  #%00000111
   STX  latch


   ; SET UP THE ACIA
   ; ===============

   ; Set up ACIA for 9600bps 8N1 comms
   LDX  acia_status        ; Read status to clear any IRQs

   LDX  #%00000011         ; No parity, no echo, DTR low
   STX  acia_command
   LDX  #%00011110         ; 1 stop bit, 8 bit data, 9600bps
   STX  acia_control


   ; *** MARKER POINT TWO: SERIAL INITIALISED OKAY
   ; -> Poke latch to switch first *TWO* LEDs *OFF*:  x x 0 0
   LDX  #%00000011
   STX  latch


   ; SET UP THE VIA
   ; ==============

   ; Clear ALL interrupts and interrupt sources
   LDX  #%01111111
   STX  via_ifr    ; IRQ flags; clear any pending
   STX  via_ier    ; IRQ enable; clear all

   ; Set up the ports
   LDX  #%00000000  ; all bits INPUT (bits 6+7 (IIC) write-thru)
   STX  via_ddra    ; write it to port A's DDR
   STX  via_iorb    ; ensure port B is inactive before we set
                    ; the DDR - paranoid, YES! but remember
                    ; that we may be connected to MOTORS, okay?
   LDX  #%00111111  ; all bits OUTPUT except bits 6+7
   STX  via_ddrb    ; write it to port B's DDR

   ; Set up Timer1 to create 20uS ticks (50Hz)
   ; If we are clocked at 2MHz, and we want to tick away 20uS,
   ; then each tick is 0.0000005th of a second, and we want a
   ; tick every 0.02th of a second. This means that we must
   ; set up the VIA to tick away 40,000 ticks per interrupt
   ; period, to give us our 50Hz!
   ; That's low-order =  64
   ;       high-order = 156
   LDX  #64         ; Timer1 continuous, no SR, PA/PB unlatched
   STX  via_acr     ; Aux. Control Register
   LDX  #&40
   STX  via_t1cl    ; Timer1 Low Order Latch first
   LDX  #&9C        ; then High Order latch
   STX  via_t1ch    ; SIDE EFFECT - timer has now been started

   ; Enable interrupts from the VIA. In AmélieEm, this will
   ; decide whether or not the Timer is fully 'active', so
   ; we'll be a few cycles off at the outset.
   LDX  #%11000000  ; enable Timer1 to generate interrupts
   STX  via_ier

   ; *** MARKER POINT THREE: VIA INITIALISED OKAY
   ; -> Poke latch to switch first three LEDs *OFF*:  x x x 1
   LDX  #%00000001
   STX  latch


   ; NOTHING should be pending, but just in case...
   CLI ; slamdunker will handle it right now
   NOP
   NOP
   SEI


   ; Set the default interrupt branch point
   LDX  #0
   LDA  bios_irqhdladdr, X ; first byte
   STA  irqv, X
   INX
   LDA  bios_irqhdladdr, X ; second byte
   STA  irqv, X

   ; Re-enable interrupts
   CLI

   ; *** MARKER POINT FOUR: BIOS INITIALISED OKAY
   ; -> Poke latch to switch *ALL* LEDs *OFF*:  x x x x
   LDX  #%00000000
   STX  latch

   ; BIOS startup complete! Call application...
   JMP  app_code_entry


; D E F A U L T   V E C T O R   T A B L E
; ---------------------------------------

; NO entry for IRQV, it is patched in separately
.bios_vectable
   DCW  bios_irqslamdunkaddr    ; Initial state of IRQV
   DCW  bios_tickv_handler      ; TICKV
   DCW  bios_viav_handler       ; VIAV
   DCW  bios_vecignore_handler  ; unused 1
   DCW  bios_sfullv_handler     ; SFULLV
   DCW  bios_vecignore_handler  ; SRXV is ignored
   DCW  bios_vecignore_handler  ; SNEWLV is ignored
   DCW  bios_stxv_handler       ; STXV
   DCW  bios_vecignore_handler  ; SEVEV is ignored
   DCW  bios_vecignore_handler  ; SWAFV is ignored (then calls ABORTV)
   DCW  bios_vecignore_handler  ; reserved 1
   DCW  bios_vecignore_handler  ; reserved 2
   DCW  bios_vecignore_handler  ; reserved 3
   DCW  bios_vecignore_handler  ; unused 2
   DCW  bios_vecignore_handler  ; unused 3
   DCW  bios_abortv_handler     ; ABORTV



; I R Q   H A N D L E R S
; -----------------------


; Addresses of built-in handlers
.bios_irqhdladdr
   DCW  bios_irq_handler ; address of...

.bios_irqslamdunkaddr
   DCW  bios_irq_slamdunk ; address of...


; PRIMARY IRQ VECTOR
; ------------------
; This is an absolute INDIRECT jump, so the user code can choose to patch
; in their own IRQ handler if they are so inclined, or otherwise do some
; sort of preprocess before calling the BIOS handler...
   DCS  "#IRQVEC#"
.irq_vector
   JMP  (irqv) ; jump indirect


; SLAMDUNK IRQ HANDLER
; --------------------
; This is an "ignore it" handler devised for the boot-up process. It'll
; simply slam EVERY device with whatever pokes are necessary to inform
; the device that the IRQ has been picked up. Of course, we couldn't care
; less WHY they're interrupting us. At startup, we simply don't wanna know.

.bios_irq_slamdunk
   ; Preserve registers
   PHA
   ; don't need to preserve X or Y, we don't use 'em

   ; Tell the VIA to take a hike
   LDA  #%11111111
   STA  &A00D

   ; Same for the ACIA
   LDX acia_status

   ; (other hardware later)

   ; Restore state and exit handler
   PLA
   RTI



; CENTRALISED IRQ HANDLER
; -----------------------
; Unless the user code is willing to replace ALL of the IRQ handler (which
; they may do via vectoring), we'll (eventually) be called...

.bios_irq_handler
   ; If patching into IRQs at base level...
   ;
   ; Intention:
   ;   User patches in to IRQ vector
   ;
   ; IRQ happens.
   ; User code called.
   ; Checks for it's own device.
   ; If not found, THIS function called.
   ;
   ; User must handle checks QUICKLY, it'll be called every 2cs; 40,000 cycles.
   ; STATE ON ENTRY *MUST* BE AS HARDWARE IRQ VECTOR SETS IT.


   PHA             ; Save ENTIRE state
   TXA             ; we'll optimise this later
   PHA
   TYA
   PHA

   ; NOTICE - we do NOT check for BRK


   ; PROBE - is IRQ from VIA?
   ; If YES: handle it
   ; ** PRIORITY FOR 50Hz TICKER **
   ; BIT via_irq_address  ; transfer b7 of via status to N register
   ; BMI ...              ; if VIA, handle it
   LDA  via_ifr
   BMI  bios_viaIRQentry


   ; PROBE - is IRQ from ACIA?
   ; If YES: handle it
   LDA  acia_status
   BMI  bios_aciaIRQ



   ; NOTHING ELSE GENERATES INTERRUPTS!

.bios_irq_exit
   PLA             ; Restore state
   TAY
   PLA
   TAX
   PLA
   RTI             ; DONE HERE!


.bios_tickerIRQ
   ; This is the regular 50Hz ticker.
   ; Here we manage the WatchDog and the LED blinking.
   ; We also check for outstanding serial data to be sent.
   LDX  #33         ; '!'
   STX  acia_txrx
   ; fall through to kill interrupts


.bios_viaIRQentry
   ; We must determine if the VIA's IRQ source
   ; is the 50Hz ticker or some sort of input.

   ; Sod off VIA (for now, later we'll actually DO something!)
   LDX  #%11000000
   STX  via_ifr

   ; DO NOT USE THE FOLLOWING IN A HARDWARE BUILD, IT (SHOULD)
   ; RESULT IN '!' BEING OUTPUT 50 TIMES A SECOND!
   LDX  #84         ; 'T' to mark that VIA interrupt happened
   STX  acia_txrx
   LDX  #105        ; 'i'
   STX  acia_txrx
   LDX  #99         ; 'c'
   STX  acia_txrx
   LDX  #107        ; 'k'
   STX  acia_txrx

   JMP  bios_irq_exit


.bios_viaIRQ
   ; Only one source - bump switches on CA1
   LDX  #66         ; 'B'
   STX  acia_txrx
   JMP  bios_irq_exit


.bios_aciaIRQ
   ; Serial event occurred...

   ; For testing emulator, if byte RECEIVED then we
   ; simply echo it back.

   LDA  acia_status
   AND  #%1000      ; mask out all except RRF
   BEQ  bios_irq_exit

   LDX  acia_txrx   ; Read the byte from serial port
   STX  acia_txrx   ; and write it back out again

   JMP  bios_irq_exit


   ; Amelie doesn't use NMIs at ALL, so force a reset
   ; if this is ever called, but it should NOT be
   ; because of the vector patch.
   DCS  "#NMIVEC#"
.nmi_vector
   JMP  reset_vector



; V E C T O R   H A N D L E R S
; -----------------------------

.bios_vecignore_handler
  ; Called for vectors which have no specific 'action',
  ; but can be tapped in to for extensibility.
  JMP  bios_irq_exit


.bios_tickv_handler
  ; The 50Hz ticker event
  ;
  ; Here we:
  ;   Increment the ticker value
  ;   Check the LED flash status
  ;   Decrement the watchdog byte

  ; == TO BE WRITTEN ==
  JMP  bios_irq_exit


.bios_viav_handler
  ; Some other VIA event happened
  ;
  ; Here we:
  ;   Set all outputs to zero (should 'stop' hardware)
  ;   Clear the interrupt
  ;
  ; This is a generic handler, it is expected that the
  ; application code will replace this with something
  ; that is appropriate for a VIA interrupt.

  ; == TO BE WRITTEN ==
  JMP  bios_irq_exit


.bios_sfullv_handler
  ; The serial buffer has filled up.
  ;
  ; Here we:
  ;   Set the handshaking to disable any further reception.
  ;
  ; This is normally a default BIOS service.

  ; == TO BE WRITTEN ==
  JMP  bios_irq_exit


.bios_stxv_handler
  ; The serial port has sent a byte.
  ; 
  ; Here we:
  ;   Send another byte, if there is one.
  ;
  ; This is normally a default BIOS service.

  ; == TO BE WRITTEN ==
  JMP  bios_irq_exit


.bios_abortv_handler
  ; The WatchDog has kicked in.
  ;
  ; Here we:
  ;   Set ALL LEDs to very-fast-blink
  ;   


  ; == TO BE WRITTEN ==

  JSR  debugger_entry
.bios_abort_nodebugger

  JMP  bios_irq_exit




; U T I L I T Y   C O D E . . . 
; -----------------------------

   DCS  "#UTILIS#"
.bios_delay
   ; This causes a delay of exactly 1ms (assuming no interrupts occur
   ; during call or return).
   ; The delay time INCLUDES call and return, so:
   ;    JSR  bios_delay
   ; would hold off for exactly a millisecond.
   ;                                              Cycles
   ; JSR bios_delay                                 6

   SEI             ;                                2
   PHP             ;                                3
   PHA             ;                                3
   LDA  #218       ; 218 loops required             2
   STA  delay_junk ; Page zero, wastes some time... 3
.bios_deloop
   NOP             ;                                2 \  = 9 cycles
   DEC  delay_junk ;                                5  } multiply by 218
   BNE  bios_deloop;                                2 /  = 1962 cycles (!)

   LDA  delay_junk ; just wasting time              3
   PLA             ;                                4
   PLP             ;                                4
   CLI             ;                                2
   RTS             ;                                6
                   ;                             ====
                   ;                           = 2000 cycles (1ms)



.bios_nidelay
   ; This causes a delay of exactly 1ms AND ASSUMES INTERRUPTS DISABLED.
   ;                                              Cycles
   ; JSR bios_nidelay                               6

   PHP             ;                                3
   PHA             ;                                3
   LDA  #218       ; 218 loops required             2
   STA  delay_junk ; Page zero, wastes some time... 3
.bios_nidelp
   NOP             ;                                2 \  = 9 cycles
   DEC  delay_junk ;                                5  } multiply by 218
   BNE  bios_nidelp;                                2 /  = 1962 cycles (!)

   ROR  delay_junk ; just wasting time              5
   NOP             ;                                2
   PLA             ;                                4
   PLP             ;                                4
   RTS             ;                                6
                   ;                             ====
                   ;                           = 2000 cycles (1ms)



   DCS  "####END-OF-BIOS-CODE####"


; LOOKING FOR THE HARDWARE VECTORS?
; The "ROM" instruction at the beginning auto-defined these for us...
;  -> nmi_vector 
;     reset_vector
;     irq_vector
;
; However we now want to PATCH the NMI vector to call the RST_VECTOR:

   ORG  &FFFA
   DCW  reset_vector



   ; NOW INCLUDE THE APPLICATION CODE!
   CNT  "appcode.s65"
