;REVISION DATE: 16Nov95 ;6-11-95 Have introduced circular buffer to incoming msgs. ;10-11-95 Modified token-passing. PU1 issues all tokens, round-robin. ;15-11-95 Start-up sync. ;Please check Web site for latest version (see end this file). ;TERSE51G.ASM ;Token-passing network, 16 processors max. ;uses in-built UART. Can use RS-485 line drivers, single twisted pair. ;Designed for open-ended, terminated each end, transmission line. ;NOTES ON THIS VERSION: ;Current version treats remote msgs as Notifications, regardless ;of setting of the Notif-field in the header. ;Remote i/p msgs only have 4-bit pu-field, 3-bit node, 1-bit token/notif, ;so can only go to terminal 1, and node 0 -- 7. ;This TERSE uses 4th reg.bank -- nodes can use 1st - 3rd banks. ;distributed-tiny TERSE-51 version-G. ;This version is for the 8051 family. ;very fast, very small, version "G". ;......EQUATES YOU MAY HAVE TO MODIFY.............. $MOD51 ;config. info for Metalink assembler. THISPU EQU 1 ;range is 1 - 16.(stored as 0-15) D1SIZE EQU 4 ;buffer1 size. DSIZE EQU 4 ;size of other buffers. ;................................................................... ;................................................................... DSEG ORG 18h sp2in: DS 1 ;ptr to top circ.buf (this is also R0, 4th reg.bank) sp2out: DS 1 ;ptr to end circ.buff.(r1 " ) databyte: DS 1 ;(r2)msgisr puts incoming msg here, temporarily. header: DS 1 ;(r3) / databyte2: DS 1 ;(r4)outgoing msg here, temp. header2: DS 1 ;(r5) / timercnt: DS 1 ;PU1: used by timer0isr to set token-posting rate. tokencnt: DS 1 ;PU1: used by msgisr to construct tokens. ORG 20h flags: DS 1 ;flags.1,2,3,4 = 1, msg on terminal-1, -2, -3, -4. flagsB: DS 1 ;flags.0,.4-.7 used by msgisr, to sync networking. pdata1: DS 1 data1: DS D1SIZE ;Allows messages to queue, for terminal1. pdata2: DS 1 data2: DS DSIZE ;memory management -- see my book. pdata3: DS 1 data3: DS DSIZE ; / pdata4: DS 1 data4: DS DSIZE ; / scratch1: DS 1 scratch2: DS 1 EMPTYST EQU $-1 ;stack starts from end of internal variables. ;......EQUATES YOU MAY HAVE TO MODIFY.............. SP2TOP EQU 7Fh ;circular buffer, for i/p msgs. SP2BOTTOM EQU 6Bh ; / allow it to hold 20 msgs.(6C--7F buffer) fENABLETX EQU p1.7 ;enable to rs-485 driver chip (1=enabled). TIMERSETTING EQU 2 ;no. entries to timer0isr before fTOKENOUT set. ;............................................ ;..............FLAGS............... fTOKENHERE EQU flagsB.7 ;set when THISPU owns token. f2NDBYTEIN EQU flagsB.6 ;set when 2nd i/p byte pending, clrd when arrived. fPOSTREMOTE EQU flagsB.5 ;set from request by POSTREMOTE, clrd on completion f2NDBYTEOUT EQU flagsB.4 ;set to request 2nd byte Tx, clrd when posted. f2NDBYTEGONE EQU flagsB.3 ;set when 2nd byte posted, clrd when gone. fTOKENOUT EQU flagsB.2 ;req. by Timer0 post token, clrd when posted(PU1) fTOKENGONE EQU flagsB.1 ;set when token posted, clrd when gone (PU1 only) fSYNCHRONIZED EQU flagsB.0 ;clrd on reset, set after all initialisation. fPOSTTIME EQU flags.7 ;set for request post globaltime, clrd when done. fIGNORE1 EQU flags.6 ;set; ignore next incoming byte. fIGNORE2 EQU flags.5 ;set; ignore next incoming byte. fTERM4 EQU flags.4 ;set if msg on terminal4. fTERM3 EQU flags.3 ;set if msg on terminal3. fTERM2 EQU flags.2 ;set if msg on terminal2. fTERM1 EQU flags.1 ;set if msg on terminal1. ;................................................................... ;......SYSTEM CODES................. ;POSTREMOTE to node=0, notif=1, is sytem msg. If pu=THISPU, msg is global. ;(but, note, THISPU originates msg and will not get it back). ;POSTLOCAL can also post system msgs to node 0, but they dont get ;intercepted by system1 or system2 (urgent response inside msgisr) ;The following codes go into the databyte: KILLTOKEN EQU 01 ;use global msg only, to all pus. kills token. RESET EQU 02 ;reset program. GLOBALTIME EQU 03 ;CURRENTLY USED FOR START-UP SYNC ;NEXT VERSION (H) USES TO POST GLOBAL TIME. ;................................................................... CSEG start: clr fENABLETX ;/turn off rs-485 driver. ajmp start1 ;reset entry point. ;.... ORG 0Bh ;timer0. entered every 256*12/clk (clk in Hz) sec. xch a,timercnt ;save old acc., load timercnt. jnz run1 IF (THISPU = 1) setb fTOKENOUT ;want to post token. setb scon.1 ;cause Tx interrupt. ENDIF mov a,#TIMERSETTING ;reload. run1: dec a ;NOTE: should get reloaded every time a token arrives. xch a,timercnt ;restore old acc. reti ;.... ORG 23h ajmp msgisr ;serial interface. ORG 32h numentries EQU 5 signatures: DB 255, 0,08,22,152 ;starting node has signature=zero. nodenum: DB 0, 1, 2, 3, 4 nodeptr: ;these occupy 2 bytes each. ajmp node0 ;1st node in table is error-handler(always node-0). ajmp node1 ;this must be in ascending order ajmp node2 ;of node number. ajmp node3 ; ajmp node4 ;..... ;msgs will always be two bytes... POSTLOCAL MACRO node,term,notif,datavalue IF ((node >= 0) AND (node <= 31)) IF ((term >= 1) AND (term <= 4)) IF ((notif = 0) OR (notif = 1)) mov b,datavalue push b mov b,#(node + ((term-1)*32) + (notif*128)) push b ENDIF ENDIF ENDIF POSTLOCAL ENDM ;note that TERM is stored one-less. EX: TERM 1 is stored as 00. ;Headerformat: bit7=notif, 5--6=term-1, 0--4=node. NORMALEXIT MACRO ;use this one for normal operation. ajmp nodereturn NORMALEXIT ENDM FASTEXIT MACRO nextnode ;bypasses signature calc. & lookup, mov r5,#nextnode ; if you know the next node. ajmp gonode FASTEXIT ENDM POSTREMOTE MACRO pu,node,notif_token,datavalue IF ((node >= 0) AND (node <= 7)) IF ((pu >= 1) AND (pu <= 16)) IF ((notif_token = 0) OR (notif_token = 1)) jb fPOSTREMOTE,$ ;wait until previous msg has been posted. mov header2,#(node + ((pu-1)*8) + (notif_token*128)) mov databyte2,datavalue setb fPOSTREMOTE ;flag intention to msgisr. ENDIF ;(dont set scon.1 now, as will happen ENDIF ; when token arrives or in extractmsgs) ENDIF POSTREMOTE ENDM ;Header: bit7=notif_token, bit3--6=pu-1, bits0--2=node. ;If pu=THISPU & node=0 & Notif_token=0, then this is the token. ;Delivery will be to terminal 1 of destination pu/node. ;Error msgs, to node0, will normally be Notifications, due to their ;sporadic nature. We reserve Node=0 & notif_token=0 to identify a token. ;(urgent remote error msgs can be acted upon immediately inside TERSE ; -- see system: procedure). ;........................................................................ ;*****MAIN LOOP******* start1: ;program entry point 1. mov sp2in,#SP2BOTTOM ;initialise circ.buff(r0, 4th reg.bank) mov sp2out,#SP2BOTTOM ; / (r1) mov sp,#EMPTYST ;initialise 1st stack. mov pdata1,#data1 ;initalise pointers, to buffers empty. mov pdata2,#data2 ;/ mov pdata3,#data3 ;/ mov pdata4,#data4 ;/ mov scon,#90h ;setup serial interface. Mode-2. setb ie.4 ; / allow serial interrupts clr scon.0 ; / clr Rx interrupt flag setb ip.4 ; / select high-priority ;anl pcon,# ;/select baud rate for mode-2(default clk/64). mov flags,#00 mov flagsB,#00 IF (THISPU = 1) ;tokens will start at pu1 setb fTOKENHERE ; / setb fSYNCHRONIZED ;used to sync to network messaging. ENDIF ; / ;NOTE that timer0 could be activated for any pus, not just PU1... IF (THISPU = 1) mov timercnt,#(TIMERSETTING - 1) ;setup timer0. mov th0,#0 ; /reload value. mov tmod,#02 ; / timer0 mode 2 (8-bit auto-reload). setb tcon.4 ; / starts timer0. setb ie.1 ; /allow timer0 interrupts. ENDIF setb ie.7 ;global interrupts allow. nodereturn: ;<<<<<<<<<< Nodes return to here <<<<< anl psw,#0E7h ;make sure we are on register-bank-0. start2: ;program entry point 2. mov r2,#0 ;initially, make signature = zero. ;calculate the signature.... mov r0,sp ;save initial sp. loop3: cjne r0,#EMPTYST,loop2 ajmp sigout loop2: mov a,@r0 ;get 1st byte of msg. mov r1,a ;save it for calcsig. dec r0 ;go past Header. dec r0 ;bypass the data. jb acc.7,loop3 ;ignore notifications. calcsig: ;entered with r1=msg-header, r2=accumulated signature (starts =0) mov a,r2 jnb acc.7,sig1 ;jump if bit a.7 is clear. xrl a,#00100001b ;tapping points. sig1: xrl a,r1 rl a mov r2,a ajmp loop3 sigout: ;if comes out of above, with sig=0, diagram will restart, as must be ;complete... lookup: ;signature in r2, use lookup table to get address of node... mov r0,#numentries mov dptr,#signatures loop1: mov a,r0 dec a movc a,@a+dptr ;start at end of array. xrl a,r2 ;compare signatures jz gotonode ;signature found. no2: djnz r0,loop1 ;...sig. not found.... inc r0 ;make r0=1. (r0= lookup-index + 1). sigfnd: gotonode: mov dptr,#nodenum ;get node number. mov a,r0 ; / dec a ; / movc a,@a+dptr ; / mov r5,a ; / -->r5 gonode: anl flags,#11100001b ;bits 1-4 set by extractmsgs... ajmp extractmsgs ;extracts all msgs for this node.(comes back main1: ; here)(preserves r5) mov dptr,#nodeptr mov a,r5 rl a ;*2 --each entry is 2 bytes. jmp @a+dptr ;jump to next node to execute.>>>>>>>> ;r0 = node-number.(0--> ). ;.... ;*****END MAIN LOOP***** ;......................................................................... extractmsgs: ;move notifications from circ.buff. to stack... orl psw,#18h ;select 4th reg.bank. sub1: mov a,r1 ;(r1 is sp2out) cjne a,sp2in,sub3 ajmp sub4 sub3: cjne a,#SP2TOP,sub2 mov r1,#SP2BOTTOM sub2: inc r1 mov a,@r1 ;extract databyte. push acc inc r1 mov a,@r1 ;extract header. push acc ajmp sub1 sub4: anl psw,#0E7h ;select 1st reg.bank. nonotifs: ;entered with r5=node-number (0 --> 31, node-0 = 0, etc) ;extracts all msgs for this node-->r1,r2,... first-bank of registers, ;where r1 is terminal-1, etc. Dumps msg-header. mov r0,sp rex1: cjne r0,#EMPTYST,ex2 ajmp main1 ;*get out*>>>>>>>> ex2: mov a,@r0 ;get msg header. anl a,#1Fh ;extract node. cjne a,05,ex3 ;test if required node. mov a,@r0 ;yes. dec r0 anl a,#60h ;extract term cjne a,#0,nxt1 ;test if term1 jnb flags.1,tile1 ;jmp if no earlier msgs here. push 01 ;temp save. mov r1,pdata1 cjne r1,#data1+D1SIZE,tile2 ajmp tile3 ;dump the msg, as buffer full. tile2: mov a,@r0 mov @r1,a ;place msg databyte in data1. inc pdata1 tile3: pop 01 ;restore. ajmp ex4 tile1: mov 01,@r0 setb fTERM1 ajmp ex4 nxt1: cjne a,#20h,nxt2 ;test if term2 mov 02,@r0 setb fTERM2 ajmp ex4 nxt2: cjne a,#40h,nxt3 ;test if term3 mov 03,@r0 setb fTERM3 ajmp ex4 nxt3: mov 04,@r0 ;term4 setb fTERM4 ex4: pop acc ;move top msg down to fill gap. pop b mov @r0,b inc r0 mov @r0,a ex3: dec r0 dec r0 ajmp rex1 ;................................................................ ;................................................................ system: ;can handle urgent system messages at this point. ;the message is in header:databyte, and default here is ;that it will be delivered to 2nd stack then Node-0 in the ;normal way... cjne r2,#GLOBALTIME,chk0 ;(r2 is databyte) ajmp endisr ;do not deliver msg locally. chk0: cjne r2,#KILLTOKEN,chk1 ;global kill. token got lost,so try kill. clr fTOKENHERE clr fENABLETX ajmp endisr ;do not deliver msg locally. stupidplace: reti ;part of weird reset below. chk1: cjne r2,#RESET,chk2 ;reset processor. rst1: clr ie.7 acall stupidplace ;weird technique to reset processor! ajmp start ; / restart program. chk2: ;....**OPTIONAL MORE CODE HERE**..... ajmp go1 ;for optional local delivery. ;................................................................ msgisr: ;incoming msgs are always delivered to term1, and are Notifications. push acc ;save acc. push psw mov psw,#18h ;select 4th reg.bank. jnb scon.0,txh ;give Rx first go. ;..............RX HANDLING.............. msg1: clr scon.0 ;Rx interrupt processing..... IF (THISPU <> 1) jnb fSYNCHRONIZED,rx3 ;jmp if local-pu not time-synchronised to pu1. ENDIF jb f2NDBYTEIN,rx2 ;jmp if this is 2nd byte of msg. ;fall-thru is to receive 1st byte of msg or token.... ;..... rx4: mov a,sbuf ;receive 1st byte of remote i/p msg. anl a,#87h ;test for token, to any pu. jnz rx5 ;jmp if not a token. ;.... ;arrived byte is a token.... IF (THISPU <> 1) mov a,sbuf cjne a,#((THISPU-1)*8),endisr ;test for token addressed THISPU. setb fTOKENHERE ;(pu1 should NEVER get this!!) setb scon.1 ;see if anything to post. ENDIF ajmp endisr ;...does it immediately, via Tx-int. ;.... rx5: ;arrived byte is 1st of a message, so process it. mov header,sbuf setb f2NDBYTEIN ;flag that first byte arrived, 2nd pending. ajmp endisr ;will reenter to rx2 when 2nd byte arrives... ;.... rx2: ;will only come here if 2nd byte has arrived. clr f2NDBYTEIN mov databyte,sbuf ;receive 2nd byte of remote i/p msg. mov a,header anl a,#07h ;extract dest. node. jz system ;>>>>>URGENT HANDLING SYSTEM MSGS>>>>> mov a,header anl a,#078h ;extract dest. pu. cjne a,#((THISPU-1)*8),endisr ;jmp if addressed to another pu. go1: ;deliver msg to circular buffer. cjne r0,#SP2TOP,ok1 ;(r0 is sp2in)add msg to circ.buffer. mov r0,#SP2BOTTOM ; /(Note, we are not checking for ok1: inc r0 ; / overrun) mov @r0,databyte ; /databyte(r2)-->circ.buf inc r0 ; / mov a,r3 ;(header) anl a,#087h ;clr the pu-field (makes term-1=0) setb acc.7 ;**PRESENT VERSION** make into a Notification. ;Delivery is to terminal 1. mov @r0,a ; /header(r3)-->circ.buf ajmp endisr ;..... IF (THISPU <> 1) rx3: ;await system GLOBALTIME msg. jb f2NDBYTEIN,rx2 ;possible for fSYNC set in middle of msg. mov a,sbuf ;header cjne a,#80h,endisr ;jmp if not broadcast system msg from PU1. jnb scon.0,$ ;wait for databyte (system code). clr scon.0 mov a,sbuf cjne a,#GLOBALTIME,endisr setb fSYNCHRONIZED ;tells application that time set ok. ajmp endisr ;do not deliver msg locally. ENDIF ;...... endisr: pop psw pop acc reti ;...........TX HANDLING........... txh: clr scon.1 ;clr transmit flag, prevent reentry. jb fTOKENHERE,txj IF (THISPU = 1) jb fTOKENGONE,tx0b ;token has gone. (PU1 only) jb fTOKENOUT,tx1 ;jmp if token to be posted. " " ENDIF ajmp endisr txj: jb f2NDBYTEGONE,tx0 ;jmp if 2nd-byte gone jb f2NDBYTEOUT,tx2 ;jmp if 2nd-byte user post from this pu(after .3) jb fPOSTREMOTE,tx3 ;jmp if user want post from this pu(must be after.4) ;nothing to post, then fall-thru is... ;...... tx0: clr fTOKENHERE clr fPOSTREMOTE ;only clrd when all finished. clr f2NDBYTEGONE ;(next POSTREMOTE can proceed...but await nxt token) tx0b: clr fTOKENGONE ;token has been posted (PU1 only). clr fENABLETX ;turn-off rs-485 driver. ajmp endisr ;....... tx3: ;post msg from local user application.... setb fENABLETX ;turn-on rs-485 driver chip. mov sbuf,r5 ;(header2) setb f2NDBYTEOUT ;flag 1st byte is now txing. ajmp endisr ;will reenter and go to tx2... ;..... tx2: ;post 2nd byte of msg... mov sbuf,r4 ;(databyte2) clr f2NDBYTEOUT ; setb f2NDBYTEGONE ;flag 2nd byte is now txing. ajmp endisr ;will reenter and go to tx1 then tx0... ;..... IF (THISPU = 1) tx1: ;post a token. clr fTOKENOUT mov a,tokencnt add a,#8 ;this increments dest.pu of token. mov tokencnt,a anl a,#78h ;convert to a token. jnz tx1b ;jmp if token not addressed to pu1. jnb fPOSTTIME,tx1c ;jmp if no request to post globaltime. setb fENABLETX mov sbuf,#(0 + (0*8) + (1*128)) ;global system msg. jnb scon.1,$ ;wait until gone. clr scon.1 mov sbuf,#GLOBALTIME ;system-code. jnb scon.1,$ ;wait until gone. clr scon.1 clr fPOSTTIME clr fENABLETX clr fTOKENOUT ;precaution, next token-post overwrite. ajmp endisr ;don't post any local outgoing msgs. tx1c: setb fTOKENHERE setb scon.1 ;see if anything to post from thispu. ajmp endisr tx1b: setb fENABLETX mov sbuf,a ;post token. setb fTOKENGONE ; ajmp endisr ;...will reenter to tx0. ENDIF ;................................................................... ;................................................................ ;Circular buffer: |________| ; SP2TOP = 7F> | | upper limit ; | | ; |________| ;sp2in (r0, 4th bank)>| | sp2in is the head of the circular buffer. ; | | ; |________| ;sp2out(r1, 4th bank)>| | sp2out is the tail(one below, actually). ; |________| ; SP2BOTTOM = 6B> | | one below lower limit. ;TOKEN-PASSING NETWORK ;It's not really, token-passing, as I've changed it with the latest ;version, to a very rigid TDM (time division multiplexing), in which ;PU1 posts the tokens to each processor, including itself (internally) ;on a round-robin basis. The complete token cycle is: ; (256*12*TIMERSETTING*16)/clock ;where clock is in Hz, and result of above formula is in seconds. ;If we make TIMERSETTING=1, then cycletime=4.4mSec. ;This is the worst-case time that any processor will have to wait ;to post a message. If you want faster than this, you can modify ;the timer0 preload register, TH0. ;Note that if a processor has a cycletime <=4.4mSec and only ;posts one message per cycle, it won't have any delay if it ;self-syncs to the token. ;The above formula presumes 16 processors, and works even if there ;are less, but it is more efficient to cut-down the round-robin ;token firing to the exact number of processors. If there are ;4 processors, token cycle time becomes 4.4/4 = 1.1mSec. ;(this is recommended, in preference to altering TH0). ;GLOBALTIME ;The global-time mechanism is real simple, and effective. ;.... version H, coming soon. ;Already in G-version: ;I was concerned about a PU going off/on-line at odd times... ;now, when a PU comes on-line, it awaits the GLOBALTIME system ;message, before receiving any other messages. This mechanism ;also prevents pickup of partial-msgs when coming on-line. ;MODIFIED EXTRACTMSGS ;Introduced with version-G. ;For speed, when a message is delivered to a node, it is extracted ;from the stack-frame, and the gap is filled by popping the top ;message off the stack and moving it to the gap. ;This means that the ordering of messages in the stack-frame is ;not purely historical, and this complicates signature calculation. ;It is, however, faster than filling the gap by shifting all ;msgs above the gap, down. ;$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ ;a node is entered with r5 (address 05) = node number. ;(first register-bank selected, as rest used by stack, so r1 is addr.01,etc) ;msgs are in terminal-order, in addresses 01,02,03,04. ;bit-addressable "flags" has bits set if msg present ; -- example, msg in term1 (addr.01, or r1), then flags.1 is set. ;................. node0: ;node-0 is the error-handler NORMALEXIT ;................................................................... node1: ;Presume that this is the first node, when signature =0. IF (THISPU = 1) setb fPOSTTIME ;request to post globaltime to all PUs. ELSE jnb fSYNCHRONIZED,$ ;optional wait (only maybe wait here on ENDIF ;1st iteration of application cycle) ;Note: any PU other than 1, can resync to globaltime simply by ;clearing fSYNCHRONIZED: ; clr fSYNCHRONIZED ; jnb fSYNCHRONIZED,$ ;this wait is optional. ;..... ;this tests msg-passing around the network.... IF (THISPU = 1) cpl p3.7 POSTREMOTE 2,1,1,acc ;pu2,n1,notif,data. POSTREMOTE 3,1,1,acc ;pu3,n1,notif,data. ELSE jnb fTERM1,n11 ;all remote i/p msgs are to term1. cpl p3.7 ;LED###### n11: ENDIF ;.... POSTLOCAL 2,1,0,#34h ;node-2,term-1,not-notif.,immediate-data. POSTLOCAL 3,1,0,#00h ;node3,term1,notnotif,immediate-data. NORMALEXIT ;................................................................... node3: IF (THISPU = 1) mov b,0FFh dn3b: mov a,0FFh dn3: djnz acc,dn3 djnz b,dn3b ENDIF POSTLOCAL 4,2,0,32h ;node4,term2,notnotif,direct-data. NORMALEXIT ;.................................................................. node2: jnb fTERM2,n22 mov a,r2 ;Notification is in r2. n22: ; mov b,0FFh ;dn2b: mov a,0FFh ;dn2: djnz acc,dn2 ; djnz b,dn2b POSTLOCAL 4,1,0,#00h ;node4,term1,0,immediate-data. NORMALEXIT ;................................................................... node4: mov b,0FFh dn4b: mov a,0FFh dn4: djnz acc,dn4 djnz b,dn4b POSTREMOTE THISPU,1,1,#00 ;test loop-back. pu1,n1,notif,data. ;(posting to itself means totally ignored, except if a ; global system msg -- node=0/notif=1) POSTLOCAL 2,2,1,#11 ;notification, back to node-2,term-2. NORMALEXIT ;................ ;%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ;COPYRIGHT & LIABILITY NOTICE ;**************************** ;TERSE may be freely used, as long as the name "TERSE" is kept, ;with suitable pre- & postfix characters, and Barry kauler is ;acknowledged as the original developer. Any publications about ;TERSE, or a TERSE derivative, must reference Barry Kauler's ;Web site, or one of his publications. ;Barry Kauler accepts no liability for how this product performs. ;(c) Copyright Barry Kauler 1995 ;email: B.KAULER@COWAN.EDU.AU ;WWW: http://scorpion.cowan.edu.au/science/terse/terse.htm ;................................................................... END