; bw 19 sept 2000, pic 16f876 version. ; derived from wiggle15.asm ; document 16 bit multiply ; first test of 16 bit divide ; crystal is 3.546864 MHZ LIST P=16F876, R=DEC INCLUDE "P16f876.inc" __CONFIG _CP_OFF & _LVP_ON & _XT_OSC & _PWRTE_ON & _WDT_OFF & _BODEN_OFF & _CPD_OFF & _WRT_ENABLE_ON & _DEBUG_OFF led14 equ 3 ; pin 14 of chip ( RC3) led15 equ 4 ; pin 15 of chip ( RC4) ; the variables beginning with the letter "i" are owned by the intr routine cblock 0x20 itick1 ; ticks every 289 uS, wraps every 256 ticks (abt 74 mS) itick2 ; ticks every 289 uS, wraps at 173 (50 mS) itick3 ; ticks every 50 mS, wraps at 20 (1 S) i50ms ; ticks every 50 mS, wraps at 256 isec ; ticks every S, wraps at 256 isec16:2 ; ticks every S, wraps 0xffff endc ; these variables are available for use as resettable timers ; accuracy is +/- 289 uS, wraps every 74 mS cblock atick endc ; these variables are available for use as resettable timers ; accuracy is +/- 50 mS, wraps at 256*50 = 12.8 seconds cblock a50ms b50ms c50ms endc ; these variables are available for use as resettable timers ; accuracy is +/- 1 S, wraps at 256 seconds cblock asec bsec csec endc ; experiment with minutes cblock amin bmin endc ; lcd related variables cblock lcdtmp1 lcdtmp2 lcdtmp3 lcdstr endc ; BCD variables cblock NumH NumL TenK Thou Hund Tens Ones endc ; lcd display line buffer -- 16 bytes cblock lcdbuf : 16 endc ; 16bit multiply/divide support. note intel byte order (least sig. byte first/lowest addr) cblock BitCount Product:4 Multiplier:2 Multiplicand:2 Dividend:2 Divisor:2 Quotient:2 Remainder:2 endc ; for interrupt save/restore. i want this in the shared area of 0x70 - 0x7F SAVE_W equ 0x70 SAVE_STATUS equ 0x71 SAVE_PCLATH equ 0x72 ORG 0 GOTO Start ; interrupt handler. context save/restore here, to avoid any PCLATH problems org 4 movwf SAVE_W ; save context swapf STATUS, w CLRF STATUS ; make sure we are in bank 0 movwf SAVE_STATUS movf PCLATH, w movwf SAVE_PCLATH clrf PCLATH goto ticktock ; now we can handle the interrupt ifinish movf SAVE_PCLATH,w ; restore context movwf PCLATH swapf SAVE_STATUS, w movwf STATUS swapf SAVE_W, f swapf SAVE_W, w bcf INTCON, T0IF ; clear int (doesnt mess up status reg) retfie ; main entry point Start CLRF STATUS CLRF PORTA ; all port pins low CLRF PORTB CLRF PORTC CLRF SSPCON ; no mssp CLRF RCSTA ; no usart BSF STATUS, RP0 MOVLW 6 ; all A/D pins are digital for now MOVWF ADCON1 MOVLW 0 MOVWF TRISA ; all port pins output MOVLW 0 MOVWF TRISB ; all port pins output MOVLW 0 MOVWF TRISC ; all port pins output MOVLW 0x88 ; no prescaler for timer, disable portb pull-ups movwf OPTION_REG BCF STATUS, RP0 ; clear mem from 0x20 - 0x7f in bank 0 movlw 0x20 movwf FSR zmemlp clrf INDF incf FSR,f btfss FSR, 7 goto zmemlp incf amin, 1 ; ****** temp incf bmin, 1 ; ****** temp clrf TMR0 clrf INTCON bcf INTCON, T0IF ; clear timer int (if any) bsf INTCON, T0IE ; enable timer int bsf INTCON, GIE ; enable global ints call initlcd call clrscr call l1home movlw hellomsg call putstr movlw 2 movwf Multiplier clrf Multiplier+1 movlw 3 movwf Multiplicand clrf Multiplicand+1 call umul16 movf Product,w ; for now, ignore the high-order two bytes movwf NumL movf Product+1,w movwf NumH call tobcd call l2home call putbcd ; should display 00006 call delay2 call delay2 movlw 99 movwf Multiplier clrf Multiplier+1 movlw 201 movwf Multiplicand clrf Multiplicand+1 call umul16 movf Product,w ; for now, ignore the high-order two bytes movwf NumL movf Product+1,w movwf NumH call tobcd call l2home call putbcd ; should display 19899 call delay2 call delay2 movlw 0x3B movwf Multiplier movlw 1 movwf Multiplier+1 movlw 0x2B movwf Multiplicand movlw 1 movwf Multiplicand+1 call umul16 movf Product,w ; for now, ignore the high-order two bytes movwf NumL movf Product+1,w movwf NumH call tobcd call l2home call putbcd ; should display 28649 (note overflow: 0x16FE9 -> 0x6FE9) call delay2 call delay2 movlw 12 movwf Dividend clrf Dividend+1 movlw 3 movwf Divisor clrf Divisor+1 call udiv16 movf Quotient,w ; for now, ignore the remainder movwf NumL movf Quotient+1,w movwf NumH call tobcd call l2home call putbcd ; should display 00004 call delay2 call delay2 movlw 0xE8 ; 65000 movwf Dividend movlw 0xFD movwf Dividend+1 movlw 0x88 ; 5000 movwf Divisor movlw 0x13 movwf Divisor+1 call udiv16 movf Quotient,w ; for now, ignore the remainder movwf NumL movf Quotient+1,w movwf NumH call tobcd call l2home call putbcd ; should display 00013 call delay2 call delay2 movlw 12 movwf Dividend clrf Dividend+1 movlw 3 movwf Divisor clrf Divisor+1 call udiv16 movf Quotient,w ; for now, ignore the remainder movwf NumL movf Quotient+1,w movwf NumH call tobcd call l2home call putbcd ; should display 00004 call delay2 call delay2 movlw 100 movwf Dividend clrf Dividend+1 movlw 33 movwf Divisor clrf Divisor+1 call udiv16 movf Quotient,w ; for now, ignore the remainder movwf NumL movf Quotient+1,w movwf NumH call tobcd call l2home call putbcd ; should display 00003 call delay2 call delay2 movf Remainder,w movwf NumL movf Remainder+1,w movwf NumH call tobcd call l2home call putbcd ; should display 00001 call delay2 call delay2 ; ********* main loop ********************* mainloop movf isec16,w movwf NumL movf isec16+1,w movwf NumH call tobcd call l2home call putbcd movlw 1 subwf amin,w bnz chk2 call blink1 call delay2 goto mupd chk2 movlw 2 subwf amin,w bnz chk3 call blink2 call delay2 goto mupd chk3 movlw 3 subwf amin,w bnz chk4 call blink3 call delay2 goto mupd chk4 movlw 4 subwf amin,w bnz chk5 call blink4 call delay2 goto mupd chk5 movlw 5 subwf amin,w bnz chk6 call blink5 call delay2 clrf amin ; ************* temp goto mupd chk6 ; tbd call blink0 call delay2 ; update the minutes variable mupd movlw 60 subwf asec,w bnc m2 incf amin, 1 clrf asec incf bmin, 1 m2 goto mainloop ; *********************** LCD routines ****************** ; my lcd is digikey part 73-1012-ND, Optrex DMC-16117A, 16x1 char. ; but chars 9-16 begin at location 40. initlcd call del5ms ; give lcd module time to power-up movlw 3 ; reset (3 times, cause datasheet says so) call lcdcmd movlw 3 ; reset call lcdcmd movlw 3 ; reset call lcdcmd movlw 2 ; 4-bit mode call lcdcmd ; **** ok now in 4 bit mode. finish init. movlw 2 ; function set call lcdcmd ;;;;; movlw 0 ; N=0, F=0 movlw 8 ; N=1, F=0 call lcdcmd movlw 0 ; display off call lcdcmd movlw 8 call lcdcmd movlw 0 ; clear display ram call lcdcmd movlw 1 call lcdcmd movlw 0 ; entry mode set call lcdcmd movlw 6 ; I/D=1, S=0 call lcdcmd movlw 0 ; display on cursor off call lcdcmd movlw 0xC call lcdcmd return clrscr: movlw 0 ; clear the display call lcdcmd movlw 1 call lcdcmd goto del5ms l1home: movlw 8 ; goto pos 0 call lcdcmd movlw 0 call lcdcmd goto del5ms l2home: movlw 0xA ; goto pos 40. (decimal) call lcdcmd movlw 8 call lcdcmd goto del5ms ; display the 5 digit BCD value on the LCD putbcd movf TenK, w call putdec movf Thou, w call putdec movf Hund, w call putdec movf Tens, w call putdec movf Ones, w call putdec return putdec addlw '0' ; for digits 0-9 only putchar movwf lcdtmp1 ; save char swapf lcdtmp1,w call lcddata ; hi nibble movf lcdtmp1,w goto lcddata ; lo nibble ; on entry, w is index into string table of the first char of the ; null-terminated string to be sent to the lcd putstr movwf lcdstr pstrloop movlw HIGH (getstrch+1) movwf PCLATH movf lcdstr,w call getstrch movwf lcdtmp3 movlw HIGH ($+2) movwf PCLATH movf lcdtmp3,w addlw 0 btfsc STATUS,Z return call putchar incf lcdstr,f goto pstrloop ; only does 4 bits per call. lcdcmd andlw 0xf movwf lcdtmp2 ; save cmd movlw 0 ; E=0, RS=0 movwf PORTA nop nop movf lcdtmp2,w ; restore cmd movwf PORTC nop nop movlw 0x20 ; E=1, RS=0 movwf PORTA nop nop movlw 0 ; E=0, RS=0 movwf PORTA goto del5ms ; give the module time to execute the cmd ; only does 4 bits per call. lcddata andlw 0xf movwf lcdtmp2 ; save data movlw 0x10 ; E=0, RS=1 movwf PORTA nop nop movf lcdtmp2,w ; restore data movwf PORTC nop nop movlw 0x30 ; E=1, RS=1 movwf PORTA nop nop movlw 0x10 ; E=0, RS=1 movwf PORTA nop nop movlw 0 ; E=0, RS=0 movwf PORTA goto del1ms ; give the module time to process the data bclrscr return ; *********************** utility routines ************** blink1 movlw 0x10 ; pin 15 high, others low movwf PORTC call delay1 movlw 0 ; all pins low movwf PORTC goto delay1 blink2 call blink1 goto blink1 blink3 call blink1 call blink1 goto blink1 blink4 call blink1 call blink1 call blink1 goto blink1 blink5 call blink1 call blink1 call blink1 call blink1 goto blink1 ; ***** warning - may be using too much stack space ***** blink6 call blink5 goto blink1 blink7 call blink5 goto blink2 blink8 call blink5 goto blink3 blink9 call blink5 goto blink4 blink0 call blink5 goto blink5 ; 1mS delay - uses atick. not real accurate del1ms clrf atick del1mslp movlw 4 subwf atick,w bnc del1mslp return ; 5mS delay - uses atick. not real accurate del5ms clrf atick del5mslp movlw 17 subwf atick,w bnc del5mslp return ; 200-250ms delay - uses a50mS delay1 clrf a50ms d1lp movlw 5 subwf a50ms,w bnc d1lp return ; 2 sec delay - uses a50mS delay2 clrf a50ms d2lp movlw 40 subwf a50ms,w bnc d2lp return ; ********* interrupt routine *********************************** ticktock incf itick1, f ; highest resolution tick is abt 289 uS incf atick, f incf itick2, f movlw 173 ; 50mS at 3.547 MHZ subwf itick2,w bnc idone clrf itick2 incf i50ms, f incf a50ms, f incf b50ms, f incf c50ms, f incf itick3, f movlw 20 ; 20*50=1000 = 1Sec subwf itick3,w bnc idone clrf itick3 incf isec, f incf isec16, f btfsc STATUS, Z incf isec16+1, f incf asec, f incf bsec, f incf csec, f idone goto ifinish ; ********* Binary-to-BCD. Written by John Payson. ******************* ; Enter with 16-bit binary number in NumH:NumL. ; Exits with BCD equivalent in TenK:Thou:Hund:Tens:Ones. tobcd: swapf NumH,w andlw 0x0F ;*** PERSONALLY, I'D REPLACE THESE 2 addlw 0xF0 ;*** LINES WITH "IORLW 11110000B" -AW movwf Thou addwf Thou,f addlw 0xE2 movwf Hund addlw 0x32 movwf Ones movf NumH,w andlw 0x0F addwf Hund,f addwf Hund,f addwf Ones,f addlw 0xE9 movwf Tens addwf Tens,f addwf Tens,f swapf NumL,w andlw 0x0F addwf Tens,f addwf Ones,f rlf Tens,f rlf Ones,f comf Ones,f rlf Ones,f movf NumL,w andlw 0x0F addwf Ones,f rlf Thou,f movlw 0x07 movwf TenK movlw 0x0A Lb1: addwf Ones,f decf Tens,f btfss 3,0 goto Lb1 Lb2: addwf Tens,f decf Hund,f btfss 3,0 goto Lb2 Lb3: addwf Hund,f decf Thou,f btfss 3,0 goto Lb3 Lb4: addwf Thou,f decf TenK,f btfss 3,0 goto Lb4 retlw 0 ; ********* 16x16 unsigned multply. Written by Bill Welch, 26 sept 2000 *********** ; Derived from "Computer Organization and Design" 2nd Ed, by Patterson & Hennessy, ; figures 4.31 and 4.32, pp 257-258. ; Note the use of the "Intel" byte order-- least significant byte is at lowest address. ; Input is in Multiplier & Multiplicand, Output is in Product (32 bits). umul16: movf Multiplier,w ; copy the multiplier into right-half movwf Product ; of the product register movf Multiplier+1,w movwf Product+1 clrf Product+2 ; zero out the left-half of the clrf Product+3 ; product register clrf BitCount m16A: movlw 16 ; repeat 16 times subwf BitCount,w bnc m16B return m16B: btfss Product,0 ; Is Least Sig. Bit Zero? goto m16C movf Multiplicand,w ; LSB is a One, so add the addwf Product+2,f ; multplicand to the left-half btfsc STATUS,C ; of the product and store the incf Product+3,f ; result in the left-half of movf Multiplicand+1,w ; the product register addwf Product+3,f m16C: rrf Product+3,f ; shift the entire product register rrf Product+2,f ; right rrf Product+1,f rrf Product,f movf BitCount,w incf BitCount, f goto m16A ; ********* 16x16 unsigned divide. Written by Bill Welch, 26 sept 2000 *********** ; Derived from "Computer Organization and Design" 2nd Ed, by Patterson & Hennessy, ; figures 4.40 and 4.41, pp 270-271. ; Note the use of the "Intel" byte order-- least significant byte is at lowest address. ; Input is in Dividend & Divisor, Output is in Remainder and Quotient udiv16: movf Dividend,w ; copy the dividend into the movwf Quotient ; quotient register movf Dividend+1,w movwf Quotient+1 clrf Remainder ; zero out the remainder clrf Remainder+1 bcf STATUS,C rlf Quotient,f ; shift quotient/remainder left 1 bit rlf Quotient+1,f rlf Remainder,f rlf Remainder+1,f clrf BitCount u16A: movlw 16 ; repeat 16 times subwf BitCount,w bnc u16B bcf STATUS,C ; we are done, shift the rrf Remainder+1,f ; remainder right 1 bit rrf Remainder,f return u16B: movf Divisor+1,w ; subtract the divisor register subwf Remainder+1,f ; from the remainder register movf Divisor,w ; and store the result in the subwf Remainder,f ; remainder register btfss STATUS,C decf Remainder+1,f btfss Remainder+1,7 ; is remainder less than zero? goto u16C movf Divisor,w ; remainder is less than zero, so addwf Remainder,f ; restore the original value by btfsc STATUS,C ; adding the divisor register to the incf Remainder+1,f ; remainder. movf Divisor+1,w addwf Remainder+1,f bcf STATUS,C ; prepare to shift in a zero bit goto u16D u16C: bsf STATUS,C ; prepare to shift in a one bit u16D: rlf Quotient,f rlf Quotient+1,f ; shift the quotient/remainder rlf Remainder,f ; left one bit position. rlf Remainder+1,f movf BitCount,w incf BitCount, f goto u16A org 0x1800 getstrch: addwf PCL,f str0: str1: hellomsg: equ str1 - str0 DT "w16 e", 0 str2: worldmsg: equ str2 - str0 DT "world", 0 END