rem HLL interpreter for PIC16F690 - external eeprom version 1.23 12/28/12 rem Copyright 2012 Terry Newton (see below for license) rem rem Serial interface is direct-connect (no inverting interface chip)... rem Connect xmt pin (porta.1) to DB9 pin 2 through a 470-1K series resistor rem Connect rcv pin (porta.0) to DB9 pin 3 through a 15K-22K series resistor rem [note - pickit 2 pulls porta.0 low so for dev move rcv to another pin] rem Pull rcv pin or DB9 pin 3 high with a 100K resistor so that user program rem will start when a serial terminal is not connected. rem NOTE! There should be at least 10K of current sink or a 5.1V zener on the rem supply to avoid voltage rise when driving with a 12V serial device. rem Set terminal emulator to 9600 baud, 8 bits, no parity, 1 stop bit. rem Use 19200 baud if running the PIC at 8mhz or 4800 baud for 2mhz, etc. rem rem Optional 24FC64 eeprom can be used to provide 4K code and 4K data rem Connect eedatpin (porta.4) to 24LC64 pin 5, pull high with 10K rem Connect eeclkpin (porta.5) to 24LC64 pin 6, pull high with 10K rem Ground pins 1-4 and 7, supply to pin 8 (best to decouple with 0.1uF) rem rem To execute code from external eeprom make internal ee locs FE,FF = E4 EE rem Once set, the serial interface will default to loading code into external rem eeprom unless told otherwise with a P or X command. rem rem Current speed running from internal eeprom is roughly 3000 commands rem per second (typical commands like a = b + 5 etc). Running code from rem external ee is lots slower.. around 1000 commands per second @4mhz, rem running @8mhz doubles the speed. Hardware I2C(TM) would help. rem rem Note - if porting to another processor, modify the ReadAD, ReadEE and rem WriteEE code as needed, register locations/banks/bits/names vary depending rem on the part. Designed for 256 byte int ee, modify getexteeflag, fetch, rem s_readmemory and s_writememory code if different. rem rem License... rem The HLLPIC SIMPLE2 source code is Copyright 2012 Terry Newton. rem May be redistributed provided the copyright remains intact, and any rem changes are documented with attribution and date. The binary produced rem by compiling this source may be used without restrictions. rem Provided as-is and without warranty. rem rem History... rem 10/8/99 - original SIMBOT version for programming a small "popper" robot rem 11/19/12 - translated Parallax(TM) SIMBOT assembly into SIMPLE2 code rem removed robot-specific instructions for GP use rem [various dev versions 1.00-1.02 for internal ee, 1.10-1.11 for ext ee...] rem added serial menu, fixed bugs, added READANALOG command, rem added external eeprom code with a mechanism to configure rem for internal or external ee. rem 11/23/12 - v1.12... removed comments about THEN/ELSE/ENDIF (won't happen), rem replaced with INCREMENT and DECREMENT instructions, rem standardized variable locations and menu options for compiler, rem added mechanism for jumping between code in int/ext ee. rem 11/30/12 - edited comments, no code changes. rem 12/3/12 - v1.20... converted to the PIC16F690 processor rem added READARRAY and WRITEARRAY instructions/vars rem 12/4/12 - v1.21... (small) optimizations to ext ee code.. avoid redundant rem clock tristates, rely on ee pullup resistors for some bit highs rem (ee is on the hardware pins but the '690 doesn't really support rem master I2C... datasheet hints of an interrupt-driven solution) rem moved serial menu and related subs to the upper rom bank rem added menu option for listing a single page of external ee rem added define/assembler ifs for serial polarity rem 12/5/12 Corrected the case of a couple labels rem 12/16/12 v1.22.. added SENDSERIAL GETSERIAL MSDELAY FLASHLED SENDHEX rem moved xmtreg rcvreg milliseconds hexbyte vars to int. block rem fixed abs bug in LEFTSHIFT RIGHTSHIFT NOT SWAPBYTES rem 12/28/12 v1.23 modified array instructions to support 128 bytes, 0-79 map rem to 120h-16Fh, 80-127 map to C0h-EFh, >127 wrap to 0-127 rem modified serial menu Z command to prompt for L or H ram. rem configure default processor speed... rem must be SPEED8MHZ or SPEED4MHZ or SPEED2MHZ raw #DEFINE SPEED4MHZ rem (user program can change speed by writing bits 4-6 of OSCCON at 8Fh) rem (serial menu always operates at defined speed regardless of user code) rem configure serial polarity... raw #DEFINE SERIAL_POLARITY 0 ;0 for resistors, 1 to invert for TTL/MAX rem if set to 1 then pull rcvpin low, or if MAX chip on-board, pull RX high rem to define the "serial not connected" state rem configure processor and fuse options... asm list p=16f690 asm radix dec asm include ;for MPASM[X]/Linux filename needs to be uppercase asm __CONFIG _WDT_OFF & _MCLRE_ON & _PWRTE_ON & _CP_OFF & _CPD_OFF & _INTRC_OSC_NOCLKOUT asm errorlevel -302 ;suppress messages about not being in bank 0 asm errorlevel -306 ;suppress messages about crossing page boundary rem configure pin assignments... (user program can change TRISx as needed) define portapindirs = 11111101B ;1 out, rest inputs define portbpindirs = 11111111B ;all inputs define portcpindirs = 11111111B ;all inputs define ErrorLED = PORTA.1 ;flashes if interpreter error define xmtpin = PORTA.1 ;serial output pin rem define rcvpin = PORTA.0 ;serial input pin - pull high when not in use define rcvpin = PORTB.5 ;alt pin for pickit 2 development define eeclkpin = PORTB.6 ;external eeprom clock pin define eedatpin = PORTB.4 ;external eeprom data pin define rombank = PCLATH.3 ;set/clear to goto/gosub to high (>2KW) or low rom asm ORG 0 goto start asm ORG 4 asm RETFIE asm ORG 8 start: ;start of program code bitclear STATUS.RP0 PORTA = 0 ;clear porta register PORTB = 0 ;clear portb register PORTC = 0 ;clear portc register INTCON = 0 ;no interrupts ADCON0 = 0 ;disable A-D T1CON = 0 ;disable timer bitset STATUS.RP0 ;select high registers asm IFDEF SPEED8MHZ OSCCON = #01110000B ;8mhz int osc asm ENDIF asm IFDEF SPEED4MHZ OSCCON = #01100000B ;4mhz int osc asm ENDIF asm IFDEF SPEED2MHZ OSCCON = #01010000B ;2mhz int osc asm ENDIF TRISA = #portapindirs ;set pin directions TRISB = #portbpindirs TRISC = #portcpindirs PIE1 = 0 ;disable peripheral interrupts IOCA = 0 ;disable interrupt on change WPUA = 0 ;disable weak pullups OPTION_REG = #11001101B ;typical options bitclear STATUS.RP0 bitset STATUS.RP1 IOCB = 0 ;disable interrupt on change WPUB = 0 ;disable weak pullups ANSEL = 0 ;set all analog inputs to digital ANSELH = 0 CM1CON0 = 0 CM2CON0 = 0 bitclear STATUS.RP1 controlflags = 0 ;default run code from internal ee, clear other bits user_eepage = 0 ;default ext ee data page (starts at 4K) define use_ext_ee = controlflags.0 ;if set then fetch opcodes from extee gosub getexteeflag ;set use_ext_ee if int.ee.locs FE,FF = E4,EE if use_ext_ee then ;this should not be needed... but otherwise it sometimes exteeadrL = 0 ;bugs out on first read when reset via MCLR (never on exteeadrH = 0 ;powerup, just reset... pin glitching, bug, who knows) gosub ReadExtEE ;after the first read it's fine endif asm IF SERIAL_POLARITY == 0 if rcvpin goto startinterpreter asm ELSE if not rcvpin goto startinterpreter asm ENDIF bitset rombank ;switch to high rom goto serialinterface ;show the menu startinterpreter: goto inithll ;jump to interpreter init code rem don't define any bytes before this so it'll start at 20h rem put all additional bytes afterwards rem ---------------------------------------------------------- rem a Simple HLL Interpreter... rem Last Parallax assembly mod Oct 8 1999 (SIMBOT.SRC) rem Translated to SIMPLE2 Nov 19 2012 (robot-specific stuff removed) rem Last mod in this block Dec 16 2012 rem rem entry: jmp inithll rem exit: drops through rem ram usage: 53 bytes from 20h to 55h - up to 5 levels of sub calls rem ram includes extra optional locations so they won't move rem calls fetch sub to get next byte to interpret (internal or external) rem jumps to s_readmemory, s_writememory, s_readanalog, s_readarray rem s_writearray s_sendserial s_getserial s_msdelay s_flashled s_sendhex rem rem Summary of opcodes: rem rem 00 NOP rem 01 IF (***) rem 02 YY INCREMENT adds 1 to ram location YY rem 03 YY DECREMENT subtracts 1 from ram location YY rem 04 reserved rem 05 CLRWDT have to run occasionally if watchdog enabled rem 06 SLEEP sleeps for a time configured by the WDTCON reg rem 07 > (**) rem 08 < (**) rem 09 = (**) rem 0A >= (**) rem 0B <= (**) rem 0C <> (**) rem 0D RETURN rem 0E XX [YY] SET bit and CLEAR bit XX=sbbbvvvv s=state b=bit rem 0F XX [YY] IF bit and IF NOT bit v=var A-N 14=ram YY rem 1v [YY] LET (implied by "v =") v=var A-N 14=ram YY rem 2h LL GOTO h=high addr nibble LL=low addr rem 3h LL GOSUB h=high addr nibble LL=low addr rem 4v [YY] + (*) v=var A-N 14=ram YY 15=const YY rem 5v [YY] - (*) v=var A-N 14=ram YY 15=const YY rem 6v [YY] AND (*) v=var A-N 14=ram YY 15=const YY rem 7v [YY] OR (*) v=var A-N 14=ram YY 15=const YY rem 8v [YY] XOR (*) v=var A-N 14=ram YY 15=const YY rem 9v [YY] NOT v=var A-N 14=ram YY rem Av [YY] LEFTSHIFT v=var A-N 14=ram YY rem Bv [YY] RIGHTSHIFT v=var A-N 14=ram YY rem Cv [YY] SWAPNIBBLES v=var A-N 14=ram YY rem D0 IF CARRY as in: a = a + 1 if carry b = b + 1 rem D1 IF NOT CARRY rem D2 IF MINUS as in: a = a - 1 if minus a = 0 rem D3 IF NOT MINUS rem D4 IF ZERO rem D5 IF NOT ZERO rem ... rem (*) accumulates result into the last LET variable or condition temp rem (**) valid only after IF [expr] rem (***) follow by [expr1] [symbol] [expr2] - if the condition is rem not true, the following opcodes are turned into nops until rem another command opcode is encountered - anything besides rem (*) opcodes but IF should not be followed by another IF. rem rem abs means absolute ram address 0-255, constant is number 0-255 rem if v is 0-13 then user ram is addressed, if v = 14 (E) then the rem next byte is the memory address to access/modify, if v = 15 (F) rem the next byte is a constant. Weird but compact, variable ops rem in one byte, absolute address or constant ops in 2 bytes. rem LET and IF expressions can be any length but avoid long LET commands rem for IF targets as the interpreter still has to interpret them to rem get to the next command. rem rem Instructions with code outside of this block... rem rem F5 SENDHEX send hexbyte to serial out as hex digits rem F6 FLASHLED flash LED on/off each defined by milliseconds rem F7 GETSERIAL wait for and read serial in into rcvreg rem F8 SENDSERIAL send xmtreg to serial out rem F9 MSDELAY delay for period defined by milliseconds rem FA READARRAY read element arrayindex into arraydata rem FB WRITEARRAY write arraydata to element arrayindex rem FC READANALOG read analog(adchannel) into adresult rem FD READMEMORY read eeprom(eeadress) into eedata rem FE WRITEMEMORY write eedata to eeprom(eeaddress) rem FF SYSTEM exit program rem rem Fixed locations... rem 20h - eedata from READMEMORY or for WRITEMEMORY rem 21h - eeaddress for READMEMORY and WRITEMEMORY rem 22h - eepage for WRITEMEMORY - offset by 4Kbytes for 8Kbyte eeprom rem (if you really want to read/write ext ee program set to F0-FF) rem (when running from 256 byte internal ee then eepage is not implemented) rem 23h - adchannel for READANALOG rem 24h - adresult from READANALOG (10 bit result / 4) rem 25h - adresult_low from READANALOG (low 8 bits of result) rem 26h - adresult_high from READANALOG (top 2 bits of result) rem 27h - index for READARRAY and WRITEARRAY rem 28h - data for READARRAY and WRITEARRAY rem 29h - xmtreg for SENDSERIAL rem 2Ah - rcvreg for GETSERIAL rem 2Bh - milliseconds for MSDELAY rem 2Ch - hexbyte for SENDHEX rem 30h to 3Dh - the 14 predefined variables A-N rem rem Note: Names given for instructions purely arbitrary, only rem the bytes matter. Some sort of editor/tokeniser is needed rem to translate robot-simple source into tokens and download rem the data into the eeprom. Or the data can be manually rem "assembled" into tokens and entered using a PIC hex editor. rem define loc4 = 4 define loc0 = 0 rambase 20h byte user_eedata ;data to write or data read byte user_eeaddress ;set to address to read/write byte user_eepage ;for data in external eeprom, default set by system byte adchannel ;set to A/D channel to read, 0-7 byte adresult ;8 bit A/D result (result/4) byte adresult_low ;lower 8 bits of 10-bit result byte adresult_high ;upper 2 bits of 10-bit result byte arrayindex ;element number for readarray/writearray byte arraydata ;data for readarray/writearray byte xmtreg ;data for SENDSERIAL byte rcvreg ;data for GETSERIAL byte milliseconds ;data for MSDELAY byte hexbyte ;data for SENDHEX byte work byte instlow byte letregister array uservars(14) ; reserve 14 bytes for A-N - arranged to be 30h-3Dh array substack(10) ; reserve 2 bytes per level define numsublevels = 5 byte sublevel byte mytemp byte cond1 ; letregister = 14 byte cond2 ; letregister = 15 byte condition byte selfrefval byte interror ;moved 12/3/12 rem error codes 0=ok 1=opcode not implemented rem 2=fetch error 3=gosub error 4=return error byte hllflags ;moved 12/3/12 define carryflag = hllflags.0 define minusflag = hllflags.1 define zeroflag = hllflags.2 define execute = hllflags.3 define absolute = hllflags.4 define constant = hllflags.5 define condflag = hllflags.6 define letabsolute = hllflags.7 byte moreflags define tempbit = moreflags.7 define fetcherror = moreflags.6 byte PClow ; moved 12/16/12 byte PChigh ; moved 12/16/12 byte instruction ; moved 12/16/12 byte op1 ; moved 12/16/12 ; jump tables (these need to be low...) maindispatch: asm ADDWF 2,1 ; jmp pc+w goto i_zero ; 0x goto i_let ; 1x goto i_goto ; 2x goto i_gosub ; 3x goto i_add ; 4x goto i_sub ; 5x goto i_and ; 6x goto i_or ; 7x goto i_xor ; 8x goto i_not ; 9x goto i_shiftleft ; Ax goto i_shiftright ; Bx goto i_swap ; Cx goto i_dcodes ; Dx goto i_ecodes ; Ex goto i_super ; Fx i_zero: asm MOVF instlow,0 asm ADDWF 2,1 ;jmp pc+w goto endinstruction ; 00 (nop) goto i_if ; 01 goto i_increment ; 02 goto i_decrement ; 03 goto i_notimpl ; 04 goto i_clrwdt ; 05 goto i_sleep ; 06 goto i_greaterthan ; 07 goto i_lessthan ; 08 goto i_equal ; 09 goto i_greateroreq ; 0A goto i_lessoreq ; 0B goto i_notequal ; 0C goto i_return ; 0D goto i_bit ; 0E goto i_ifbit ; 0F i_super: asm MOVF instlow,0 asm ADDWF 2,1 ;jmp pc+w goto i_notimpl ; F0 goto i_notimpl ; F1 goto i_notimpl ; F2 goto i_notimpl ; F3 goto i_notimpl ; F4 goto s_sendhex ; F5 added 12/16/12 goto s_flashled ; F6 added 12/16/12 goto s_getserial ; F7 added 12/16/12 goto s_sendserial ; F8 added 12/16/12 goto s_msdelay ; F9 added 12/16/12 goto s_readarray ; FA added 12/3/12 goto s_writearray ; FB added 12/3/12 goto s_readanalog ; FC goto s_readmemory ; FD goto s_writememory ; FE goto s_system ; FF bit_table: asm ADDWF 2,1 ;jmp pc+w goto getbit0 goto getbit1 goto getbit2 goto getbit3 goto getbit4 goto getbit5 goto getbit6 goto getbit7 set_table: asm ADDWF 2,1 ;jmp pc+w goto setbit0 goto setbit1 goto setbit2 goto setbit3 goto setbit4 goto setbit5 goto setbit6 goto setbit7 clear_table: asm ADDWF 2,1 ;jmp pc+w goto clrbit0 goto clrbit1 goto clrbit2 goto clrbit3 goto clrbit4 goto clrbit5 goto clrbit6 goto clrbit7 cond_table: asm ADDWF 2,1 ;jmp pc+w goto eval_eq goto eval_ne goto eval_lt goto eval_gt goto eval_ltor goto eval_gtor ifflag_table: asm ADDWF 2,1 ;jmp pc+w goto ifcarry goto ifnotcarry goto ifminus goto ifnotminus goto ifzero goto ifnotzero goto i_notimpl goto i_notimpl rem ------------------------ rem interpreter entry inithll: asm CLRWDT PClow = 0 PChigh = 0 sublevel = 0 hllflags = 0 moreflags = 0 interror = 0 ;clear error byte bitset execute rem execution loop hllrun: gosub fetch ; external routine configed for ee rem returns (PClow,PChigh) in work, increments PClow,PChigh if fetcherror goto fetch_error ; ee fetch sets fetcherror if adr bad instruction = work bitclear constant bitclear absolute work = work and #00001111b instlow = work if instlow = 14 then bitset absolute endif if instlow = 15 then bitset constant endif work = instruction work = work and #11110000b asm SWAPF work,1 asm MOVF work,0 goto maindispatch i_if: bitclear execute bitclear condflag cond1 = 0 cond2 = 0 letregister = #cond1 goto hllrun i_add: gosub getop loc4 = letregister loc0 = loc0 + work ccfjr: gosub copyflags ;10-7-99 goto hllrun i_sub: gosub getop loc4 = letregister loc0 = loc0 - work goto ccfjr i_and: gosub getop loc4 = letregister loc0 = loc0 and work goto ccfjr i_or: gosub getop loc4 = letregister loc0 = loc0 or work goto ccfjr i_xor: gosub getop loc4 = letregister loc0 = loc0 xor work goto ccfjr i_equal: condition = 0 i_setcond: letregister = #cond2 bitset condflag goto hllrun i_notequal: condition = 1 goto i_setcond i_lessthan: condition = 2 goto i_setcond i_greaterthan: condition = 3 goto i_setcond i_lessoreq: condition = 4 goto i_setcond i_greateroreq: condition = 5 goto i_setcond i_let: bitclear condflag gosub getop if not execute goto nolet if absolute goto letabs letregister = #uservars letregister = letregister + instlow goto endlet letabs: letregister = loc4 ; 4 was set by getop goto endlet nolet: letregister = #cond1 endlet: loc4 = letregister selfrefval = loc0 loc0 = 0 goto endinstruction i_goto: gosub fetch if not execute goto endinstruction PChigh = instlow PClow = work goto endinstruction i_gosub: gosub fetch if not execute goto endinstruction loc4 = #substack loc4 = loc4 + sublevel loc0 = PClow increment loc4 loc0 = PChigh if sublevel >= #numsublevels*2 goto gosub_error increment sublevel increment sublevel PClow = work PChigh = instlow goto endinstruction i_return: if not execute goto endinstruction decrement sublevel decrement sublevel if sublevel.7 goto return_error loc4 = #substack loc4 = loc4 + sublevel PClow = loc0 increment loc4 PChigh = loc0 goto endinstruction i_not: gosub getop if not execute goto endinstruction rem loc4 = work ;bug fixed 12/16/12 (also in swap,shifts) if absolute goto donot loc4 = #uservars loc4 = loc4 + instlow donot: asm COMF 0,1 ccfcbjei: bitclear condflag ;10-7-99 gosub copyflags goto endinstruction i_swap: gosub getop if not execute goto endinstruction if absolute goto doswap loc4 = #uservars loc4 = loc4 + instlow doswap: asm SWAPF 0,1 goto ccfcbjei i_sleep: ;if not execute goto endinstruction asm BTFSC execute asm SLEEP goto endinstruction i_shiftleft: gosub getop if not execute goto endinstruction if absolute goto dosl loc4 = #uservars loc4 = loc4 + instlow dosl: shift loc0 left goto ccfcbjei i_shiftright: gosub getop if not execute goto endinstruction if absolute goto dosr loc4 = #uservars loc4 = loc4 + instlow dosr: shift loc0 right goto ccfcbjei i_clrwdt: ;if not execute goto endinstruction asm BTFSC execute asm CLRWDT goto endinstruction i_ifbit: bitclear execute gosub fetch op1 = work work = work and #00001111b if work >= 14 goto ifbitabs ;11/19/12 loc4 = #uservars loc4 = loc4 + work goto ifbit2 ifbitabs: gosub fetch loc4 = work ifbit2: gosub getbitstate if not op1.7 goto ifnotbit if tempbit goto runnext goto hllrun ifnotbit: if not tempbit goto runnext goto hllrun i_bit: gosub fetch op1 = work work = work and #00001111b if work >= 14 goto bitabs ;11/19/12 loc4 = #uservars loc4 = loc4 + work goto bit2 bitabs: gosub fetch loc4 = work bit2: if not execute goto endinstruction mytemp = op1 asm SWAPF mytemp,1 mytemp = mytemp and #00000111b asm MOVF mytemp,0 if op1.7 goto set_table goto clear_table setbit7: bitset loc0.7 goto endinstruction setbit6: bitset loc0.6 goto endinstruction setbit5: bitset loc0.5 goto endinstruction setbit4: bitset loc0.4 goto endinstruction setbit3: bitset loc0.3 goto endinstruction setbit2: bitset loc0.2 goto endinstruction setbit1: bitset loc0.1 goto endinstruction setbit0: bitset loc0.0 goto endinstruction clrbit7: bitclear loc0.7 goto endinstruction clrbit6: bitclear loc0.6 goto endinstruction clrbit5: bitclear loc0.5 goto endinstruction clrbit4: bitclear loc0.4 goto endinstruction clrbit3: bitclear loc0.3 goto endinstruction clrbit2: bitclear loc0.2 goto endinstruction clrbit1: bitclear loc0.1 goto endinstruction clrbit0: bitclear loc0.0 goto endinstruction i_dcodes: if instlow < 8 goto doifflags goto i_notimpl doifflags: bitclear execute asm MOVF instlow,0 goto ifflag_table ifcarry: if carryflag goto runnext goto hllrun ifnotcarry: if not carryflag goto runnext goto hllrun ifminus: if minusflag goto runnext goto hllrun ifnotminus: if not minusflag goto runnext goto hllrun ifzero: if zeroflag goto runnext goto hllrun ifnotzero: ;if not zeroflag goto runnext asm BTFSC zeroflag goto hllrun runnext: bitset execute goto hllrun i_increment: ;added 11/23/12 gosub fetch if not execute goto endinstruction loc4 = work increment loc0 mytemp = STATUS ;save status bitclear carryflag asm BTFSC mytemp,Z bitset carryflag ;carry set if rolls over from FF to 0 goto i_incdecflags i_decrement: ;added 11/23/12 gosub fetch if not execute goto endinstruction loc4 = work decrement loc0 mytemp = STATUS bitclear carryflag if loc0 = #0FFh then bitset carryflag ;carry set if rolls under from 0 to FF endif i_incdecflags: bitclear zeroflag bitclear minusflag asm BTFSC mytemp,Z bitset zeroflag ;zero flag set if result of inc/dec is 0 asm BTFSC loc0,7 bitset minusflag ;minus flag set if bit 7 of result is set endinstruction: bitset execute bitclear condflag goto hllrun rem -- subs ------------ getbitstate: rem bit in op1 4-6, byte in 0 (indexed) rem return state in tempbit mytemp = op1 asm SWAPF mytemp,1 getrawbit: bitclear tempbit mytemp = mytemp and #00000111b asm MOVF mytemp,0 goto bit_table getbit7: asm BTFSC 0,7 asm BSF tempbit return getbit6: asm BTFSC 0,6 asm BSF tempbit return getbit5: asm BTFSC 0,5 asm BSF tempbit return getbit4: asm BTFSC 0,4 asm BSF tempbit return getbit3: asm BTFSC 0,3 asm BSF tempbit return getbit2: asm BTFSC 0,2 asm BSF tempbit return getbit1: asm BTFSC 0,1 asm BSF tempbit return getbit0: asm BTFSC 0,0 asm BSF tempbit return rem --- get operand into work getop: if absolute goto getopabs ; fixed 10-7-99 if constant goto getopconst ; absolutes can now be loc4 = #uservars ; self referenced... duh loc4 = loc4 + instlow getop1: if loc4 = letregister goto getop2 ; go if refering to self work = loc0 ; return current contents return getop2: work = selfrefval ; return self instead return ; of current contents getopabs: gosub fetch loc4 = work goto getop1 getopconst: gosub fetch return rem --- set flags, evaluate condition if condition flag set copyflags: bitclear zeroflag bitclear carryflag bitclear minusflag asm BTFSC STATUS,C asm BSF carryflag asm BTFSC STATUS,Z asm BSF zeroflag asm BTFSC 0,7 asm BSF minusflag if not condflag goto copyfldone bitclear execute rem evaluate condition, if true set execute flag rem condition.. 0=equal 1=ne 2=lt 3=gt 4=ltor 5=gtor asm MOVF condition,0 goto cond_table eval_gtor: if cond1 >= cond2 goto eval_true goto copyfldone eval_ltor: if cond1 <= cond2 goto eval_true goto copyfldone eval_gt: if cond1 > cond2 goto eval_true goto copyfldone eval_lt: if cond1 < cond2 goto eval_true goto copyfldone eval_ne: if cond1 <> cond2 goto eval_true goto copyfldone eval_eq: if cond1 = cond2 goto eval_true goto copyfldone eval_true: bitset execute copyfldone: return s_system: if not execute goto endinstruction goto system rem exits return_error: ;interror 4 increment interror gosub_error: ;interror 3 increment interror fetch_error: ;interror 2 increment interror ; not implemented... all interror 1 ;uncomment these if not implemented ;s_readmemory: ;external ;s_writememory: ;external ;s_readanalog: ;external ;s_readarray: ;external ;s_writearray: ;external i_ecodes: i_notimpl: increment interror system: ;interror 0 if normal exit ; ----------------- end interpreter code --------------- byte controlflags rem define use_ext_ee = controlflags.0 ;if set then fetch opcodes from extee define gotenter = controlflags.1 ;to make echo look right define eeinvalid = controlflags.2 ;to be able to write to FF too define extee_op = controlflags.3 ;to minimize code duplication define extee_data = controlflags.4 ;so int ee code can access ext ee data define eerw = controlflags.7 ;defined in ext ee subs define eefast = controlflags.6 ;clear on start and if ext ee written to if interror <> 0 then ;if an error occured rem conditional assembly trips up SIMPLE's if/then/else go rearranged a bit asm IF SERIAL_POLARITY == 0 if not rcvpin goto docoredump asm ELSE if rcvpin goto docoredump asm ENDIF milliseconds = 250 ;blink at 2hz rate if error errorhang: ;this won't be seen if user prog reconfigures the pin asm CLRWDT bitcopy ErrorLED = not ErrorLED gosub MSdelay goto errorhang docoredump: bitset rombank ;switch to high rom goto coredump ;if terminal attached dump ram and show menu endif rem This is kind of contrived but I really want some way to jump between rem running from internal and external eeprom but without adding opcodes rem or too much code (need to keep it trim for adding robot/app code). rem The solution is to reuse the eepage variable - the only useful eepage rem values with an 8Kbyte eeprom are 00-0F for data and F0-FF for code. So... rem user_eepage = E0 - jump directly to internal ee, leave eepage set to EE rem so upon exiting via system will jump back to ext.ee. rem user_eepage = EE - jump directly to external ee. rem To remain in internal ee with normal post-processing, do eepage = 0. rem rem Provided internal ee locs FE,FF = E4,EE to configure for external code, rem then code running in internal ee after being started from external ee will rem continue to access data from external ee via READMEMORY/WRITEMEMORY but rem have to set eepage before accessing then set it back to EE before exiting. rem rem Example - Call internal code like a subroutine... rem (at start) if eepage = EEh goto resume_processing rem eepage = E0h system 'call fast code in internal ee rem resume_processing: eepage = 0 'restore normal operation rem Code in int.ee will return to ext upon system if it rem doesn't change eepage, or to process data in ext.ee... rem eepage = 0 (or other page) (do processing) eepage = EEh system if user_eepage = #0E0h then user_eepage = #0EEh ;return directly to external after running one pass bitclear use_ext_ee ;execute next pass from internal eeprom goto startinterpreter ;run the code immediately endif if user_eepage = #0EEh then bitset use_ext_ee ;resume execution in external eeprom goto startinterpreter ;app must change eepage to restore post-processing endif rem put any app-specific post-processing here... for picbot apps rem can be stuff like execute the move, read the senses, etc. goto startinterpreter getexteeflag: rem if int ee locations 254-255 contain "E4 EE" then set the rem use_ext_ee bit which is used by fetch sub and serial interface rem this sub called at reset and when serial menu displayed eeaddress = 254 gosub ReadEE bitclear use_ext_ee bitclear extee_data if eebyte = #0E4h then increment eeaddress gosub ReadEE if eebyte = #0EEh then bitset use_ext_ee bitset extee_data endif endif return rem ------ extra code for interpreter ------- rem fetch sub required by interpreter rem sets work to byte at PClow/PChigh then increments PClow/PChigh rem sets fetcherror bit if address is out of range (int only) fetch: if use_ext_ee then rem all addresses assumed valid, no fetch error exteeadrL = PClow exteeadrH = PChigh gosub ReadExtEE work = exteebyte else if PChigh = 0 then eeaddress = PClow gosub ReadEE work = eebyte else bitset fetcherror endif endif asm INCF PClow,1 ;increment PClow/PChigh asm BTFSC STATUS,Z asm INCF PChigh,1 return rem "superinstructions" - optional one-byte shortcut opcodes rem that do stuff then jump back into the interpreter.. general rem form is if execute then, [stuff], endif, goto endinstruction rem (the execute flag is cleared if after an IF and the condition is false) s_readmemory: if execute then if extee_data then exteeadrL = user_eeaddress exteeadrH = user_eepage + #10h if exteeadrH < #20h then gosub ReadExtEE user_eedata = exteebyte endif else ;use internal eeprom for data eeaddress = user_eeaddress gosub ReadEE user_eedata = eebyte endif endif goto endinstruction s_writememory: if execute then if extee_data then exteeadrL = user_eeaddress exteeadrH = user_eepage + #10h exteebyte = user_eedata if exteeadrH < #20h then gosub WriteExtEE endif else eeaddress = user_eeaddress eebyte = user_eedata gosub WriteEE endif endif goto endinstruction s_readanalog: if execute then adchannel = adchannel and 7 gosub ReadAD endif goto endinstruction rem array read/write instructions rem 0-79 map to 120h-16Fh, 80-127 map to C0h-EFh rem bit 7 of arrayindex cleared so 128-255 same as 0-127 s_readarray: if execute then loc4 = arrayindex bitclear loc4.7 if loc4 < 80 then bitset STATUS.IRP loc4 = loc4 + 32 else loc4 = loc4 + 112 endif arraydata = loc0 bitclear STATUS.IRP endif goto endinstruction s_writearray: if execute then loc4 = arrayindex bitclear loc4.7 if loc4 < 80 then bitset STATUS.IRP loc4 = loc4 + 32 else loc4 = loc4 + 112 endif loc0 = arraydata bitclear STATUS.IRP endif goto endinstruction rem more added instructions s_flashled: if execute then bitset ErrorLED gosub MSdelay bitclear ErrorLED gosub MSdelay endif goto endinstruction s_sendserial: if execute then bitset rombank gosub sendserial bitclear rombank endif goto endinstruction s_getserial: if execute then bitset rombank gosub getserial bitclear rombank endif goto endinstruction s_msdelay: if execute then gosub MSdelay endif goto endinstruction s_sendhex: if execute then bitset rombank gosub printhexbyte bitclear rombank endif goto endinstruction rem ============== low rom subroutines ================== rem delay for specified milliseconds rem not that accurate, only for approximate timing rem byte milliseconds ;set to #ms to delay (defined in int block) byte delaycounter byte innercounter byte countervalue MSdelay: rem read OSCCON to determine the value to use so the program rem can alter the processor speed without messing up delays bitset STATUS.RP0 asm MOVF OSCCON,0 bitclear STATUS.RP0 asm MOVWF delaycounter ;reuse delaycounter = delaycounter and #01110000b ;isolate oscillator bits rem delaycounter now 00000000 to 01110000 for 31khz to 8mhz rem will think 31khz is 62khz as it skips from 000 to 001 so if rem set to 000 will run twice as slow - I don't care, not worth rem wasting code on a clock speed that's way too slow for this, rem plus this is mainly used to make sure there's enough delay after rem eeprom writes, when reading analog pins, and flashing if error. countervalue = 247 MSdelaySetCVloop: if delaycounter = #01110000b goto MSdelay0 shift countervalue right delaycounter = delaycounter + 16 goto MSdelaySetCVloop MSdelay0: increment countervalue ;must be > 0 loop delaycounter from milliseconds ;2 innercounter = countervalue MSdelay1: ;8 cycles per loop asm NOP ;1 asm GOTO $+1 ;2 asm GOTO $+1 ;2 asm DECFSZ innercounter,F ;1 or 2 if skip asm GOTO MSdelay1 ;2 asm NOP ;1 next delaycounter ;3 return rem 256-byte eeprom subroutines (128-byte for '629/675) byte eeaddress byte eebyte ReadEE: asm MOVF eeaddress,W asm BSF STATUS,RP1 asm MOVWF EEADR asm BSF STATUS,RP0 asm BSF EECON1,RD asm BCF STATUS,RP0 asm MOVF EEDAT,W asm BCF STATUS,RP1 asm MOVWF eebyte return WriteEE: rem assumes interupts are disabled! asm MOVF eeaddress,W asm BSF STATUS,RP1 asm MOVWF EEADR asm BCF STATUS,RP1 asm MOVF eebyte,W asm BSF STATUS,RP1 asm MOVWF EEDAT asm BSF STATUS,RP0 asm BSF EECON1,WREN asm MOVLW 0x55 asm MOVWF EECON2 asm MOVLW 0xAA asm MOVWF EECON2 asm BSF EECON1,WR waitforeewrite: asm BTFSC EECON1,WR asm GOTO waitforeewrite asm BCF EECON1,WREN asm BCF STATUS,RP0 asm BCF STATUS,RP1 return rem ----- external eeprom subs ---------- rem requires eedatpin eeclkpin defined to eeprom pins rem uses MSdelay sub for write delay byte exteeadrL byte exteeadrH byte exteebyte byte i2cbyte byte eetemp rem define eerw = controlflags.7 rem define eefast = controlflags.6 ;clear on start and if ext ee written to byte lastadrL byte lastadrH rem *** read external eeprom *** rem address in exteeadrL/exteeadrH rem data returned in exteebyte ReadExtEE: if not eefast goto fullread ;if write then do full read (send address) asm INCF lastadrL,1 ;increment lastadrL/lastadrH asm BTFSC STATUS,Z asm INCF lastadrH,1 if lastadrL <> exteeadrL goto fullread ;not consecutive, do full read if lastadrH = exteeadrH goto fastread ;consecutive, don't send address fullread: bitclear eerw gosub eecommand i2cbyte = exteeadrH gosub eeoutbyte i2cbyte = exteeadrL gosub eeoutbyte bitset eefast ;next read fast if next address fastread: bitset eerw gosub eecommand gosub eeinbyte lastadrL = exteeadrL lastadrH = exteeadrH goto eestop rem ** write to external eeprom *** rem address in exteeadrL/exteeadrH rem data in exteebyte WriteExtEE: bitclear eefast ;force next read to be full bitclear eerw gosub eecommand i2cbyte = exteeadrH gosub eeoutbyte i2cbyte = exteeadrL gosub eeoutbyte i2cbyte = exteebyte gosub eeoutbyte gosub eestop milliseconds = 8 goto MSdelay rem ----- EE/I2C subs ----- ;optimizing 12/4/12 eecommand: bitset STATUS.RP0 bitclear eeclkpin bitclear eedatpin bitclear STATUS.RP0 bitset eedatpin bitset eeclkpin bitclear eedatpin bitclear eeclkpin i2cbyte = #10100000b asm BTFSC eerw bitset i2cbyte.0 goto eeoutbyte1 eeoutbyte: bitset STATUS.RP0 bitclear eedatpin bitclear STATUS.RP0 eeoutbyte1: loop eetemp from 8 bitclear eedatpin asm BTFSC i2cbyte,7 bitset eedatpin bitset eeclkpin bitclear eeclkpin asm RLF i2cbyte,1 next eetemp eeskipack: bitset STATUS.RP0 bitset eedatpin bitclear STATUS.RP0 bitset eeclkpin bitclear eeclkpin return eestop: bitset STATUS.RP0 bitclear eedatpin bitclear STATUS.RP0 bitclear eedatpin bitset STATUS.RP0 bitset eeclkpin bitset eedatpin bitclear STATUS.RP0 return eeinbyte: bitset STATUS.RP0 bitset eedatpin bitclear STATUS.RP0 loop eetemp from 8 bitset eeclkpin asm RLF exteebyte,1 bitclear exteebyte.0 asm BTFSC eedatpin bitset exteebyte.0 bitclear eeclkpin next eetemp goto eeskipack rem ------- end of ee subs ------ rem read analog pin for PIC16F684 rem tristate register must already be set to input rem this is chip-specific, code is similar for other rem PICs but control bits may be in different registers rem This sub is kind of bloated in an attempt to make GP rem moved these to interpreter block for easier access rem byte adchannel ;set to channel to read, 0-7 rem byte adresult ;8 bit result rem byte adresult_low ;lower 8 bits of 10-bit result rem byte adresult_high ;upper 2 bits of 10-bit result byte adtemp ReadAD: bitset STATUS.RP1 ANSEL = #00000001B bitclear STATUS.RP1 if adchannel > 0 then loop adtemp from adchannel bitset STATUS.RP1 shift ANSEL left bitclear STATUS.RP1 next adtemp endif adtemp = adchannel shift adtemp left shift adtemp left ADCON0 = adtemp bitset ADCON0.ADFM bitset ADCON0.ADON loop adtemp from 100 ;delay for AD to power up next adtemp bitset ADCON0.GO_DONE WaitForVoltage: if ADCON0.GO_DONE goto WaitForVoltage bitset STATUS.RP0 asm MOVF ADRESL,W bitclear STATUS.RP0 asm MOVWF adresult_low bitclear ADCON0.ADON adresult_high = ADRESH adresult = adresult_low shift adresult right shift adresult right bitcopy adresult.6 = adresult_high.0 bitcopy adresult.7 = adresult_high.1 return rem --------- end of low rom subs -------------- rem ========= start of high rom code ================== asm ORG 0x800 rem menu for the serial interface rem expanded for the '690 version since flash isn't as tight menutext: rem format is screwy but was having problems getting gpasm to rem assemble the more sane RETLW "Text",13,10 etc format rem avoid hex digits in menu so if an error occurs while sending rem a continuing data stream will be ignored asm ADDWF 2,1 asm DT 'H','L','L','P','I','C',' ','1','.','2','3',13,10 asm DT 'P',')',' ','P','r','o','g',' ','i','n','t',13,10 asm DT 'L',')',' ','L','i','s','t',' ','i','n','t',13,10 asm DT 'Z',')',' ','L','i','s','t',' ','r','a','m',13,10 asm DT 'X',')',' ','P','r','o','g',' ','e','x','t',13,10 asm DT 'Y',')',' ','D','u','m','p',' ','e','x','t',13,10 asm DT 'S',')',' ','D','u','m','p',' ','s','e','l',13,10 asm DT 'R',')',' ','R','u','n',13,10 asm DT '>',' ',0 rem ---------- serial user interface ------------------- rem The serial menu is displayed when the processor is reset with the serial rem input pin low - when a terminal is not connected a high-value resistor rem pulls the line high for normal program operation. Normally the only way rem to access the menu is by resetting the PIC but presently if an interpreter rem error occurs and a serial terminal is attached then it jumps to code that rem dumps ram and displays the menu - useful for debugging, can trigger in rem various ways such as coding nops into the program then manually replacing rem with an invalid opcode (D7 or another unused opcode). rem rem The serial menu provides the following functions... rem P) Prog int - program internal eeprom locations rem L) List int - list all 256 internal eeprom locations rem Z) List ram - list lower 256 bytes of ram rem X) Prog ext - program external eeprom locations rem Y) Dump ext - list all 8Kbytes of the external eeprom rem S) Dump sel - list selected external eeprom page (00 to 1F) rem R) Run - runs the user code rem rem The hex load format is simply address: byte byte .. END or an extra cr rem (END is used by the compiler to avoid having an extra blank line). rem For example... rem 0000: 0E 9E 05 0E 2E 05 30 11 0E 1E 05 0E AE 05 30 11 rem 0010: FF 10 4F 64 11 4F 0A 11 41 5F 01 D5 20 17 10 40 rem 0020: 5F 01 D5 20 14 0D rem END rem ...or manually like P then FE:E4 EE enter enter to configure for ext.ee. rem If the address isn't specified then starts at 0, newlines not required, rem CRs are echoed for neatness, bytes must be 2 characters and followed by rem either space or CR. If invalid hex then stops loading and displays "ERR". rem After programming each byte a "." is echoed, replacing the space. rem Can send files with ~20mS delay between each character to account for rem programming and echoing (simple serial code, if a char sent while it's rem sending it'll misread it), or for faster loading, PC software can wait rem for ":" after sending addresses and wait for "." after sending each byte. rem rem For convenience, if a load file starts with "0000:" it can be sent rem while the menu is showing without selecting P or X, will load into the rem default run space - if locations FE,FF contain E4,EE then loads into rem external eeprom, otherwise loads into internal eeprom. Load files can rem also specify directly by the first character, ie P 00: for internal rem or X 00: for external.. handy if setting up code that uses internal ee rem for "fast" subroutines using eepage (see previous comments). byte address_low byte address_high byte ptr rem byte hexbyte ;(defined in int block) byte gptemp array hexbuffer(4) byte buffer_low byte buffer_high rem define gotenter = controlflags.1 ;to make echo look right rem define eeinvalid = controlflags.2 ;to be able to write to FF too rem define extee_op = controlflags.3 ;to minimize code duplication serialinterface: gosub crlf ptr = 0 ;show title when starting goto showmenu serialmenu: ptr = 13 ;show just the menu when returning from functions showmenu: bitclear rombank ;low rom gosub getexteeflag ;set/clear use_ext_ee bit in case FE FF changed bitset rombank ;high bank gosub crlf menuloop: asm MOVF ptr,0 gosub menutext asm MOVWF xmtreg if xmtreg <> 0 then gosub sendserial increment ptr goto menuloop endif waitforcommand: gosub getserial if rcvreg = #'0' goto progdirect ;to just load without selecting gosub uppercase if rcvreg = #'R' goto runprogram if rcvreg = #'P' goto progint if rcvreg = #'L' goto dumpint if rcvreg = #'X' goto progext if rcvreg = #'Y' goto dumpext if rcvreg = #'Z' goto dumpram if rcvreg = #'S' goto dumpsel ;12/4/12 goto waitforcommand runprogram: gosub echoback gosub crlf milliseconds = 100 bitclear rombank ;low rom gosub MSdelay goto startinterpreter progdirect: rem must receive 3 more 0's in a row plus : to be valid, otherwise ignore loop gptemp from 3 gosub getserial if rcvreg <> #'0' goto waitforcommand next gptemp gosub getserial if rcvreg <> #':' goto waitforcommand rem now fake the display... if use_ext_ee then ;indicate if programming external or internal ee xmtreg = #'X' ;a bit of extra code but something nice to know else xmtreg = #'P' endif gosub sendserial gosub crlf loop gptemp from 4 xmtreg = #'0' gosub sendserial next gptemp xmtreg = #':' gosub sendserial rem for convenience if configured to execute from external eeprom rem then by default that's where code should be loaded to rem to override include the proper key in the hex text being loaded if use_ext_ee goto progextdirect goto progintdirect rem eeprom dump code dumpsel: gosub echoback gosub crlf xmtreg = #'P' gosub sendserial xmtreg = #'a' gosub sendserial xmtreg = #'g' gosub sendserial xmtreg = #'e' gosub sendserial xmtreg = #':' gosub sendserial ptr = 0 getpageloop: gosub getserial gosub uppercase if rcvreg >= 48 then if rcvreg <= 57 goto gpvalidhex endif if rcvreg >= 65 then if rcvreg <= 70 goto gpvalidhex endif goto serialerror gpvalidhex: rem first digit must be 0 or 1 for 8Kbyte ee if ptr = 0 then if rcvreg > #'1' goto serialerror endif hexbuffer(ptr) = rcvreg gosub echoback increment ptr if ptr < 2 goto getpageloop gosub getbuffervalue address_high = buffer_low address_low = 0 ptr = buffer_low increment ptr bitset extee_op ;read from external ee goto dumploop ;jump right to loop dumpint: bitclear extee_op ;read from internal ee ptr = 1 goto dumpcommon dumpext: bitset extee_op ptr = #20h ;dump all dumpcommon: gosub echoback address_low = 0 address_high = 0 rem address_low/high set to starting address rem stops when address_high = ptr dumploop: gptemp = address_low and #00001111b if gptemp = 0 then gosub crlf hexbyte = address_high gosub printhexbyte hexbyte = address_low gosub printhexbyte xmtreg = #':' gosub sendserial xmtreg = 32 gosub sendserial endif if not extee_op then ;read internal ee eeaddress = address_low bitclear rombank ;low rom gosub ReadEE bitset rombank ;high bank hexbyte = eebyte else ;read external ee exteeadrL = address_low exteeadrH = address_high bitclear rombank ;low rom gosub ReadExtEE bitset rombank ;high bank hexbyte = exteebyte endif gosub printhexbyte xmtreg = 32 gosub sendserial asm INCF address_low,1 ;increment address_low/address_high asm BTFSC STATUS,Z asm INCF address_high,1 if address_high < ptr goto dumploop goto serialmenu progintdirect: bitclear extee_op ;extee_op flag selects what code is used goto progdirectcommon progextdirect: bitset extee_op goto progdirectcommon progint: bitclear extee_op goto progcommon progext: bitset extee_op progcommon: gosub echoback gosub crlf progdirectcommon: address_low = 0 address_high = 0 bitclear eeinvalid progintloop: bitclear gotenter ptr = 0 pigetchar: gosub getserial if rcvreg = 10 goto pigetchar gosub uppercase if ptr = 0 then if rcvreg = 32 then gosub echoback goto pigetchar endif if rcvreg = 13 goto serialmenu else if ptr = 1 then if rcvreg = #'N' then ;exit prog mode if END received gosub echoback gptemp = hexbuffer(0) if gptemp = #'E' then gosub getserial gosub uppercase if rcvreg = #'D' then gosub echoback goto serialmenu endif endif goto serialerror endif endif if rcvreg = #':' goto pisetaddress if rcvreg = 32 goto piprocessbyte if rcvreg = 13 then bitset gotenter goto piprocessbyte endif endif if ptr > 3 goto serialerror if rcvreg >= 48 then if rcvreg <= 57 goto pivalidhex endif if rcvreg >= 65 then if rcvreg <= 70 goto pivalidhex endif goto serialerror pivalidhex: hexbuffer(ptr) = rcvreg gosub echoback increment ptr goto pigetchar pisetaddress: if ptr < 2 goto serialerror ;addresses must be 2-4 chars bitclear eeinvalid ;in case previous address rolled over gosub getbuffervalue address_low = buffer_low address_high = buffer_high if not extee_op then if address_high <> 0 goto serialerror else if address_high > #1Fh goto serialerror endif gosub echoback ;none of this changes rcvreg, delay echo for handshaking goto progintloop piprocessbyte: if eeinvalid goto serialerror ;last write rolled over if ptr <> 2 goto serialerror ;bytes must be 2 chars gosub getbuffervalue if not extee_op then ;for internal eeprom eeaddress = address_low eebyte = buffer_low bitclear rombank ;low rom gosub WriteEE bitset rombank ;high bank else ;for external eeprom exteeadrL = address_low exteeadrH = address_high exteebyte = buffer_low bitclear rombank ;low rom gosub WriteExtEE bitset rombank ;high bank endif asm INCF address_low,1 ;increment address_low/address_high asm BTFSC STATUS,Z asm INCF address_high,1 if not extee_op then if address_high <> 0 then bitset eeinvalid ;max 256 locations endif else if address_high > #1Fh then bitset eeinvalid ;max 8KB locations endif endif xmtreg = #'.' ;handshake - tell sender to send next byte gosub sendserial if gotenter then gosub crlf endif goto progintloop serialerror: gosub crlf xmtreg = #'E' gosub sendserial xmtreg = #'R' gosub sendserial xmtreg = #'R' gosub sendserial goto serialmenu coredump: 'target if interpreter error rem User code might have changed speed, redefined tristate, rem flashing an led on the serial pin.. all that has to be reset. bitset STATUS.RP0 ;select high registers asm IFDEF SPEED8MHZ OSCCON = #01110000B ;8mhz int osc asm ENDIF asm IFDEF SPEED4MHZ OSCCON = #01100000B ;4mhz int osc asm ENDIF asm IFDEF SPEED2MHZ OSCCON = #01010000B ;2mhz int osc asm ENDIF bitclear xmtpin ;make xmtpin an output (still in bank 1) bitset rcvpin ;make rcvpin an input bitclear STATUS.RP0 ;back to bank 0 bitclear xmtpin ;clear the serial out pin milliseconds = 100 ;and delay to avoid garbage bitclear rombank ;low rom gosub MSdelay bitset rombank ;high bank coredumpcommon: gosub crlf ptr = 0 coredumploop: gptemp = ptr and #00001111b if gptemp = 0 then gosub crlf hexbyte = ptr gosub printhexbyte xmtreg = #':' gosub sendserial endif loc4 = ptr hexbyte = loc0 gosub printhexbyte xmtreg = 32 gosub sendserial increment ptr if ptr <> 0 goto coredumploop bitclear STATUS.IRP goto serialmenu dumpram: ;target for menu Z command gosub echoback gosub crlf xmtreg = #'L' gosub sendserial xmtreg = #32 gosub sendserial xmtreg = #'o' gosub sendserial xmtreg = #'r' gosub sendserial xmtreg = #32 gosub sendserial xmtreg = #'H' gosub sendserial xmtreg = #':' gosub sendserial gosub getserial gosub uppercase if rcvreg = #'L' then gosub echoback goto coredumpcommon endif if rcvreg = #'H' then gosub echoback bitset STATUS.IRP goto coredumpcommon endif goto serialmenu rem ============== high rom subroutines ================= rem echo received character back echoback: xmtreg = rcvreg goto sendserial rem get buffer value, parse hex in hexbuffer and rem ptr contains number of digits in hexbuffer() rem return value in buffer_low and buffer_high rem uses gptemp getbuffervalue: buffer_high = 0 buffer_low = 0 decrement ptr gosub gbvsub buffer_low = buffer_low or gptemp if ptr = 0 goto gpvreturn decrement ptr gosub gbvsub asm SWAPF gptemp,1 buffer_low = buffer_low or gptemp if ptr = 0 goto gpvreturn decrement ptr gosub gbvsub buffer_high = buffer_high or gptemp if ptr = 0 goto gpvreturn decrement ptr gosub gbvsub asm SWAPF gptemp,1 buffer_high = buffer_high or gptemp gpvreturn: return gbvsub: gptemp = hexbuffer(ptr) if gptemp > 64 then gptemp = gptemp - 7 endif gptemp = gptemp - 48 return rem print hexbyte printhexbyte: xmtreg = hexbyte and #11110000b asm SWAPF xmtreg,1 gosub printhexdigit xmtreg = hexbyte and #00001111b printhexdigit: xmtreg = xmtreg + 48 if xmtreg > 57 then xmtreg = xmtreg + 7 endif goto sendserial rem send CRLF crlf: xmtreg = 13 gosub sendserial xmtreg = 10 goto sendserial ;return from there rem convert lc chars to uppercase uppercase: if rcvreg > 96 then rcvreg = rcvreg - 32 ;convert to uppercase endif return rem ----- serial subs ----- rem for resistor-connected serial port rem byte rcvreg ;serial received here (defined in int block) rem byte xmtreg ;byte here transmitted (defined in int block) byte bitcount ;for looping bits byte timercount ;for time delay rem *** serial input *** rem receive serial port data into rcvreg getserial: asm CLRWDT asm IF SERIAL_POLARITY == 0 if not rcvpin goto getserial ;wait for start bit asm ELSE if rcvpin goto getserial ;wait for start bit asm ENDIF gosub FullDelay loop bitcount from 8 gosub HalfDelay ;2 for 106uS total, need asm RRF rcvreg,1 ;1 106-12=94/2=47uS delay asm IF SERIAL_POLARITY == 0 asm BTFSC rcvpin asm ELSE asm BTFSS rcvpin asm ENDIF asm BCF rcvreg,7 asm IF SERIAL_POLARITY == 0 asm BTFSS rcvpin asm ELSE asm BTFSC rcvpin asm ENDIF asm BSF rcvreg,7 ;4 for HalfDelay routine gosub HalfDelay ;2 next bitcount ;3 = 12uS return rem *** serial output *** rem send xmtreg to serial port sendserial: asm CLRWDT asm IF SERIAL_POLARITY == 0 bitset xmtpin asm ELSE bitclear xmtpin asm ENDIF gosub FullDelay ;need ~102uS delay loop bitcount from 8 ;2 asm BTFSC xmtreg,0 ;4 asm IF SERIAL_POLARITY == 0 asm BCF xmtpin asm ELSE asm BSF xmtpin asm ENDIF asm BTFSS xmtreg,0 asm IF SERIAL_POLARITY == 0 asm BSF xmtpin asm ELSE asm BCF xmtpin asm ENDIF gosub HalfDelay ;2 gosub HalfDelay ;2 asm RRF xmtreg,1 ;1 next bitcount ;3 = 12uS asm IF SERIAL_POLARITY == 0 bitclear xmtpin asm ELSE bitset xmtpin asm ENDIF gosub FullDelay return rem *** serial timing subs for 9600 baud 4mhz *** rem -- 47uS HalfDelay: loop timercount from 14 ;2 next timercount ;3 * 14 - 1 = 41 asm GOTO $+1 ;2 return ;2 = 47uS rem -- 102uS FullDelay: gosub HalfDelay ;49 gosub HalfDelay ;49 asm GOTO $+1 ;2 return ;2 = 102uS rem --------- end of high rom subroutines ------------- rem default program in internal eeprom asm ORG 2100h ;rem 'simple test program - flash porta bits 1 and 2 randomly ;rem set bit 1 of porta ;rem clear bit 2 of porta ;rem gosub delay ;rem clear bit 1 of porta ;rem set bit 2 of porta ;rem gosub delay ;rem system ;rem delay: a = c xor rtcc ;rem c = c + 1 ;rem delay1: b = 10 ;rem delay2: b = b - 1 ;rem if not zero goto delay2 ;rem a = a - 1 ;rem if not zero goto delay1 ;rem return ; ;asm DE 0x0E,0x9E,0x05,0x0E,0x2E,0x05,0x30,0x11 ;asm DE 0x0E,0x1E,0x05,0x0E,0xAE,0x05,0x30,0x11 ;asm DE 0xFF,0x10,0x42,0x8E,0x01,0x12,0x42,0x4F ;asm DE 0x01,0x11,0x4F,0x0A,0x11,0x41,0x5F,0x01 ;asm DE 0xD5,0x20,0x1C,0x10,0x40,0x5F,0x01,0xD5 ;asm DE 0x20,0x19,0x0D ;to use external eeprom for program store... asm ORG 21FEh asm DE 0xE4,0xEE asm end