                  ; KCPSM3 PicoBlaze DIGITAL-OSZILLOSKOP by cm 5/2009
                  ; Wave-RAM auslesen und anzeigen
                  ; Referenz-Triggerpunkt wird von wave_ram berechnet,
                  ; wir fangen immer bei Adresse 0 an
                  ; Port-Definitionen
                  ;Eingangsports
                  ; STATUS Bit 0 GDONE, Bit 1 FRAMEDONE, Bit 2 trig_done, Bit 3 trig_ok
                  CONSTANT in_status, 00
                  CONSTANT in_com_data, 01
                  CONSTANT in_com_sel, 02
                  CONSTANT in_scaled_y, 04
                  CONSTANT in_read_y1, 06
                  CONSTANT in_read_y2, 07
                  ;Ports in parser.vhd
                  CONSTANT out_adrlow, 10
                  CONSTANT out_adrhi, 11
                  CONSTANT out_data, 12
                  ;Ports extern
                  ; TRIGCTRL Bit 0..2 trig_mode, Bit 3 Reset, Bit 4 select_Ch2, Bit 5 COM_WR, Bit 6 BUSREQ
                  CONSTANT out_trigctrl, 40
                  CONSTANT out_com, 41
                  CONSTANT out_triglevel, 42
                  CONSTANT out_hystlevel, 43
                  CONSTANT out_fac, 44
                  CONSTANT out_sel_y, 45       ; 0=wave1, 1=wave2, 2=hyst_level, 3=trig_level auf Multiplier
                  CONSTANT out_wavelow, 46     ;Wave-RAM-Adresse
                  CONSTANT out_wavehi, 47
                  ;Grafik-Befehle
                  CONSTANT gcmd_xdata, 00
                  CONSTANT gcmd_ydata, 01
                  CONSTANT gcmd_move, 02
                  CONSTANT gcmd_point, 03
                  CONSTANT gcmd_line, 04
                  CONSTANT gcmd_rect, 05
                  CONSTANT gcmd_fill, 06
                  CONSTANT gcmd_small, 07
                  CONSTANT gcmd_xstart, 08
                  CONSTANT gcmd_ystart, 09
                  CONSTANT gcmd_xsize, 0A
                  CONSTANT gcmd_ysize, 0B
                  CONSTANT gcmd_xgrid, 0C
                  CONSTANT gcmd_ygrid, 0D
                  CONSTANT gcmd_frameEna, 0E
                  CONSTANT gcmd_color, 0F
                  CONSTANT gcmd_xtrig, 20
                  CONSTANT gcmd_ytrig, 21
                  ;Scratchpad Memory, Interface to t65 Core
                  ; Trigger-Modus mem_trigmode:
                  ; 0: Nicht auf Trigger warten
                  ; 1: analog, positive Flanke adc_wert > trig_level
                  ; 2: analog, negative Flanke adc_wert < hyst_level
                  ; 3: exakter digitaler Wert  adc_wert = trig_level
                  ; 4: positive Bit-Flanke, Maske in trig_level TBI!
                  ; 5: negative Bit-Flanke, Maske in trig_level TBI!
                  ; Bit 3 ist Trigger-Reset, Bit 4 ist Channel Select (0=CH1, 1=CH2)
                  CONSTANT mem_cmd, 00         ; ct-BASIC POKE 960,xx ($3C0)
                  CONSTANT mem_trigmode, 01    ; ct-BASIC POKE 961,xx ($3C1)
                  CONSTANT mem_triglevel, 02   ; ct-BASIC POKE 962,xx ($3C2)
                  CONSTANT mem_hystlevel, 03   ; ct-BASIC POKE 963,xx ($3C3)
                  CONSTANT mem_y1base, 04      ; ct-BASIC POKE 964,xx ($3C4)
                  CONSTANT mem_y2base, 05      ; ct-BASIC POKE 965,xx ($3C5)
                  CONSTANT mem_offsetlow, 06   ; ct-BASIC POKE 966,xx ($3C6)
                  CONSTANT mem_offsethi, 07    ; ct-BASIC POKE 967,xx ($3C7)
                  CONSTANT mem_display, 08     ; ct-BASIC POKE 968,xx ($3C8)
                  CONSTANT mem_y1scale, 0A     ; ct-BASIC POKE 970,xx ($3CA)
                  CONSTANT mem_y2scale, 0B     ; ct-BASIC POKE 971,xx ($3CB)
                  CONSTANT mem_y1mid, 0C       ; ct-BASIC POKE 970,xx ($3CA)
                  CONSTANT mem_y2mid, 0D       ; ct-BASIC POKE 971,xx ($3CB)
                  ;Schnelle Register
                  ; s0 temporär nur innerhalb einer Subroutine
                  ; s3 und s4 reserviert für IRQ-Service
                  NAMEREG s3, isema
                  NAMEREG s4, itemp
                  NAMEREG s5, y1base
                  NAMEREG s6, y2base
                  NAMEREG s7, running
                  NAMEREG s8, x
                  NAMEREG s9, y
                  NAMEREG sA, y1
                  NAMEREG sB, y2
                  NAMEREG sC, acc              ; meist Übergabe-Parameter
                  NAMEREG sD, mode             ; Plot-Mode
                  NAMEREG sE, color            ; Plot-Color
                  NAMEREG sF, mask             ; für Logic-Darstellung
                  ;
                  ;=========================================================================
                  ;Hauptschleife
                  ;=========================================================================
      cold_start: 
                  DISABLE INTERRUPT
                  LOAD isema, 00
                  LOAD running, 00
                  CALL init
                  LOAD s0, 00
                  STORE s0, mem_trigmode
                  STORE s0, mem_cmd
                  ENABLE INTERRUPT
      warm_start: 
                  CALL disable_trigger
                  CALL setlevels
                  CALL refresh
                  COMPARE isema, 00
                  JUMP Z, warm_start
                  LOAD isema, 00
                  FETCH s0, mem_trigmode
                  ;AND s0, 0F
                  COMPARE s0, 00               ; Trigger aus?
                  JUMP Z, warm_start
       cont_loop: 
                  CALL setlevels
                  CALL reset_trigger
                  CALL aquire
                  CALL disable_trigger
                  CALL refresh
                  FETCH s0, mem_trigmode
                  AND s0, 80                   ; Repeat-Bit in mem_trigmode gesetzt?
                  JUMP NZ, cont_loop
                  LOAD isema, 00
                  JUMP warm_start
          aquire: 
                  COMPARE running, 40          ; running-Flag evt. durch PS2-Event gelöscht?
                  RETURN NZ
                  INPUT s0, in_status          ; Trigger DONE true abwarten
                  TEST s0, 04
                  JUMP Z, aquire
                  RETURN
       setlevels: 
                  FETCH acc, mem_y1base
                  LOAD y1base, 00
                  SUB acc, y1base
                  LOAD y1base, acc
                  FETCH acc, mem_y2base
                  LOAD y2base, 00
                  SUB acc, y2base
                  LOAD y2base, acc
                  FETCH acc, mem_hystlevel
                  OUTPUT acc, out_hystlevel
                  FETCH acc, mem_triglevel
                  OUTPUT acc, out_triglevel
                  ; neue Mittellinie berechnen
                  FETCH acc, mem_y1scale       ; Mittellinie = halber Skalenendwert
                  SR0 acc
                  LOAD s0, 60                  ; Endwert Screen $C0/2
                  SUB s0, acc
                  FETCH acc, mem_y1base        ; Mittellinie verschieben
                  ADD acc, A0                  ; Korrekturwert $60=96=Mitte
                  ADD s0, acc
                  STORE s0, mem_y1mid
                  FETCH acc, mem_y2scale
                  SR0 acc
                  LOAD s0, 60                  ; Endwert Screen $C0/2
                  SUB s0, acc
                  FETCH acc, mem_y2base        ; Mittellinie verschieben
                  ADD acc, A0
                  ADD s0, acc
                  STORE s0, mem_y2mid
                  CALL set_trigline
                  LOAD acc, 80
                  OUTPUT acc, gcmd_xtrig
                  FETCH s0, mem_display
                  COMPARE s0, 00
                  JUMP NZ, frameOn
                  OUTPUT s0, gcmd_frameEna
                  RETURN
         frameOn: 
                  LOAD s0, 07
                  OUTPUT s0, gcmd_frameEna
                  RETURN
                  ;=========================================================================
                  ;Screen-Hilfsroutinen
                  ;=========================================================================
     setReadAddr: 
                  LOAD s0, 00
                  FETCH acc, mem_offsetlow
                  ADD acc, x
                  OUTPUT acc, out_wavelow
                  FETCH acc, mem_offsethi
                  ADDCY acc, s0
                  OUTPUT acc, out_wavehi
                  RETURN
    set_trigline: 
                  ; Position der Trigger-Hilslinie berechnen inkl. yNbase
                  LOAD acc, 02                 ; trig_level Multiplier select
                  OUTPUT acc, out_sel_y
                  FETCH acc, mem_trigmode      ; welcher Kanal?
                  TEST acc, 10                 ; Bit 4 übrig lassen, Channel select im Z-Flag
                  JUMP NZ, set_trigline2       ; wenn CH 2
   set_trigline1: 
                  FETCH acc, mem_y1scale
                  OUTPUT acc, out_fac
                  TEST acc, 00                 ; NOP dummy, Zeit für Multiplier
                  INPUT y1, in_scaled_y
                  CALL y1offset
                  OUTPUT y, gcmd_ytrig
                  RETURN
   set_trigline2: 
                  ; Position der Trigger-Hilslinie berechnen inkl. y2base
                  FETCH acc, mem_y2scale
                  OUTPUT acc, out_fac
                  TEST acc, 00                 ; NOP dummy, Zeit für Multiplier
                  INPUT y2, in_scaled_y
                  CALL y2offset
                  OUTPUT y, gcmd_ytrig
                  RETURN
        y1offset: 
                  ; skaliertes Byte y1 negieren und mit Offset versehen, Ergebnis in y
                  FETCH s0, mem_y1mid
                  ADD y1, s0
                  LOAD y, BF                   ; von 192 abziehen (Nullpunkt oben), y max=191=$BF
                  SUB y, y1                    ; Wert in y jetzt zwischen 0 (oben) und 191 (unten)
                  RETURN NC
                  ; wenn Carry gesetzt, ist Limit erreicht
                  COMPARE s0, 80               ; ist noch mem_y1mid
                  JUMP NC, ysetlower
                  LOAD y, 00
                  RETURN
        y2offset: 
                  ; skaliertes Byte y2 negieren und mit Offset versehen, Ergebnis in y
                  FETCH s0, mem_y2mid
                  ADD y2, s0
                  LOAD y, BF                   ; von 192 abziehen (Nullpunkt oben), y max=191=$BF
                  SUB y, y2
                  RETURN NC
                  ; wenn Carry gesetzt, ist Limit erreicht
                  COMPARE s0, 80               ; ist noch mem_y2mid
                  JUMP NC, ysetlower
                  LOAD y, 00
                  RETURN
       ysetlower: 
                  LOAD y, BF
                  RETURN
                  ;=========================================================================
                  ;1 oder 2 Kanäle darstellen, je nach mem_display-Inhalt
                  ;=========================================================================
         refresh: 
                  CALL wait_framedone
                  CALL gclear
                  FETCH s0, mem_display
                  AND s0, 01
                  CALL NZ, y1display
                  FETCH s0, mem_display
                  AND s0, 02
                  CALL NZ, y2display
                  FETCH s0, mem_display
                  AND s0, 04
                  CALL NZ, y1logic
                  RETURN
                  ;=========================================================================
                  ;Samples von Kanal 1 als Logiksignale darstellen, 256 Bytes
                  ;=========================================================================
         y1logic: 
                  LOAD x, 00
                  CALL setReadAddr
                  LOAD color, 03
                  OUTPUT color, gcmd_color
                  LOAD mask, 01
                  LOAD y1base, 08
y1logictraceloop: 
                  LOAD x, 00
                  LOAD y, y1base
                  INPUT y1, in_read_y1
                  AND y1, mask
                  JUMP NZ, y1logic0prep
                  ADD y, 08
    y1logic0prep: 
                  LOAD mode, gcmd_move         ; Startpunkt links
                  CALL plotxy
                  LOAD mode, gcmd_point
     y1logicloop: 
                  CALL setReadAddr
                  LOAD y, y1base
                  LOAD color, 03
                  OUTPUT color, gcmd_color
                  INPUT y1, in_read_y1
                  AND y1, mask
                  JUMP NZ, y1logic0
                  LOAD color, 01
                  OUTPUT color, gcmd_color
                  ADD y, 04
        y1logic0: 
                  CALL plotxy
                  ADD x, 01
                  COMPARE x, FF
                  JUMP NZ, y1logicloop
                  SL0 mask
                  ADD y1base, 10
                  COMPARE y1base, 88
                  JUMP NZ, y1logictraceloop
                  RETURN
                  ;=========================================================================
                  ;Samples von Kanal 1 analog darstellen, 256 Bytes
                  ;=========================================================================
       y1display: 
                  LOAD y, BF
                  FETCH acc, mem_y1base
                  SUB y, acc
                  LOAD mode, gcmd_point        ; Startpunkt links
                  LOAD x, 00
                  LOAD color, 02
                  OUTPUT color, gcmd_color
       y1midline: 
                  CALL plotxy
                  ADD x, 04
                  COMPARE x, 00
                  JUMP NZ, y1midline
                  LOAD acc, 00
                  OUTPUT acc, out_sel_y        ; Multiplexer auf Kanal 1 setzen
                  FETCH acc, mem_y1scale       ; Hardware-Multiplier über Multiplexer
                  OUTPUT acc, out_fac
                  LOAD x, 00
                  CALL setReadAddr
                  INPUT y1, in_scaled_y
                  CALL y1offset
                  LOAD mode, gcmd_move         ; Startpunkt links
                  CALL plotxy
                  LOAD mode, gcmd_line
   y1refreshloop: 
                  ADD x, 01
                  CALL setReadAddr
                  INPUT y1, in_scaled_y
                  CALL y1offset
                  CALL plotxy
                  COMPARE x, FF
                  JUMP NZ, y1refreshloop
                  RETURN
                  ;=========================================================================
                  ;Samples von Kanal 2 analog darstellen, 256 Bytes
                  ;=========================================================================
       y2display: 
                  LOAD y, BF
                  FETCH acc, mem_y2base
                  SUB y, acc
                  LOAD mode, gcmd_point        ; Startpunkt links
                  LOAD x, 02
                  LOAD color, 01
                  OUTPUT color, gcmd_color
       y2midline: 
                  CALL plotxy
                  ADD x, 04
                  COMPARE x, 02
                  JUMP NZ, y2midline
                  LOAD acc, 01
                  OUTPUT acc, out_sel_y        ; Multiplexer auf Kanal 2 setzen
                  FETCH acc, mem_y2scale       ; Hardware-Multiplier über Multiplexer
                  OUTPUT acc, out_fac
                  LOAD x, 00
                  CALL setReadAddr
                  INPUT y2, in_scaled_y
                  CALL y2offset
                  LOAD mode, gcmd_move         ; Startpunkt links
                  CALL plotxy
                  LOAD mode, gcmd_line
   y2refreshloop: 
                  ADD x, 01
                  CALL setReadAddr
                  INPUT y2, in_scaled_y
                  CALL y2offset
                  CALL plotxy
                  COMPARE x, FF
                  JUMP NZ, y2refreshloop
                  RETURN
                  ;=========================================================================
                  ;Grafik- und Text-Subroutinen
                  ;=========================================================================
          plotxy: 
                  ;plot at point x,y with mode (MOVE, POINT, LINE etc).
                  INPUT s0, in_status          ; letzter Befehl fertig?
                  TEST s0, 01
                  JUMP Z, plotxy
                  OUTPUT x, gcmd_xdata
                  OUTPUT y, gcmd_ydata
                  LOAD s0, 00
                  OUTPUT s0, (mode)            ; Befehl triggern
                  RETURN
                  ;=========================================================================
                  ;Steuerungs-Subroutinen
                  ;=========================================================================
                  ; STATUS Bit 0 GDONE, Bit 1 FRAMEDONE, Bit 2 trig_done, Bit 3 trig_ok
                  ; Ende der Frame-Anzeige abwarten (Flicker!)
  wait_framedone: 
                  INPUT s0, in_status          ; FRAMEDONE false abwarten
                  TEST s0, 02
                  JUMP NZ, wait_framedone
     frame_done2: 
                  INPUT s0, in_status          ; FRAMEDONE true abwarten
                  TEST s0, 02
                  JUMP Z, frame_done2
                  RETURN
          sdelay: 
                  LOAD s0, FF
     sdelay_loop: 
                  SUB s0, 01
                  JUMP NZ, sdelay_loop
                  RETURN
wait_key_release: 
                  INPUT s0, in_status          ; Key losgelassen abwarten
                  TEST s0, 08
                  JUMP NZ, wait_key_release
                  RETURN
                  ; Trigger-Modus (trig_mode):
                  ; 0: Nicht auf Trigger warten
                  ; 1: analog, positive Flanke
                  ; 2: analog, negative Flanke
                  ; 3: exakter digitaler Wert adc_wert = trig_level
                  ; 4: positive Bit-Flanke, Maske in trig_level
                  ; 5: negative Bit-Flanke, Maske in trig_level
                  ; +16 dez: Select Channel 2 (sonst 1)
                  ; TRIG_CTRL Bit 0..2 trig_mode, Bit 3 Reset, Bit 4 select_Ch2, Bit 5 COM_WR, Bit 6 BUSREQ
 disable_trigger: 
                  LOAD running, 00
                  FETCH s0, mem_trigmode       ; trig_reset
                  AND s0, 07                   ; 0 bis 2 übrig lassen
                  JUMP reset_trigger1
   reset_trigger: 
                  LOAD running, 40             ; wird Bit 6 von trig_ctrl BUSREQ
                  LOAD s0, 08                  ; trig_reset
                  FETCH acc, mem_trigmode      ; trig_reset
                  AND acc, 17                  ; Bit 4  und 0 bis 2 übrig lassen
                  OR s0, acc
                  OR s0, running               ; Bit 6 von trig_ctrl BUSREQ setzen
  reset_trigger1: 
                  OUTPUT s0, out_trigctrl
                  AND s0, 17                   ; Bit 4 (trig_ch)  und 0 bis 2 übrig lassen
                  OUTPUT s0, out_trigctrl
                  RETURN
            init: CALL sdelay
                  LOAD s0, 80
                  STORE s0, mem_triglevel
                  STORE s0, mem_y1base
                  STORE s0, mem_y2base
                  LOAD s0, 60
                  STORE s0, mem_hystlevel
                  LOAD s0, C0                  ; Screen-Höhe 192 Pixel dez.
                  STORE s0, mem_y1scale
                  STORE s0, mem_y2scale
                  LOAD s0, 10
                  OUTPUT s0, gcmd_xgrid
                  OUTPUT s0, gcmd_ygrid
                  LOAD s0, 36
                  OUTPUT s0, gcmd_xstart
                  LOAD s0, 24
                  OUTPUT s0, gcmd_ystart
                  LOAD s0, 80
                  OUTPUT s0, gcmd_xsize
                  LOAD s0, 60
                  OUTPUT s0, gcmd_ysize
                  LOAD s0, 00
                  OUTPUT acc, gcmd_frameEna
                  LOAD s0, 00                  ; Anzeige abschalten
                  STORE s0, mem_display
          gclear: ; Grafikfenster löschen: Rechteck mit Farbe 0 zeichnen
                  LOAD x, 00
                  LOAD y, 00
                  LOAD mode, gcmd_move
                  CALL plotxy
                  LOAD s0, 00
                  OUTPUT s0, gcmd_color
                  LOAD x, FF
                  LOAD y, BF
                  LOAD mode, gcmd_fill
                  CALL plotxy
     gclear_wait: 
                  INPUT s0, in_status          ; letzter Befehl fertig?
                  TEST s0, 01
                  JUMP Z, gclear_wait
                  RETURN
      IRQservice: 
                  ; nach Tastendruck-IRQ BUSREQ sofort wieder freigeben, Running-Flag löschen
                  LOAD running, 00
                  OUTPUT running, out_trigctrl
                  ; eingehende Daten in Scratchpad Memory (erste 16 Bytes) ablegen
                  INPUT itemp, in_com_sel
                  INPUT isema, in_com_data
                  AND itemp, 0F
                  STORE isema, (itemp)
                  FETCH itemp, mem_cmd         ; Registerinhalt ausgeben
                  FETCH isema, (itemp)
                  OUTPUT isema, out_com
                  LOAD isema, FF
                  RETURNI ENABLE
                  ADDRESS 3FF
                  JUMP IRQservice
