
.include "fb32x32.inc"

.include "globals.inc"

.define NMI_DELAY    200
.define IRQ_DELAY    500
.import  __PADATA_LOAD__
.import  __PADATA_SIZE__

.segment "DATA"

ir_vec:
   .word nmihandler, irqhandler  ; note that order is not same as below
ir_tim:
   .word IRQ_DELAY, NMI_DELAY    ; timer delays in ms/10: irq (20hz), nmi (50hz)

.segment "STARTUP"

fail:
   stz   TRAP
   int   MONITOR
   jmp   ($fffc)

.segment "CODE"

reloop:
   pla                     ; clear out return address from jsr (calltable,x)
   pla

   lda   #$00              ; first part will be only played in first loop
   .byte $2c
main:
   int   COPYBIOS
   stz   BANK              ; make sure we're running after UART download like
                           ; in filebrowser load

;   lda   #$ff
;   pha
   sei                     ; sorbus runs with sei as default, just to be sure
   lda   #<fail
   sta   UVBRK+0
   lda   #>fail
   sta   UVBRK+1

; future-proofing: set up audio for the time, modes are switchable
   lda   #$02              ; sound mode: SID
   sta   $D4FF

   ; clear out sound
   ldx   #$1d
:
   stz   $d400,x
   stz   $d420,x
   dex
   bpl   :-

; set up LEDs clear out and set up framebuffer
   lda   #<FRAMEBUFFER
   ldx   #>FRAMEBUFFER
   ldy   #$01
   int   FB32X32

; short message
   jsr   PRINT
   .byte 10,"Welcome to 1k of LEDs is no limit"
   .byte 10,"================================="
   .byte 10,"Music by Skyrunner"
   .byte 10,"Code by SvOlli"
   .byte 10,"Graphics by Titus, Veto and SvOlli"
   .byte 10,10,"SX4 from $0400-$",0
   lda   #<(__PADATA_LOAD__+__PADATA_SIZE__)
   ldx   #>(__PADATA_LOAD__+__PADATA_SIZE__)
   int   PRHEX16

   jsr   PRINT
   .byte 10,10,"Select mode:"
   .byte 10,"L)oop endless"
   .byte 10,"S)ingle run"
   .byte 10,"D)elayed single run"
   .byte 10,0

@keyloop:
   int   CHRINUC
   cmp   #$03
   bne   :+
   jmp   exit
:
   cmp   #'L'
   bne   :+
   lda   #FEAT_LOOP
   bra   @keydone
:
   cmp   #'D'
   bne   :+
   jsr   delay

   lda   #'S'
:
   cmp   #'S'
   bne   @keyloop
   lda   #FEAT_GURU

@keydone:
   pha

   jsr   PRINT
   .byte 10,10,"(Press Ctrl+C to quit)"
   .byte 10,0

; clear out zp
   ldx   #$08              ; first 8 bytes are special, no need to clear
:
   stz   $00,x             ; clear out zeropage
   inx
   bne   :-
   pla
   sta   features
   lda   #$ff
   sta   partindex

; set up vectors for irq and nmi
   ldy   #$03
:
   lda   ir_vec,y
   sta   UVNMI,y           ; UVNBI follows
   lda   ir_tim,y
   sta   TM_IMR,y          ; TM_NMR follows
   dey
   bpl   :-
   bra   firstswitch

scheduleloop:
   jsr   CHRIN
   cmp   #$03              ; Ctrl+C
   beq   exit

   lda   #$80
   trb   partindex
   beq   scheduleloop      ; wait for "next part" bit being set

firstswitch:
   sei                     ; switching part now

   ldx   #<(localramend-localram-1)
:
   stz   localram,x
   dex
   bpl   :-

   ;ldx   #$ff              ; already at $ff because of bpl
   stx   frmcnt+0          ; reset frame counter
   stx   frmcnt+1          ; reset frame counter
   inc   partindex
   ; reset subpartindex
   stz   subpartindex

   cli                     ; precalc of next part should be interruptable
   bra   scheduleloop

jsrtable:
   jmp   (calltable,x)

exit:
   ; clear interrupt timers
   sei
   ldx   #$04
:
   stz   TM_IMR-1,x
   dex
   bne   :-

   ; clear out sound
   ldx   #$1d
:
   stz   $d400,x
   stz   $d420,x
   dex
   bpl   :-

   ; clear out framebuffer
   lda   #<FRAMEBUFFER
   ldx   #>FRAMEBUFFER
   ldy   #$01
   int   FB32X32

   jsr   PRINT
   .byte 10,"IRQ count: $",0
   lda   irqcnt+0
   ldx   irqcnt+1
   int   PRHEX16
   lda   #' '
   jsr   CHROUT
   lda   irqcnt+0
   ldx   irqcnt+1
   int   PRDEC16

   jsr   PRINT
   .byte 10,"NMI count: $",0
   lda   nmicnt+0
   ldx   nmicnt+1
   int   PRHEX16
   lda   #' '
   jsr   CHROUT
   lda   nmicnt+0
   ldx   nmicnt+1
   int   PRDEC16

   jsr   PRINT
   .byte 10,"NMI expect:$",0
   ; multiply IRQ counter by 2.5
   lda   irqcnt+0
   sta   vector1+0
   sta   vector2+0
   lda   irqcnt+1
   sta   vector1+1
   sta   vector2+1

   lsr   vector1+1          ; div by 2
   ror   vector1+0
   asl   vector2+0          ; mul by 2
   rol   vector2+1

   clc                      ; add them
   lda   vector1+0
   adc   vector2+0
   sta   vector1+0
   lda   vector1+1
   adc   vector2+1
   sta   vector1+1

   lda   vector1+0
   ldx   vector1+1
   int   PRHEX16
   lda   #' '
   jsr   CHROUT
   lda   vector1+0
   ldx   vector1+1
   int   PRDEC16

   ; leave program through reset
   jmp   ($fffc)


subschedule:
   ; CAN be used by demoparts for branching due to frmcnt
   ; usage:
   ; subtable:
   ;    .byte $0000, branch1
   ;    .byte $0140, branch2
   ;    .byte $0200, branch3
   ; [...in routine...]
   ;    lda   #<subtable
   ;    ldx   #>subtable
   ;    jsr   subschedule ; or jmp
   sta   vector+0
   stx   vector+1

   ldy   subpartindex
   lda   frmcnt+0
   cmp   (vector),y
   bne   @nomatch
   iny
   lda   frmcnt+1
   cmp   (vector),y
   bne   @nomatch

   iny
   lda   (vector),y
   sta   @nomatch+1
   iny
   lda   (vector),y
   sta   @nomatch+2
   iny
   sty   subpartindex
@nomatch:
   jmp   $ffff

schedulenext:
   ; TSB  tests and sets bits in memory, using the accumulator for both
   ; a test mask, and a set or reset mask. First, a logical AND is
   ; performed between memory and the accumulator. The "Z" or "zero"
   ; flag is set if all bits of the result of the AND are zero.

   lda   #$80
   tsb   partindex
   beq   :+
   ; ERROR handling: a part was scheduled, before init was completed
   jsr   PRINT
   .byte 10,"schedulenext ",0
   lda   partindex
   int   PRHEX8
   stz   TRAP
   jmp   ($fffc)
:
noprecalc:
   rts

runprecalc:
   lda   #<CHRIN
   sta   scheduleloop+1
   lda   #>CHRIN
   sta   scheduleloop+2
precalccode:
   jsr   $ffff
   jmp   CHRIN

precalcstart:
   ; called from IRQ to start precalc in non-IRQ context
   ; IN:
   ; A/X = lo/hi of precalc start

   sta   precalccode+1
   stx   precalccode+2
   lda   #<runprecalc
   ldx   #>runprecalc
   sta   scheduleloop+1
   stx   scheduleloop+2
   rts

irqhandler:
   pha
   phx
   phy
   bit   TM_IMR            ; ack timer

   inc   irqcnt+0
   bne   :+
   inc   irqcnt+1
:

   inc   frmcnt+0
   bne   :+
   inc   frmcnt+1
:

   lda   partindex
   asl
   tax
   jsr   jsrtable

@irqdone:
.if FRAMECHECK
   bit   TM_IMR
   bpl   :+
   ; no IRQ should be pending
   jsr   PRINT
   .byte 10,"frame took too long",0
   stz   TRAP
:
.endif
   ply
   plx
   pla
   rti

nmihandler:
   pha
   phx
   phy
   inc   nmicnt+0
   bne   :+
   inc   nmicnt+1
:
.if NMI_DELAY = 200
   ; this routine only works with 50Hz (time delay of 200)
   sed
   lda   tm_frc            ; 100th of second
   clc
   adc   #$02
   sta   tm_frc

   lda   tm_sec            ; seconds
   adc   #$00
   cmp   #$60
   bcc   :+
   lda   #$00
:
   sta   tm_sec

   lda   tm_min            ; minutes
   adc   #$00
   cmp   #$60
   bcc   :+
   lda   #$00
:
   sta   tm_min

   lda   tm_hr             ; hours
   adc   #$00
   cmp   #$24
   bcc   :+
   lda   #$00
:
   sta   tm_hr
   cld
.endif

   lda   features
   bpl   :+
   jsr   music+3           ; play music
:

   bit   TM_NMR            ; ack timer
   ply
   plx
   pla
   rti

delay:
   jsr   PRINT
   .byte "Starting in: ",0

   lda   #<secirq
   sta   UVNBI+0
   lda   #>secirq
   sta   UVNBI+1
   lda   #<10000          ; IRQ every second
   sta   TM_IMR+0
   lda   #>10000
   sta   TM_IMR+1

   lda   #'9'
   cli
@loop:
   jsr   CHROUT
   pha
   lda   #FEAT_TICK
:
   trb   features
   beq   :-

   lda   #$08              ; backspace
   jsr   CHROUT
   pla

   dec
   cmp   #'0'
   bcs   @loop

   sei
   stz   TM_IMR+0
   stz   TM_IMR+1
   lda   #$0a
   jmp   CHROUT

secirq:
   pha
   lda   #FEAT_TICK
   tsb   features
   bit   TM_IMR
   pla
   rti
