When writing C# programs with Visual Studio you have a very nice debugger at hand. When writing assembly/C/C++ code for microchip's micro-controllers, you can use MPLAB Integrated Development Environment. If you have a programmer with debug support you can debug in-circuit. If you don't have such a board but a simple board like Velleman's K8076/VM134, you still can use MLAB because it has a good simulator feature. But if you don't have MPLAB or for whatever reason you need to 'manually' debug your program you can use the approach described in this blog. N.B.: If your device is part of a complex system with lots of high frequency input, then I would advice you to buy a programmer with debugging support. For more simple systems like the LCD-driver I wrote, you can use 'print-statements'. You can read the data and display it on one of the micro-controller ports connected to leds. See schema below for an example. Port B of the PIC16F628A is used for displaying debug information.
The added section reads the value LCD_TMP into register W and puts the result on port B (RB7...RB0) and then starts an endless loop.
I also change the interrupt handler for timer 0 overflows. Because this handler changes the value of port B, it would ruin my captured LCD_TMP state, so I just let it return immediately.
In order to show more than one byte of information (e.g. 32-bits value) you can show the information in 4 groups of 8-bits. To indicate which byte is shown, all 8 leds blink the n-times, where n = byte number. In the function below, the number of blinks is stored in DLY_VAR.
I'm aware that it is a poor man's solution. But it did help me in finding some bugs in my program that counts from 2147483648...0 and displays the value on an LCD-module. It uses 32-bits calculations: a divided by b, a times b and a minus b.
#include <E:\Elec\PicProg2009\Include\P16F628A.INC>
__CONFIG(_HS_OSC & _WDT_OFF & _MCLRE_OFF & _BOREN_OFF & _LVP_OFF & _DATA_CP_OFF & _CP_OFF);
org 0x0000
goto MAIN_INIT
org 0x0004
goto MY_INTERRUPT_HANDLER
;N.B. all sub routines must return with BANK 0 selected
;N.B. lower and higher nibbles are LCD nibbles, but they are stored in two bytes because bytes also contain status of LCD_E and LCD_RS
;--- MY_BITS ---
#define LCDE 0 ;LCD Enable bit
#define WRT 1 ;Ready for writing bit
#define SKF 2 ;Skip frame bit
#define U4B 3 ;Write upper four bits (high nibble)
#define OVERFLOW 4 ;Math overflow
#define DIV0 5 ;Division by zero
;PORTB wiring
#define RB0 0 ;RB0 => RS is wired to RB0
#define RB1 1 ;RB1 => E is wired to RB1
#define RB3 3 ;RB3 => LED is wired to RB3
;binary constants
#define b00000000 0x00
#define b00000001 0x01
#define b00000011 0x03
#define b00000101 0x05
#define b00001010 0x0A
#define b00001111 0x0f
#define b00010000 0x10
#define b00011000 0x18
#define b00011111 0x1F
#define b00100000 0x20
#define b00100111 0x27
#define b00111011 0x3B
#define b01000000 0x40
#define b01000010 0x42
#define b01010101 0x55
#define b01100000 0x60
#define b01100100 0x64
#define b01110000 0x70
#define b10000000 0x80
#define b10000110 0x86
#define b10010110 0x96
#define b10011000 0x98
#define b10011010 0x9A
#define b10100000 0xA0
#define b10101010 0xAA
#define b11001010 0xCA
#define b11000000 0xC0
#define b11100001 0xE1
#define b11101000 0xE8
#define b11110000 0xF0
#define b11110101 0xF5
#define b11111111 0xFF
;Timer constants
#define TMR_DELTA 0xF0 ;start at: 255 - x, overflow on 255+1
;RAM data variables bank 0
cblock 0x20
MY_BITS
W_TEMP
STATUS_TEMP
LCD_BUF_00
LCD_BUF_01
LCD_BUF_02
LCD_BUF_03
LCD_BUF_04
LCD_BUF_05
LCD_BUF_06
LCD_BUF_07
CNT_VAR
DLY_VAR
CNT0
CNT1
CNT2
CNT3
CNT_RW ;number of items in LCD_BUF
LCD_TMP ;buffer for tmp lcd data
MATH_A3 ;MATH_A3...0 32 bits unsigned int 'A'
MATH_A2
MATH_A1
MATH_A0
MATH_B3 ;MATH_B3...0 32 bits unsigned int 'B'
MATH_B2
MATH_B1
MATH_B0
MATH_TMP3 ;MATH_TMP3...0 32 bits unsigned int 'TMP'
MATH_TMP2
MATH_TMP1
MATH_TMP0
MATH_RES3 ;MATH_R3...0 32 bits unsigned int 'RESULT'
MATH_RES2
MATH_RES1
MATH_RES0
MATH_REM3 ;MATH_REM3...0 32 bits unsigned int 'MATH_REM'
MATH_REM2
MATH_REM1
MATH_REM0
CAP_MES3 ;CAP_MES3...0 32 bits unsigned int 'CAP_MES3'
CAP_MES2
CAP_MES1
CAP_MES0
LCD_DIV3 ;LCD_DIV3..0 32 bits unsigned int 'LCD_DIV'
LCD_DIV2
LCD_DIV1
LCD_DIV0
endc;
MAIN_INIT:
;--- select bank 0 ---
bcf STATUS, RP0;
bcf STATUS, RP1;
clrf PORTA;
clrf PORTB;
clrf MY_BITS
clrf W_TEMP
clrf STATUS_TEMP
clrf CNT_RW
clrf LCD_BUF_00
clrf LCD_BUF_01
clrf LCD_BUF_02
clrf LCD_BUF_03
clrf LCD_BUF_04
clrf LCD_BUF_05
clrf LCD_BUF_06
clrf LCD_BUF_07
clrf LCD_TMP
clrf DLY_VAR
clrf CNT0
clrf CNT1
clrf CNT2
clrf CNT3
;--- select bank 1 ---
bsf STATUS, RP0
;disable all interrupts
clrf INTCON ;GIE PEIE T0IE INTE RBIE T0IF INTF RBIF
clrf PIE1 ;EEIE CMIE RCIE TXIE xxx CCP1IE TMR2IE TMR1IE
movlw b11000000 ;RA0...RA5 as outputs
movwf TRISA ;TRISA, bank 1
movlw b00000000 ;RB0...RB7 as outputs
movwf TRISB ;TRISB, bank 1
;--- select bank 0 ---
bcf STATUS, RP0 ;
clrf PIR1 ;EEIF CMIF RCIF TXIF xxx CCP1IF TMR2IF TMR1IF
call start_lcd_timer
;RS => is wired to RB0
;E => is wired to RB1
;Wait for more than 30ms
;after VDD rises to 4.5v
movlw 0x80 ;wait > 30 msec;
movwf DLY_VAR ;DLY_VAR bank 0
call delay_msec ;
;N: 0 = 1-line mode , 1 = 2-line mode
;F: 0 = 5 x 7 dots, 1 = 5 x 10 dots
;
;RS RW DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0
;0 0 0 0 1 0 X X X X
;0 0 0 0 1 0 X X X X
;0 0 N F X X X X X X
call init_write_mode;
movlw b00100000 ;
movwf LCD_BUF_00 ;move w to LCD_BUFxx bank 0
incf CNT_RW, 1 ;RW index bank 0, write result back in CNT_RW
movlw b00100000 ;
movwf LCD_BUF_01 ;move w to LCD_BUFxx bank 0
incf CNT_RW, 1 ;RW index bank 0, write result back in CNT_RW
movlw b01000000 ;
movwf LCD_BUF_02 ;move w to LCD_BUFxx bank 0
incf CNT_RW, 1 ;RW index bank 0, write result back in CNT_RW
movlw b01000000 ;
movwf LCD_BUF_03 ;move w to LCD_BUFxx bank 0
incf CNT_RW, 1 ;RW index bank 0, write result back in CNT_RW
bsf MY_BITS, WRT ;ready for writing data bank 0
movlw 0x80 ;wait >100 msec
movwf DLY_VAR ;DLY_VAR bank 0
call delay_msec ;
;D: 0 display off, 1 display on
;C: 0 cursor off, 1 cursor on
;B: 0 blink off, 1 blink on
;RS RW DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0
;0 0 0 0 0 0 X X X X
;0 0 1 D C B X X X X
call init_write_mode;
movlw b00000000 ;
movwf LCD_BUF_00 ;move w to LCD_BUFxx bank 0
incf CNT_RW, 1 ;RW index bank 0, write result back in CNT_RW
movlw b11000000 ;
movwf LCD_BUF_01 ;move w to LCD_BUFxx bank 0
incf CNT_RW, 1 ;RW index bank 0, write result back in CNT_RW
bsf MY_BITS, WRT ;ready for writing data bank 0
movlw 0x80 ;wait >50 msec
movwf DLY_VAR ;move w to DLY_VAR bank 0
call delay_msec;
;Clear Display
;RS RW DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0
;0 0 0 0 0 0 X X X X
;0 0 0 0 0 1 X X X X
call init_write_mode;
movlw b00000000 ;
movwf LCD_BUF_00 ;move w to LCD_BUFxx bank 0
incf CNT_RW, 1 ;RW index bank 0, write result back in CNT_RW
movlw b00010000 ;
movwf LCD_BUF_01 ;move w to LCD_BUFxx bank 0
incf CNT_RW, 1 ;RW index bank 0, write result back in CNT_RW
bsf MY_BITS, WRT ;ready for writing data bank 0
movlw 0x80 ;wait >50 msec
movwf DLY_VAR ;move w to DLY_VAR bank 0
call delay_msec ;
;I/D: 0 decrement mode, 1 increment mode
;SH: 0 entire shift off, 1 entire shift on
;RS RW DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0
;0 0 0 0 0 0 X X X X
;0 0 0 1 I/D SH X X X X
call init_write_mode;
movlw b00000000 ;
movwf LCD_BUF_00 ;move w to LCD_BUFxx bank 0
incf CNT_RW, 1 ;RW index bank 0, write result back in CNT_RW
movlw b01100000 ;
movwf LCD_BUF_01 ;move w to LCD_BUFxx bank 0
incf CNT_RW, 1 ;RW index bank 0, write result back in CNT_RW
bsf MY_BITS, WRT ;ready for writing data bank 0
movlw 0x80 ;wait >100 msec
movwf DLY_VAR ;move w to DLY_VAR bank 0
call delay_msec;
MAIN_LOOP:
;original measurement
;2147483648 = 0x80000000 = b10000000000000000000000000000000 = 2^32 / 2
movlw b10000000
movwf CAP_MES3
movlw b00000000
movwf CAP_MES2
movlw b00000000
movwf CAP_MES1
movlw b00000000
movwf CAP_MES0
MAIN_COUNT_DOWN:
movlw 0x00 ;set LCD position to 0
movwf LCD_TMP ;move w to LCD_TMP bank 0
call set_position
movf CAP_MES3, 0 ;copy 'CAP_MES' to 'MATH_REM'
movwf MATH_REM3
movf CAP_MES2, 0;
movwf MATH_REM2
movf CAP_MES1, 0;
movwf MATH_REM1
movf CAP_MES0, 0;
movwf MATH_REM0
;1000000000 = 0x3B9ACA00 = b00111011100110101100101000000000 = 10^9
movlw b00111011
movwf LCD_DIV3
movlw b10011010
movwf LCD_DIV2
movlw b11001010
movwf LCD_DIV1
movlw b00000000
movwf LCD_DIV0
call calc_char_n
;100000000 = 0x5F5E100 = b00000101 11110101 11100001 00000000 = 10^8
movlw b00000101
movwf LCD_DIV3
movlw b11110101
movwf LCD_DIV2
movlw b11100001
movwf LCD_DIV1
movlw b00000000
movwf LCD_DIV0
call calc_char_n
;100000000 = 0x989680 = b00000000 10011000 10010110 10000000 = 10^7
movlw b00000000
movwf LCD_DIV3
movlw b10011000
movwf LCD_DIV2
movlw b10010110
movwf LCD_DIV1
movlw b10000000
movwf LCD_DIV0
call calc_char_n
;1000000 = 0xF4240 = b00000000 00001111 01000010 01000000 = 10^6
movlw b00000000
movwf LCD_DIV3
movlw b00001111
movwf LCD_DIV2
movlw b01000010
movwf LCD_DIV1
movlw b01000000
movwf LCD_DIV0
call calc_char_n
;call debug_show_x
;100000 = 186A0 = b00000001 10000110 10100000 = 10^5
movlw b00000000
movwf LCD_DIV3
movlw b00000001
movwf LCD_DIV2
movlw b10000110
movwf LCD_DIV1
movlw b10100000
movwf LCD_DIV0
call calc_char_n
;10000 = 0x2710 = b00000000 00000000 00100111 00010000 = 10^4
movlw b00000000
movwf LCD_DIV3
movlw b00000000
movwf LCD_DIV2
movlw b00100111
movwf LCD_DIV1
movlw b00010000
movwf LCD_DIV0
call calc_char_n
;1000 = 0x3E8 = b00000000 00000000 00000011 11101000 = 10^3
movlw b00000000
movwf LCD_DIV3
movlw b00000000
movwf LCD_DIV2
movlw b00000011
movwf LCD_DIV1
movlw b11101000
movwf LCD_DIV0
call calc_char_n
;100 = 0x64 = b00000000 00000000 00000000 01100100 = 10^2
movlw b00000000
movwf LCD_DIV3
movlw b00000000
movwf LCD_DIV2
movlw b00000000
movwf LCD_DIV1
movlw b01100100
movwf LCD_DIV0
call calc_char_n
;10 = 0x0a = b00000000 00000000 00000000 00001010 = 10^1
movlw b00000000
movwf LCD_DIV3
movlw b00000000
movwf LCD_DIV2
movlw b00000000
movwf LCD_DIV1
movlw b00001010
movwf LCD_DIV0
call calc_char_n
;1 = 0x01 = b00000000 00000000 00000000 00000001 = 10^0
movlw b00000000
movwf LCD_DIV3
movlw b00000000
movwf LCD_DIV2
movlw b00000000
movwf LCD_DIV1
movlw b00000001
movwf LCD_DIV0
call calc_char_n
movf CAP_MES3, 0 ;copy 'CAP_MES' to 'MATH_A'
movwf MATH_A3
movf CAP_MES2, 0;
movwf MATH_A2
movf CAP_MES1, 0;
movwf MATH_A1
movf CAP_MES0, 0;
movwf MATH_A0
movlw b00000000
movwf MATH_B3
movlw b00000000
movwf MATH_B2
movlw b00000000
movwf MATH_B1
movlw b00000001
movwf MATH_B0
call calc_a_minus_b
movf MATH_RES3, 0 ;copy 'MATH_RES' to 'CAP_MES'
movwf CAP_MES3
movf MATH_RES2, 0;
movwf CAP_MES2
movf MATH_RES1, 0;
movwf CAP_MES1
movf MATH_RES0, 0;
movwf CAP_MES0
goto MAIN_COUNT_DOWN
;--- end MAIN_LOOP ---
;--- copy 'A' to 'TMP' and clear result
copy_a_to_tmp_and_clear_result:
movf MATH_A3, 0 ;copy 'A' to 'TMP'
movwf MATH_TMP3
movf MATH_A2, 0
movwf MATH_TMP2
movf MATH_A1, 0
movwf MATH_TMP1
movf MATH_A0, 0
movwf MATH_TMP0
movlw b00000000
movwf MATH_RES0 ;clear 'RES'
movwf MATH_RES1
movwf MATH_RES2
movwf MATH_RES3
return
;--- endd copy 'A' to 'TMP' and clear result
;--- calc_a_minus_b ---
calc_a_minus_b:
call copy_a_to_tmp_and_clear_result
calc_a_minus_b_b0:
movf MATH_B0, 0 ;move MATH_B0 to W
subwf MATH_TMP0, 1 ;subtract W from TMP0 and store result in TMP0
;If result is negative then C=0, Z=0
;If result is positive then C=1, Z=0
;If result is zero then C=1, Z=1
btfsc STATUS, C ;If result is negative, then Carry = 0
goto calc_a_minus_b_b1
movlw 0x01 ;decrement TMP1
subwf MATH_TMP1, 1 ;subtract W from TMP1 and store result in TMP1
btfsc STATUS, C ;If result is negative, then Carry = 0
goto calc_a_minus_b_b1;
movlw 0x01 ;decrement TMP2
subwf MATH_TMP2, 1 ;subtract W from TMP2 and store result in TMP2
btfsc STATUS, C ;If result is negative, then Carry = 0
goto calc_a_minus_b_b1;
movlw 0x01 ;decrement TMP2
subwf MATH_TMP3, 1 ;subtract W from TMP3 and store result in TMP3
btfss STATUS, C ;If result is negative, then Carry = 0
goto calc_a_minus_b_end;
calc_a_minus_b_b1:
movf MATH_B1, 0 ;move MATH_B1 to W
subwf MATH_TMP1, 1 ;subtract W from TMP1 and store result in TMP1
btfsc STATUS, C ;If result is negative, then Carry = 0
goto calc_a_minus_b_b2
movlw 0x01 ;decrement TMP2
subwf MATH_TMP2, 1 ;subtract W from TMP2 and store result in TMP2
btfsc STATUS, C ;If result is negative, then Carry = 0
goto calc_a_minus_b_b2;
movlw 0x01 ;decrement TMP2
subwf MATH_TMP3, 1 ;subtract W from TMP3 and store result in TMP3
btfss STATUS, C ;If result is negative, then Carry = 0
goto calc_a_minus_b_end;
calc_a_minus_b_b2:
movf MATH_B2, 0 ;move MATH_B2 to W
subwf MATH_TMP2, 1 ;subtract W from TMP2 and store result in TMP2
btfsc STATUS, C ;If result is negative, then Carry = 0
goto calc_a_minus_b_b3
movlw 0x01 ;decrement TMP3
subwf MATH_TMP3, 1 ;subtract W from TMP3 and store result in TMP3
btfss STATUS, C ;If result is negative, then Carry = 0
goto calc_a_minus_b_end;
calc_a_minus_b_b3:
movf MATH_B3, 0 ;move MATH_B3 to W
subwf MATH_TMP3, 1 ;subtract W from TMP3 and store result in TMP3
btfss STATUS, C ;If result is negative, then Carry = 0
goto calc_a_minus_b_end;
movf MATH_TMP3, 0
movwf MATH_RES3
movf MATH_TMP2, 0
movwf MATH_RES2
movf MATH_TMP1, 0
movwf MATH_RES1
movf MATH_TMP0, 0
movwf MATH_RES0
calc_a_minus_b_end:
return
;--- end calc_a_minus_b ---
;--- calc_a_divided_by_b ---
calc_a_divided_by_b:
call copy_a_to_tmp_and_clear_result
calc_a_divided_by_b_b0:
movf MATH_B0, 0 ;move MATH_B0 to W
subwf MATH_TMP0, 1 ;subtract W from TMP0 and store result in TMP0
;If result is negative then C=0, Z=0
;If result is positive then C=1, Z=0
;If result is zero then C=1, Z=1
btfsc STATUS, C ;If result is negative, then Carry = 0
goto calc_a_divided_by_b_b1
movlw 0x01 ;decrement TMP1
subwf MATH_TMP1, 1 ;subtract W from TMP1 and store result in TMP1
btfsc STATUS, C ;If result is negative, then Carry = 0
goto calc_a_divided_by_b_b1;
movlw 0x01 ;decrement TMP2
subwf MATH_TMP2, 1 ;subtract W from TMP2 and store result in TMP2
btfsc STATUS, C ;If result is negative, then Carry = 0
goto calc_a_divided_by_b_b1;
movlw 0x01 ;decrement TMP2
subwf MATH_TMP3, 1 ;subtract W from TMP3 and store result in TMP3
btfss STATUS, C ;If result is negative, then Carry = 0
goto calc_a_divided_by_b_end;
calc_a_divided_by_b_b1:
movf MATH_B1, 0 ;move MATH_B1 to W
subwf MATH_TMP1, 1 ;subtract W from TMP1 and store result in TMP1
btfsc STATUS, C ;If result is negative, then Carry = 0
goto calc_a_divided_by_b_b2
movlw 0x01 ;decrement TMP2
subwf MATH_TMP2, 1 ;subtract W from TMP2 and store result in TMP2
btfsc STATUS, C ;If result is negative, then Carry = 0
goto calc_a_divided_by_b_b2;
movlw 0x01 ;decrement TMP2
subwf MATH_TMP3, 1 ;subtract W from TMP3 and store result in TMP3
btfss STATUS, C ;If result is negative, then Carry = 0
goto calc_a_divided_by_b_end;
calc_a_divided_by_b_b2:
movf MATH_B2, 0 ;move MATH_B2 to W
subwf MATH_TMP2, 1 ;subtract W from TMP2 and store result in TMP2
btfsc STATUS, C ;If result is negative, then Carry = 0
goto calc_a_divided_by_b_b3
movlw 0x01 ;decrement TMP3
subwf MATH_TMP3, 1 ;subtract W from TMP3 and store result in TMP3
btfss STATUS, C ;If result is negative, then Carry = 0
goto calc_a_divided_by_b_end;
calc_a_divided_by_b_b3:
movf MATH_B3, 0 ;move MATH_B3 to W
subwf MATH_TMP3, 1 ;subtract W from TMP3 and store result in TMP3
btfss STATUS, C ;If result is negative, then Carry = 0
goto calc_a_divided_by_b_end;
incf MATH_RES0, 1 ;increment MATH_RES0
btfss STATUS, Z ;on overflow, Z=1
goto calc_a_divided_by_b_b0 ;try another subtraction
incf MATH_RES1, 1 ;increment MATH_RES1
btfss STATUS, Z ;on overflow, Z=1
goto calc_a_divided_by_b_b0 ;try another subtraction
incf MATH_RES2, 1 ;increment MATH_RES2
btfss STATUS, Z ;on overflow, Z=1
goto calc_a_divided_by_b_b0 ;try another subtraction
incf MATH_RES3, 1 ;increment MATH_RES3
btfss STATUS, Z ;on overflow, Z=1
goto calc_a_divided_by_b_b0 ;try another subtraction
bsf MY_BITS, OVERFLOW ;set overflow flag
calc_a_divided_by_b_end:
return
;--- calc_a_divided_by_b ---
;--- calc_a_times_b ---
;TMP0 contains current number of runs
calc_a_times_b:
call copy_a_to_tmp_and_clear_result
calc_a_times_b_run:
;decrement, if < 0 then stop adding
movlw 0x01 ;decrement TMP0
subwf MATH_TMP0, 1 ;subtract W from TMP0 and store result in TMP0
btfsc STATUS, C ;If result is negative, then Carry = 0
goto calc_a_times_b_b0;
movlw 0x01 ;decrement TMP1
subwf MATH_TMP1, 1 ;subtract W from TMP1 and store result in TMP1
btfsc STATUS, C ;If result is negative, then Carry = 0
goto calc_a_times_b_b0;
movlw 0x01 ;decrement TMP2
subwf MATH_TMP2, 1 ;subtract W from TMP2 and store result in TMP2
btfsc STATUS, C ;If result is negative, then Carry = 0
goto calc_a_times_b_b0;
movlw 0x01 ;decrement TMP3
subwf MATH_TMP3, 1 ;subtract W from TMP3 and store result in TMP3
btfss STATUS, C ;If result is negative, then Carry = 0
goto calc_a_times_b_end;
;add B to RES and store result in RES
calc_a_times_b_b0:
movf MATH_B0, 0 ;move MATH_B0 to W
addwf MATH_RES0, 1 ;add W to MATH_RES0 and store result in MATH_RES0
btfss STATUS, C ;If result overflows, then Carry = 1
goto calc_a_times_b_b1
incf MATH_RES1, 1 ;increment RES1
btfss STATUS, Z ;If result overflows, then Z = 1
goto calc_a_times_b_b1;
incf MATH_RES2, 1 ;increment RES2
btfss STATUS, Z ;If result overflows, then Z = 1
goto calc_a_times_b_b1;
incf MATH_RES3, 1 ;increment RES3
btfss STATUS, Z ;If result overflows, then Z = 1
goto calc_a_times_b_b1 ;
bsf MY_BITS, OVERFLOW ;set overflow flag
goto calc_a_times_b_end
calc_a_times_b_b1:
movf MATH_B1, 0 ;move MATH_B0 to W
addwf MATH_RES1, 1 ;add W to MATH_RES1 and store result in MATH_RES1
btfss STATUS, C ;If result overflows, then Carry = 1
goto calc_a_times_b_b2
incf MATH_RES2, 1 ;increment RES2
btfss STATUS, Z ;If result overflows, then Z = 1
goto calc_a_times_b_b2;
incf MATH_RES3, 1 ;increment RES3
btfss STATUS, Z ;If result overflows, then Z = 1
goto calc_a_times_b_b2 ;
bsf MY_BITS, OVERFLOW ;set overflow flag
goto calc_a_times_b_end
calc_a_times_b_b2:
movf MATH_B2, 0 ;move MATH_B2 to W
addwf MATH_RES2, 1 ;add W to MATH_RES2 and store result in MATH_RES2
btfss STATUS, C ;If result overflows, then Carry = 1
goto calc_a_times_b_b3;
incf MATH_RES3, 1 ;increment RES3
btfss STATUS, Z ;If result overflows, then Carry = 1
goto calc_a_times_b_b3;
bsf MY_BITS, OVERFLOW ;set overflow flag
goto calc_a_times_b_end
calc_a_times_b_b3:
movf MATH_B3, 0 ;move MATH_B3 to W
addwf MATH_RES3, 1 ;add W to MATH_RES3 and store result in MATH_RES3
btfss STATUS, C ;If result overflows, then Carry = 1
goto calc_a_times_b_run
bsf MY_BITS, OVERFLOW ;set overflow flag
calc_a_times_b_end:
return
;--- calc_a_times_b ---
init_write_mode:
bcf MY_BITS, WRT ;not ready for writing data to LCD
movlw 0x00
movwf CNT_RW ;
bsf MY_BITS, U4B ;upper four bits
return
set_char:
call init_write_mode
movf LCD_TMP, 0 ;write LCD_TMP to W
andlw b11110000 ;get high nibble and move result in W
movwf LCD_BUF_00 ;write high nible to LCD_BUF
movlw b00000001 ;or with 1 in order to set LCD_RS
iorwf LCD_BUF_00, 1 ;Inclusive OR and write back to LCD_BUF_00
incf CNT_RW, 1 ;RW index bank 0, write result back in CNT_RW
movf LCD_TMP, 0 ;
andlw b00001111 ;get lower nibble and move result in W
movwf LCD_BUF_01
bcf STATUS, C ;clear carry
rlf LCD_BUF_01,1 ;shift left and store result in LCD_BUF
rlf LCD_BUF_01,1
rlf LCD_BUF_01,1
rlf LCD_BUF_01,1
movlw b00000001 ;or with 1 in order to set LCD_RS
iorwf LCD_BUF_01, 1 ;Inclusive OR and write back to LCD_BUF_00
incf CNT_RW, 1 ;RW index bank 0, write result back in CNT_RW
bsf MY_BITS, WRT ;ready for writing data bank 0
movlw 0x50 ;
movwf DLY_VAR ;move w to DLY_VAR bank 0
call delay_usec;
return
set_position:
call init_write_mode
;Set DDRAM Address 0
;RS RW DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0
;0 0 1 0 AC5 AC4 AC3 AC2 AC1 AC0
movf LCD_TMP, 0 ;write LCD_TMP to W
andlw b11110000 ;get high nibble and move result in W
movwf LCD_BUF_00 ;write high nible to LCD_BUF
movlw b10000000 ;set DB7 to 1
IORWF LCD_BUF_00, 1 ;write back to LCD_TMP
incf CNT_RW, 1 ;RW index bank 0, write result back in CNT_RW
movf LCD_TMP, 0 ;write LCD_TMP to W
andlw b00001111 ;get lower nibble and move result in W
movwf LCD_BUF_01 ;
bcf STATUS, C ;clear carry
rlf LCD_BUF_01, 1 ;shift left and store result in LCD_BUF
rlf LCD_BUF_01, 1
rlf LCD_BUF_01, 1
rlf LCD_BUF_01, 1
;movlw b10000000 ;set DB7 to 1
;IORWF LCD_BUF_01, 1 ;write back to LCD_TMP
incf CNT_RW, 1 ;RW index bank 0, write result back in CNT_RW
bsf MY_BITS, WRT ;ready for writing data bank 0
movlw 0x05 ;
movwf DLY_VAR ;move w to DLY_VAR bank 0
call delay_msec;
return
delay_sec: ;20 Mhz
movf DLY_VAR, 0 ;1 cycle, move content of DLY_VAR to W
movwf CNT0 ;1 cycle
delay_sec_cnt0:
movlw 0x60 ;1 cycle 96
movwf CNT1 ;1 cycle
delay_sec_cnt1:
movlw 0x45 ;1 cycle 69
movwf CNT2 ;1 cycle
delay_sec_cnt2:
movlw 0xFA ;1 cycle 250
movwf CNT3 ;1 cycle
delay_sec_cnt3:
decfsz CNT3 ;1 cycle
goto delay_sec_cnt3 ;2 cycles
decfsz CNT2 ;1 cycle
goto delay_sec_cnt2 ;2 cycles
decfsz CNT1 ;1 cycle
goto delay_sec_cnt1 ;2 cycles
decfsz CNT0 ;2 cycles
goto delay_sec_cnt0 ;1 cycle
return
delay_msec: ;20 Mhz
movf DLY_VAR, 0 ;1 cycle, move content of DLY_VAR to W
movwf CNT0 ;1 cycle
delay_msec_cnt0:
movlw 0x28 ;1 cycle 40
movwf CNT1 ;1 cycle
delay_msec_cnt1:
movlw 0x28 ;1 cycle 40
movwf CNT2 ;1 cycle
delay_msec_cnt2:
decfsz CNT2 ;1 cycle
goto delay_msec_cnt2 ;2 cycles
decfsz CNT1 ;1 cycle
goto delay_msec_cnt1 ;2 cycles
decfsz CNT0 ;2 cycles
goto delay_msec_cnt0 ;1 cycle
return
delay_usec: ;20 Mhz
movf DLY_VAR, 0 ;1 cycle, move content of DLY_VAR to W
movwf CNT0 ;1 cycle
delay_usec_cnt0:
nop ;1 cycle
nop ;1 cycle
decfsz CNT0 ;2 cycles
goto delay_usec_cnt0 ;1 cycle
return
start_lcd_timer:
;--- BANK 1 ---
bsf STATUS, RP0 ;select bank 1
;disable all interrupts
clrf INTCON ;GIE PEIE T0IE INTE RBIE T0IF INTF RBIF
clrf PIE1 ;EEIE CMIE RCIE TXIE xxx CCP1IE TMR2IE TMR1IE
bsf PCON, OSCF ;4 Mhz
movlw b00011111 ;
movwf TRISA ;TRISA, bank 1
movlw b00000000 ;RB0...RB7 as outputs
movwf TRISB ;TRISB, bank 1
;bit 7 RBPU: PORTB Pull-up Enable bit
; 1 = PORTB pull-ups are disabled
; 0 = PORTB pull-ups are enabled by individual port latch values
;bit 6 INTEDG: Interrupt Edge Select bit
; 1 = Interrupt on rising edge of RB0/INT pin
; 0 = Interrupt on falling edge of RB0/INT pin
;bit 5 T0CS: TMR0 Clock Source Select bit
; 1 = Transition on RA4/T0CKI/CMP2 pin
; 0 = Internal instruction cycle clock (CLKOUT)
;bit 4 T0SE: TMR0 Source Edge Select bit
; 1 = Increment on high-to-low transition on RA4/T0CKI/CMP2 pin
; 0 = Increment on low-to-high transition on RA4/T0CKI/CMP2 pin
;bit 3 PSA: Prescaler Assignment bit
; 1 = Prescaler is assigned to the WDT
; 0 = Prescaler is assigned to the Timer0 module
;bit 2-0 PS<2:0>: Prescaler Rate Select bits
; 0 = nPBPU INTEDG T0CS T0SE PSA PS<2:0>: Prescaler Rate Select bits
movlw b00011000 ;pull-ups, internal oscillator, WDT prescalar
movwf OPTION_REG ;OPTION_REG, bank 1
;--- BANK 0 ---
bcf STATUS, RP0 ;select bank 0
movlw U4B;
movwf MY_BITS;
movlw TMR_DELTA ;set initial value
movwf TMR0; ;TMR0, bank 0
clrf PORTA ;PORTA, bank 0
clrf PORTB ;PORTB, bank 0
bsf INTCON, T0IE ;INTCON enable timer 0 overflow
bsf INTCON, GIE ;enable interrupts
return
timer0_overflow:
;--- BANK 0 ---
bcf STATUS, RP0
bcf STATUS, RP1
t0if_set_data:
;--- BANK 0 ---
bcf STATUS, RP0 ;select bank 0
bcf STATUS, RP1 ;select bank 0
btfss MY_BITS, SKF ;Bit test flag, skip if set 'Skip frame'
goto t0if_nSKF ;SKF is not set, normal mode
bcf MY_BITS, SKF ;clear SKF-flag and go to toggle LCD_E
goto t0if_toggle_lcd_e
t0if_nSKF:
btfss MY_BITS, LCDE ;Bit test flag, skip if set LCD_E == LCD enable bit is set
goto t0if_reset_lcd_e ;go to reset lcde
btfss MY_BITS, WRT ;Bit test flag, skip if set WRT == ready to write data to LCD
goto t0if_reset_lcd_e ;go to reset lcde
bcf STATUS, C ;clear carry
bcf STATUS, Z ;clear carry
movf CNT_RW, 0 ;check if CNT_RW is 0
addlw b11111111
btfss STATUS, C ;if CNT_RW not is 0, then Carry flag will be set
goto t0if_no_more_data ;go to toggle LCD_E
movf LCD_BUF_00, 0 ;Move content of LCD_BUF_00 to W
movwf PORTB ;Move content of W to PORTB
movf LCD_BUF_01, 0 ;Move LCD_BUF one down, this could probably be optimized
movwf LCD_BUF_00
movf LCD_BUF_02, 0
movwf LCD_BUF_01
movf LCD_BUF_03, 0
movwf LCD_BUF_02
movf LCD_BUF_04, 0
movwf LCD_BUF_03
movf LCD_BUF_05, 0
movwf LCD_BUF_04
movf LCD_BUF_06, 0
movwf LCD_BUF_05
movf LCD_BUF_07, 0
movwf LCD_BUF_06
clrf LCD_BUF_07
decf CNT_RW, 1 ;Decrement CNT_RW and store result in CNT_RW
btfsc MY_BITS, U4B ;Bit test flag, skip if clear
goto t0if_lower_nibble
bsf MY_BITS, SKF ;After writing lower nibble, set skip frames flag. We don't wair for LCD is busy, have build in delay
bsf MY_BITS, U4B ;Next write will be upper nibble of next frame
goto t0if_toggle_lcd_e ;go to toggle LCD_E
t0if_lower_nibble:
bcf MY_BITS, U4B ;Next write will be lower nibble of current frame.
goto t0if_toggle_lcd_e ;go to toggle LCD_E
t0if_no_more_data:
bcf MY_BITS, LCDE ;clear 'LCD Enable'
bcf PORTB, 1;
goto t0if_toggle_lcd_e
t0if_reset_lcd_e:
bcf PORTB, 1 ;clear PORTB RB1, bank 0
bcf MY_BITS, LCDE ;clear LCDE
t0if_toggle_lcd_e:
btfss MY_BITS, LCDE ;if LCDE is set, set RB1 and clear LCDE
goto t0if_set_lcd_e; ;else clear RB1 and set LCDE
bsf PORTB, 1 ;set PORTB RB1, bank 0
bcf MY_BITS, LCDE ;clear LCDE
t0if_set_lcd_e:
bcf PORTB, 1 ;set PORTB RB1, bank 0
bsf MY_BITS, LCDE ;clear LCDE
movlw TMR_DELTA ;set initial value
movwf TMR0; ;TMR0, bank 0
bcf INTCON, T0IF ;reset interrupt T0IF
return
calc_char_n:
bcf INTCON, T0IE ;INTCON disable timer 0 overflow
movf MATH_REM3, 0 ;copy 'MATH_REM' to 'A'
movwf MATH_A3
movf MATH_REM2, 0
movwf MATH_A2
movf MATH_REM1, 0
movwf MATH_A1
movf MATH_REM0, 0
movwf MATH_A0
movf LCD_DIV3, 0 ;copy 'LCD_DIV' to 'B'
movwf MATH_B3
movf LCD_DIV2, 0
movwf MATH_B2
movf LCD_DIV1, 0
movwf MATH_B1
movf LCD_DIV0, 0
movwf MATH_B0
call calc_a_divided_by_b
movlw TMR_DELTA ;set initial value
movwf TMR0; ;TMR0, bank 0
bsf INTCON, T0IE ;INTCON enable timer 0 overflow
movlw 0x30 ;'0'
ADDWF MATH_RES0, 0 ;add w and RES3 store result in W
movwf LCD_TMP ;move w to LCD_TMP bank 0
call set_char
bcf INTCON, T0IE ;INTCON disable timer 0 overflow
movf MATH_RES3, 0
movwf MATH_A3
movf MATH_RES2, 0
movwf MATH_A2
movf MATH_RES1, 0
movwf MATH_A1
movf MATH_RES0, 0 ;copy 'RES' to 'A'
movwf MATH_A0
call calc_a_times_b
movf MATH_REM3, 0 ;copy 'MATH_REM' to 'A'
movwf MATH_A3
movf MATH_REM2, 0
movwf MATH_A2
movf MATH_REM1, 0
movwf MATH_A1
movf MATH_REM0, 0
movwf MATH_A0
movf MATH_RES0, 0 ;copy 'RES' to 'B'
movwf MATH_B0
movf MATH_RES1, 0
movwf MATH_B1
movf MATH_RES2, 0
movwf MATH_B2
movf MATH_RES3, 0
movwf MATH_B3
call calc_a_minus_b
movf MATH_RES3, 0 ;copy 'RES' to 'MATH_REM'
movwf MATH_REM3
movf MATH_RES2, 0
movwf MATH_REM2
movf MATH_RES1, 0
movwf MATH_REM1
movf MATH_RES0, 0
movwf MATH_REM0
movlw TMR_DELTA ;set initial value
movwf TMR0; ;TMR0, bank 0
bsf INTCON, T0IE ;INTCON enable timer 0 overflow
return
MY_INTERRUPT_HANDLER: ;SAVING THE STATUS AND W REGISTERS IN RAM
movwf W_TEMP ;copy W to temp register, could be in any bank
swapf STATUS,W ;swap status to be saved into W
bcf STATUS,RP0 ;change to bank 0 regardless of current bank
movwf STATUS_TEMP ;save status to bank 0 register
;--- ISR ---
bsf STATUS, RP0 ;select bank 1
btfsc INTCON, T0IF ;timer0 overflow? skip if clear
call timer0_overflow ;
;--- end ISR ---
swapf STATUS_TEMP,W ;swap STATUS_TEMP register into W, sets bank to original state
movwf STATUS ;move W into STATUS register
swapf W_TEMP,F ;swap W_TEMP
swapf W_TEMP,W ;swap W_TEMP into W
retfie ;return from interrupt
flash_n_times:
movlw b11111111 ;all leds on
movwf PORTB
movlw 0xff ;wait 255 msec;
movwf DLY_VAR ;DLY_VAR bank 0
call delay_msec ;
movlw b00000000 ;all leds off
movwf PORTB
movlw 0xff ;wait 255 msec;
movwf DLY_VAR ;DLY_VAR bank 0
call delay_msec ;
DECFSZ CNT_VAR, 1 ;decrement CNT_VAR and write back in CNT_VAR, skip if zero
goto flash_n_times
return
debug_show_x:
movlw 0x04 ;flash 4 times
movwf CNT_VAR
call flash_n_times
movf MATH_RES3, 0 ;the register you want to see
movwf PORTB
movlw 0x03 ;wait 3 sec;
movwf DLY_VAR ;DLY_VAR bank 0
call delay_sec ;
movlw 0x03 ;flash 3 times
movwf CNT_VAR
call flash_n_times
movf MATH_RES2, 0 ;the register you want to see
movwf PORTB
movlw 0x03 ;wait 3 sec;
movwf DLY_VAR ;DLY_VAR bank 0
call delay_sec ;
movlw 0x02 ;flash 2 times
movwf CNT_VAR
call flash_n_times
movf MATH_RES1, 0 ;the register you want to see
movwf PORTB
movlw 0x03 ;wait 3 sec;
movwf DLY_VAR ;DLY_VAR bank 0
call delay_sec ;
movlw 0x01 ;flash 1 time
movwf CNT_VAR
call flash_n_times
movf MATH_RES0, 0 ;the register you want to see
movwf PORTB
movlw 0x03 ;wait 3 sec;
movwf DLY_VAR ;DLY_VAR bank 0
call delay_sec ;
goto debug_show_x
end