; ===============================================================================

; S t o p W a t c h - Picoblaze application for the BASYS kit
;
; # The command below will create a BlockROM for the hardware design
VHDL "JTAG_Loader_ROM_form.vhd", "Display_demo_rom.vhd", "Display_demo_rom"
; # The commands below will download code directly to BROM via JTAG
; # Note! Will only works with Printerport JTAG or Xilinx USB JTAG prgrammer
; COE "Romfile.COE"
; EXEC "JTAG_LOADER.BAT"
; # Please comment out the commands above when not used

; ==============================================================================
; Port Definitions DSIO = both IN and OUT locations
Bcd0 DSIO 0 ; Least significant digit
Bcd1 DSIO 1 ; Least significant digit
Bcd2 DSIO 2 ; Least significant digit
Bcd3 DSIO 3 ; Least significant digit
Bcd_lo DSIO 4 ; Two least sign. digits
Bcd_hi DSIO 5 ; Two most sign. digits
Dp DSIO 6 ; Four decimal points
Timer_Scale DSOUT 7 ; Timer scale (1 = 0.01 sec)
; PPM_Scale DSIO 8 ; Time scale for PPM
Led DSIO 9 ; 8-Leds
; PPM_Leds_on DSIO 10
; PCM0 DSIO 11
; PCM1 DSIO 12
; PCM2 DSIO 13
; PCM3 DSIO 14
; PCM4 DSIO 15
; PCM5 DSIO 16
; PCM6 DSIO 17
; PCM7 DSIO 18
DIPSW_port DSIN 20 ; 8-Dip Switches
Buttons DSIN 21 ; 4-push buttons
; --------------------------------------------- Hardware multiplier ----------
A DSOUT 22
B DSOUT 23
AxB_LowByte DSIN 22
AxB_HighByte DSIN 23
; =============================================================================
; = Register usage
; =============================================================================
R0 EQU s0
R1
EQU s1
R2
EQU s2
R3
EQU s3
R4
EQU s4
R5
EQU s5
R6
EQU s6
R7
EQU s7
R8
EQU s8
; -----------------------------Special register use----------------------------
R_Button EQU s9
R_State
EQU sA
; -----------------------------Interrupt registers-----------------------------
; This registers should only be used in ISR or rutines call from ISR
IR0 EQU sB ; Reg used by Interrupt
IR1 EQU sC ; Reg used by Interrupt
IR2 EQU sD ; Reg used by Interrupt
; -----------------------------Not used in this program------------------------
Mode_reg EQU sF ; operating mode of processor

X_Running EQU %010 ; <- R_State.StopWatch Running
X_Lap EQU %100 ; <- R_State.StopWatch Lap display
S_IDLE EQU %001 ; <- R_State
S_RUNNING EQU %010 ; <- R_State
S_RUNNING_LAP EQU %110 ; <- R_State
S_STOPPED_LAP EQU %100 ; <- R_State
S_STOPPED EQU %000 ; <- R_State

Start_Stop EQU %010000 ; <- R_Button
Lap_Reset EQU %100000 ; <- R_Button
; ============================================================================
; = Scratch pad memory locations
; ============================================================================
Watch EQU 0 ; 1/100 of second
W_Sec EQU 1
W_Min EQU 2
W_Hour EQU 3
; ----------------------------------
StopWatch EQU 4 ; 1/100 of second
SW_Sec EQU 5
SW_Min EQU 6
SW_Hour EQU 7
; ----------------------------------
LapTime EQU 8 ; 1/100 of second
Lap_Sec EQU 9
Lap_Min EQU 10
Lap_Hour EQU 11
; ---------------------------------
Last_Buttons EQU 12

Half_sec EQU 14
; -----------------------------------------------------------------------------
; Initialise the system
;
Cold_Start: LOAD R0, 1
OUT R0, Timer_Scale
LOAD R0, $0B
OUT R0, Dp
LOAD R0, $58
STORE R0, Watch + 1
STORE R0, Watch + 2
EINT ; enable interrupt

; JUMP Demo_loop ;
; Start of the main program loop.
;
; ##############################################################################
IDLE:
LOAD R0, 0
STORE R0, StopWatch
STORE R0, StopWatch + 1
STORE R0, StopWatch + 2
STORE R0, StopWatch + 3
LOAD R_State, S_IDLE
AND R_Button, - ( Start_Stop | Lap_Reset ) + 1
IDLE1:
TEST R_Button, Start_Stop
JUMP NZ, RUNNING
CALL Main
JUMP IDLE1
; ------------------------------------------------------------------------------
RUNNING:
LOAD R_State, S_RUNNING
AND R_Button, - ( Start_Stop | Lap_Reset ) + 1
RUNNING1:
TEST R_Button, Start_Stop
JUMP NZ, STOPPED
TEST R_Button, Lap_Reset
JUMP NZ, RUNNING_LAP
CALL Main
JUMP RUNNING1
; ------------------------------------------------------------------------------
RUNNING_LAP:
FETCH R0, StopWatch ; Store the Lap time
STORE R0, LapTime
FETCH R0, StopWatch + 1
STORE R0, LapTime + 1
FETCH R0, StopWatch + 2
STORE R0, LapTime + 2
FETCH R0, StopWatch + 3
STORE R0, LapTime + 3
RUNNING_LAPx:
LOAD R_State, S_RUNNING_LAP
AND R_Button, - ( Start_Stop | Lap_Reset ) + 1
RUNNING_LAP1:
TEST R_Button, Start_Stop
JUMP NZ, STOPPED_LAP
TEST R_Button, Lap_Reset
JUMP NZ, RUNNING
CALL Main
JUMP RUNNING_LAP1
; ------------------------------------------------------------------------------
STOPPED_LAP:
LOAD R_State, S_STOPPED_LAP
AND R_Button, - ( Start_Stop | Lap_Reset ) + 1
STOPPED_LAP1:
TEST R_Button, Start_Stop
JUMP NZ, RUNNING_LAP
TEST R_Button, Lap_Reset
JUMP NZ, STOPPED
CALL Main
JUMP STOPPED_LAP1
; ------------------------------------------------------------------------------
STOPPED:
LOAD R_State, S_STOPPED
AND R_Button, - ( Start_Stop | Lap_Reset ) + 1
STOPPED1:
TEST R_Button, Start_Stop
JUMP NZ, RUNNING
TEST R_Button, Lap_Reset
JUMP NZ, IDLE
CALL Main
JUMP STOPPED1

; #############################################################################
; # M a i n must be called from every state
; #############################################################################
Main:
LOAD R4, R_State
OUT R4, Led
LOAD R4, LapTime
TEST R_State, X_Lap
JUMP NZ, Main2
LOAD R4, StopWatch
Main2:
IN R0, DIPSW_port
AND R0, 1
ADD R4, R0
CALL Display_Watch2
RET

; ==============================================================================
Demo_loop: IN R0, Buttons
TEST R0, $01
JUMP NZ, Label1

CALL Display_Watch
JUMP Label2
Label1:
CALL Multiplier_demo
Label2:
IN R0, DIPSW_port
OUT R0, Led
JUMP Demo_loop
; ==============================================================================
Multiplier_demo:
IN R4, DIPSW_port
AND R4, $0F
OUT R4, A ; A = Switch 3:0
OUT R4, Bcd0 ; Display at Bcd 0
IN R4, DIPSW_port
SR0 R4
SR0 R4
SR0 R4
SR0 R4
OUT R4, B ; A = Switch 7:4
OUT R4, Bcd1 ; Display at Bcd 1
IN R4, AxB_LowByte ; Get low byte of result
OUT R4, Bcd_Hi ; Display at Bcd 3 + Bcd 2
RET
; ==============================================================================
; Display of: h:m or m:s or s:1/100s
Display_Watch:
IN R4, DIPSW_port
Display_Watch2:
; R4 = Pointer to watch
AND R4, $0F
FETCH R0, ( R4 )
OUT R0, Bcd_lo
ADD R4, 1
FETCH R0, ( R4 )
OUT R0, Bcd_hi
RET
; ==============================================================================
; Mostly for debug and demonstraion
Display_ports:
IN R4, DIPSW_port
AND R4, $1F
IN R0, ( R4 )
OUT R0, Bcd_lo
ADD R4, 1
IN R0, ( R4 )
OUT R0, Bcd_hi
RET
; #############################################################################
; # Watch_function will call the Bcd_counter
; # IR1 = Address (Pointer) to the Watch memoryblok - 1/100, sec, min, hour
; #############################################################################
Watch_function:
LOAD IR2, $9A ; Max Value + 1 for 1/100 sec
CALL Bcd_counter ; Always call
LOAD IR2, $5A ; Max Value + 1 for sec and min
CALL Z, Bcd_counter ; call if Z=1
CALL Z, Bcd_counter ; call if Z=1
LOAD IR2, $24 ; Max Value + 1 for hour
CALL Z, Bcd_counter ; call if Z=1
RET
; #############################################################################
; # Bcd_counter:
; # IR1 = Address (Pointer) to the Watch memoryblok - 1/100, sec, min, hour
; # IR2 = Max BCD value
; # Output:
; # The memory byte will act as an BCD-counter like 19->20 and 59->00
; # if the BCD-counter had an overrun like 59->00 will Z=1 else Z=0
; # the IR1 pointer will increase by one (points to next position)
; #############################################################################
Bcd_counter:
FETCH IR0, ( IR1 )
ADD IR0, 1
COMP IR0, IR2
JUMP Z, Set2zero
STORE IR0, ( IR1 )
AND IR0, $0F
COMP IR0, 10
JUMP C, Digit_ok ; Digit below 10
FETCH IR0, ( IR1 )
ADD IR0, 6 ; Hex2Bcd adjustment $0A -> $10
JUMP Store_and_return
Set2Zero:
LOAD IR0, 0
Store_and_return:
STORE IR0, ( IR1 )
Digit_ok:
ADD IR1, 1 ; Point to next Address
COMP IR0, 0 ; Set Z=1 if Bcd counter = 0
RET
; == Called by interrupt =======================================================
; This rutine let Decimal Point 2 toggle every 0.5 sec
Half_Sec_Rutine:
TEST R_State, X_Running
JUMP Z, no_blink
IN IR1, Dp
XOR IR1, $04
OUT IR1, Dp
RET
no_blink:
LOAD IR1, $0F
OUT IR1, Dp
RET

; == Called by interrupt =======================================================
; This rutine will detect if any Buttons pressed since the last call
; Detections will be stored in Button_Reg together with the actual status
Scan_button_rutine:
IN IR0, Buttons ; Read New xxxx.3210 buttons
FETCH IR1, Last_Buttons ; Get the Last scan
STORE IR0, Last_Buttons ; Remember the xxxx.3210 for later
XOR IR1, $0F ; Negate bit 3..0
AND IR1, IR0 ; Pressed <= not Last and New
SL0 IR1
SL0 IR1
SL0 IR1
SL0 IR1
OR R_Button, IR1 ; Set if any pressed
AND R_Button, $F0
OR R_Button, IR0
RET

; == Called by interrupt ======= Interrupt service routine (ISR) ===============
ORG $3E0
ISR: LOAD IR1, StopWatch
TEST R_State, X_Running
CALL NZ, Watch_function ; If SW running call

LOAD IR1, Watch ; Normal watch adr.
CALL Watch_function ;
; ------------------------------------- Half second routine administration
FETCH IR0, Half_sec
SUB IR0, 1
JUMP NZ, Retur
CALL Half_Sec_Rutine
LOAD IR0, 50
Retur:
STORE IR0, Half_sec
AND IR0, 7
CALL Z, Scan_button_rutine
RETI ENABLE
;
; Interrupt vector
;
ORG $3FF
JUMP ISR
END

StopWatch1 (html)