*
* Copyright (c) 1998 Massachusetts Institute of Technology * * This material was developed by the Amorphous Computing project at the * Massachusetts Institute of Technology Artificial Intelligence * Laboratory. Permission to copy and modify this software, to * redistribute either the original software or a modified version, and * to use this software for any purpose is granted, subject to the * following restrictions and understandings. * * 1. Any copy made of this software must include this copyright notice * in full. * * 2. Users of this software agree to make their best efforts (a) to * return to the MIT Amorphous Computing project any improvements or * extensions that they make, so that these may be included in future * releases; and (b) to inform MIT of noteworthy uses of this * software. * * 3. All materials developed as a consequence of the use of this * software shall duly acknowledge such use, in accordance with the * usual standards of acknowledging credit in academic research. * * 4. MIT has made no warrantee or representation that the operation of * this software will be error-free, and MIT is under no obligation to * provide any services, by way of maintenance, update, or otherwise. * * 5. In conjunction with products arising from the use of this material, * there shall be no use of the name of the Massachusetts Institute of * Technology nor of any adaptation thereof in any advertising, * promotional, or sales literature without prior written consent from * MIT in each case. ******************************** ** LABELS ** ******************************** * REGBASE EQU $1000 PORTA EQU REGBASE+$00 PIOC EQU REGBASE+$02 PORTC EQU REGBASE+$03 PORTB EQU REGBASE+$04 PORTCL EQU REGBASE+$05 DDRC EQU REGBASE+$07 PORTD EQU REGBASE+$08 DDRD EQU REGBASE+$09 PORTE EQU REGBASE+$0A CFORC EQU REGBASE+$0B OC1M EQU REGBASE+$0C OC1D EQU REGBASE+$0D TCNT EQU REGBASE+$0E TIC1 EQU REGBASE+$10 TIC2 EQU REGBASE+$12 TIC3 EQU REGBASE+$14 TOC1 EQU REGBASE+$16 TOC2 EQU REGBASE+$18 TOC3 EQU REGBASE+$1A TOC4 EQU REGBASE+$1C TI4O5 EQU REGBASE+$1E TCTL1 EQU REGBASE+$20 TCTL2 EQU REGBASE+$21 TMSK1 EQU REGBASE+$22 TFLG1 EQU REGBASE+$23 TMSK2 EQU REGBASE+$24 TFLG2 EQU REGBASE+$25 PACTL EQU REGBASE+$26 PACNT EQU REGBASE+$27 SPCR EQU REGBASE+$28 SPSR EQU REGBASE+$29 SPDR EQU REGBASE+$2A BAUD EQU REGBASE+$2B SCCR1 EQU REGBASE+$2C SCCR2 EQU REGBASE+$2D SCSR EQU REGBASE+$2E SCDR EQU REGBASE+$2F ADCTL EQU REGBASE+$30 ADR1 EQU REGBASE+$31 ADR2 EQU REGBASE+$32 ADR3 EQU REGBASE+$33 ADR4 EQU REGBASE+$34 BPROT EQU REGBASE+$35 OPTION EQU REGBASE+$39 COPRST EQU REGBASE+$3A PPROG EQU REGBASE+$3B HPRIO EQU REGBASE+$3C INIT EQU REGBASE+$3D TEST1 EQU REGBASE+$3E CONFIG EQU REGBASE+$3F * ******************************** *** EEPROM CONFIGURATION *** *** EEPROM starts at $F800 *** USER at $F820 (total ? bytes) *** LOADER at $F980 (total ? bytes) ******************************** EEPROM EQU $F800 USERPROG EQU $F820 USERPROGEND EQU $F980 LOADER EQU $F9A0 ********************************** *** RAM MEMORY CONFIGURATION ***** *** MESSAGING VARIABLES (4 bytes) *** STACK $5F to $04 (92 bytes) *** HEAP $60 to $B7 (88 bytes) -- also used from RAMPROG *** MSG OUT $B8 (36 bytes) -- also used from RAMPROG *** MSG IN $DC (36 bytes) ********************************** COUNT EQU $00 MSGID EQU $01 SENDID EQU $02 STACK EQU $5F LOCRAMPROG EQU $60 DST EQU $B8 SLEN EQU $B9 SFLAG EQU $BA SBUF EQU $BB SRC EQU $DC RLEN EQU $DD RFLAG EQU $DE RBUF EQU $DF * * Overlay for generic header for USERPROG messages: OUTMSGNUM EQU $BB for transferring numbers OUTMSGTYPE EQU $BD message type - for USERPROG interpretation OUTMSGID EQU $BF to identify this message OUTMSGSENDID EQU $C0 to identify the sender OUTMSG EQU $C2 message text INMSGNUM EQU $DF for transferring numbers INMSGTYPE EQU $E1 message type - for USERPROG interpretation INMSGID EQU $E3 to identify this message INMSGSENDID EQU $E4 to identify the sender INMSG EQU $E6 message text *** VARIABLES *** * Place your variables in the HEAP REGION * * TMPA is 2 bytes long TMPA EQU $60 TMPC EQU $62 VERSION EQU $64 OSRPCRUNNING EQU $65 FOOA EQU $66 -- for program use only FOOB EQU $68 FOOC EQU $6A FOOD EQU $6C TMPB EQU $6E TMPMESSAGE EQU $70 -- This is 36 bytes long. *** PARAMETERS *** * parameters for send and recieve SDELAY EQU 300 TDELAY EQU 50 FCOUNT EQU 15 RDELAY EQU 200 QTDELAY EQU 12 TDELAY2 EQU 100 D10ms EQU 10000/3 * message protocol YESACK EQU $AA NOACK EQU $3C TAGDATA EQU $00 -- This must be zero. TAGVERSION EQU $20 TAGPROGRAM EQU $60 TAGOS EQU $E0 TAGMASK EQU $E0 LENMASK EQU $1F * OS Remote Control (RPC, etc.) TAGOSRPC EQU $E854 -- Tag for RPC OSREQOK EQU $D3 -- OS Request return receipt *********************************************** ** The Loader program + Example User Program ** *********************************************** ORG EEPROM RESET LDS #STACK CLR BPROT enable EEPROM erase/write * (needs to happen in first 60ms) STX SENDID create a unique stamp for this machine JMP OSRUNUSERPROG EECOUNT FCB $00 used to determine program validity * ****************************** ** User Program Starts Here ** ****************************** * USER PROGRAM must start with the version number * User supplied program must poll for new messages * (OSPOLL) at least every RDELAY delay cycles * User program can use loader supplied subroutines * User Program must be no larger then 3840 bytes or * run over USERPROGEND * Program has access to FOOA - FOOD, contiguous 16-bit vars *************************************************** ORG USERPROG * * Contagious Loader * * * This is THE firefly/circle demo. * * * FOOA my circle demo number (distance) * FOOA+1 how long I've been displaying it * FOOB not listening delay * FOOC firefly times * FOOC+1 pulse width * FOOD listening delay * VER FCB $08 PROGBEGIN LDD SENDID get a unique ID ANDA #$04 ORA #$01 between #$100-#$400 STD FOOD store it as my delay LDY #$0200 initialize not-listening delay STY FOOB LDY #$0020 firefly count/pulse width STY FOOC LDY #$7F00 start with number 7F STY FOOA CLRA PSHA STARTLOOP LDAA FOOA+1 BNE DISPCIRC LDAA #$D3 STAA OUTMSGTYPE label message as a firefly LDAA FOOC increment firefly count BNE FIREOK LDAB #$7F CMPB FOOA circle been over? BEQ FIREOK if not, PULA JSR NOHEARFIRE then display without communication PSHA FIREOK INCA STAA FOOC CMPA #$15 timeout for circle (one more BNE DISPFIRE than display) LDAA #$7F kill the circle demo number STAA FOOA DISPFIRE PULA COMA blink STAA PORTB flash lights PSHA BRA BROAD go to broadcast it DISPCIRC LDAA #$3D STAA OUTMSGTYPE label message as a circle DEC FOOA+1 I've displayed it once LDAA FOOA get circle number INCA add one STAA PORTB display it BROAD STAA OUTMSGNUM broadcast what displaying LDAA #$04 STAA SLEN it's four bytes LDAB FOOC+1 pulse width CLRA XGDY CLRB broadcast the message JSR BROADCAST flash on the air LDY FOOB set natural frequency JSR YDELAY (don't listen to world) LDY FOOD DELAYLOOP LDAB #$01 specify delay factor JSR DELAYINT delay CMPB #$04 BEQ STARTCIRCLE was the light sensor hit? * CMPB #$05 * BEQ CONFIGMODE was the button pressed? TSTB BEQ DONELOOP done loop? LDAB INMSGTYPE was it a flash? CMPB #$D3 BEQ FLASHMSG CMPB #$3D was it a circle? BEQ CIRCLEMSG BRA DELAYLOOP if not - false alarm FLASHMSG PULA LDAA INMSGNUM fix up message if not good CMPA #$0F (set to everyone elses) BLO FLASHON CLRA BRA FLASH FLASHON LDAA #$FF FLASH PSHA LDD FOOD SUBD #$04 FIXNUM ANDA #$04 ORA #$01 STD FOOD JMP STARTLOOP DONELOOP LDD FOOD drift up ADDD #$01 BRA FIXNUM CIRCLEMSG LDAA INMSGNUM get the number that was broadcast CMPA FOOA is it less? BHS GOTOSTART if so PSHA LDD FOOA CMPA #$7F is it new/killed? BNE NOTNEW LDAB #$14 display 20 times NOTNEW PULA STD FOOA BRA CIRCLEEND STARTCIRCLE LDD FOOA CMPA #$7F BNE STARTNOTNEW is it new/killed? LDAB #$14 display 20 times STARTNOTNEW CLRA STD FOOA CIRCLEEND CLR FOOC GOTOSTART JMP STARTLOOP NOHEARFIRE BSR NOHEARBIGLOOP BSR NOHEARBIGLOOP BSR NOHEARBIGLOOP RTS NOHEARBIGLOOP LDY FOOD PSHA NOHEARLOOP LDAB #$01 JSR DELAYINT TSTB BNE NOHEARLOOP LDY FOOB JSR YDELAY PULA COMA STAA PORTB RTS * CONFIGMODE LDAA FOOA * CMPA #$7F timeout for config/same for other * BNE GOTOSTART config code below * DECA * STAA FOOA clear for new timeout * CLR FOOC clear firefly time * LDD FOOB allow change - FOOB, FOOC+1 * LSRD * LSRD * LSRD /16 * LSRD * CLRA * JSR SETNUM * LSLD * LSLD * LSLD *16 * LSLD * STD FOOB * LDAB FOOC+1 * JSR SETNUM * STAB FOOC+1 * LDY FOOB * BSR BROADCONFIG * BRA GOTOSTART * CONFIGRPC LDAA FOOA * CMPA #$7F timeout for config/same for other * BNE RETURN config code below * DECA * STAA FOOA clear for new timeout * CLR FOOC clear firefly time * STAB FOOC+1 change pulse width * STY FOOB change not-listening delay * BSR BROADCONFIG * RETURN RTS * BROADCONFIG LDX #$0008 try 8 times * BROADLOOP PSHX * PSHB * LDX #CONFIGRPC * CLR DST * JSR OSRPC * PULB * PULX * DEX * BNE BROADLOOP * RTS * * ENCODE gets value in B, puts encoded in A * DECODE gets encoded in A, puts value in B * * * Mapping - 1, 2, 3, 4, 6 ,8,10,12,16,20,24,28,36,44,52,60 * 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 * 0 0 0 0 1 1 1 1 0 0 0 0 1 1 1 1 * 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1 * 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 * * CONFIGNUM PSHA * BSR ENCODE * JSR SETNUM * BSR DECODE * PULA * RTS * ENCODE LDAA #$FF initialize A * ENCODELOOP INCA * BSR FINDADDER * CLC * SBCB TMPB allow numbers greater or equal to be * BHI ENCODELOOP remapped * RTS * DECODE CLRB * DECODELOOP BSR FINDADDER * ADDB TMPB * DECA * CMPA #$FF * BNE DECODELOOP * RTS * FINDADDER PSHA * ANDA #$0C * LSRA * LSRA * CLR TMPB * INC TMPB set TMPB=1 * INCA * FINDADDLOOP LSR TMPB * DECA * BNE FINDADDLOOP * LSL TMPB * PULA * RTS * * Must have this to signal end of user program. * PROGEND NOP ********************************************* *** LIBRARY OF PROCEDURES STARTS HERE ******* ********************************************* * ********************************************** ** PROCEDURE : OSSEND ** SEND starts here - all labels start with OS ** MUST save all registers, all get trashed ** ** ARGUMENTS: ** place msg in SBUF ** length of MSG + msg tag in SLEN (TAG[3]:LENGTH[5]) ** TAG values: ** DATA 000 ** VERSION 001 ** PROGRAM 011 ** OS 111 ** destination of MSG in DST ** Clear SFLAG ** RETURN: ** SFLAG = ** $00 = success ** $01 = failure, no response ** $02 = line in use already ********************************************** ************************************ ** HANDSHAKE with neighbor ** ** before starting transmission ** ************************************ ** ** Handshake protocol (sender): ** ** 1. check if line is LOW. If LOW, goto receive mode ** 2. hold line LOW for SDELAY (= 300) ** 3. sample line 3 times checking for LOW (i.e. receiver confirmation) ** 4. if line did not go LOW, send failed ** 5. after TDELAY check if line still LOW, if not LOW, goto 8 ** 6. after another TDELAY, check if line still LOW, if HIGH goto 8 ** 7. not a confirmation, send failed ** 8. handshake complete. start sending ** *************************************** ORG LOADER OSSEND LDAA DDRC store old DDRC on stack PSHA restore at end (ossdone) * * check if line is already LOW. If LOW, then line is in use * CLR DDRC enable reads c0-c3 LDAA PORTC ANDA DST BNE OSSHANDSHAKE JMP OSLINEINUSE * * Otherwise, try to transmit beginning of handshake * OSSHANDSHAKE LDAA #$0E enable write c1-c3, read c0 STAA DDRC LDAA DST COMA LOWER LINE for delay=SDELAY STAA PORTC LDX #SDELAY JSR DELAY LDAA #$FF STAA PORTC then raise line again * * wait for confirmation from receiver * LDAB #$03 poll 3 times looking for confirmation CLR DDRC enable reads c0-c3 OSSPOLL1 LDAA PORTC ANDA DST BEQ OSSWAITHIGH recieved confirm (= low), then wait for high LDX #TDELAY JSR DELAY DECB BNE OSSPOLL1 JMP OSNOCONFIRM for now an error, usually try again from start * * Confirmation must last at most 2*TDELAY * OSSWAITHIGH LDX #TDELAY wait TDELAY, then check if line is HIGH JSR DELAY LDAA PORTC ANDA DST BNE OSTRANSMIT if got HIGH, then ready to transmit LDX #TDELAY wait TDELAY, then check if line is HIGH JSR DELAY LDAA PORTC ANDA DST BNE OSTRANSMIT BRA OSWRONGCONFIRM if still LOW, then error ******************************************* *** TRANMISSION *** Transmit bytes stored in SBUF *** Number of bytes to be transmitted SLEN *** START transmission with a ZERO BIT *** send HEADER byte TAG(3):SLEN(5) *** send SLEN bytes, one byte at a time *** END transmission with a ONE BIT (reset line) ******************************************* OSTRANSMIT * LDAA #$F9 1001 pattern to say sending msg on c1 * STAA PORTB LDAA #$0E enable write c1-c3, read c0 STAA DDRC * * Start by Sending Zero * LDAA DST COMA STAA PORTC LDX #TDELAY JSR DELAY *** NOTE : cycle counting from this point on * * SEND HEADER = TAG(3):SLEN(5) * LDAB SLEN (3) put header in B LDY #$08 (4) JSR OSSENDBITS send the header * * setup * LDAA SLEN (3) sending SLEN bytes ANDA #LENMASK (2) clear TAG STAA TMPC (3) store counter in memory LDX #SBUF need 2 byte address for indirect addressing STX TMPA copy address of data being sent to tmpA * * SEND SLEN * 8 BITS * OSSENDBYTE LDX TMPA (4) LDAB 0,X (4) put msg in B LDY #$08 (4) send 8 bits JSR OSSENDBITS send 8 bits stored in accumulator B LDX TMPA (4) NOP, add 4 cycles LDX TMPA (4) increment address INX (3) STX TMPA (4) DEC TMPC (6) decrement counter BNE OSSENDBYTE * * Send ONE to Terminate Transmission * LDAA #$FF end with HIGH STAA PORTC LDX #TDELAY JSR DELAY CLR SFLAG set SFLAG to indicate success * * wait, and go back to beginning * * LDAA #$FF 1111 pattern to say done with sending * STAA PORTB * LDY #$0700 * JSR YDELAY * * reset PORTC STATE * OSSDONE PULA STAA DDRC RTS ***************************************************** * * PROCEDURE : OSSENDBITS * sends 8 bits stored in accumulator B * Y must be set = 8 (number of bits) * trashes X, A, Y * OSSENDBITS TBA (2) ANDA #$01 get bit to send (2) BEQ OSSENDZERO if bit=0, (3) LDAA #$FF send a "1" (2) BRA OSCONTSEND (3) OSSENDZERO LDAA DST send a "0" (3) COMA (2) OSCONTSEND STAA PORTC (3) LDX #TDELAY JSR DELAY LSRB (2) NOP (2) just to add 2 cycles DEY BNE OSSENDBITS RTS ***************************************************** ** ERROR STATES ** ** FLAG =$00 = success ** $01 = failure, no response ** $02 = line in use already ** $03 = confirmation signal too long OSLINEINUSE LDAA #$02 STAA SFLAG * LDAA #$FE 1110 light pattern for receiving * STAA PORTB * LDY #$0700 * JSR YDELAY BRA OSSDONE OSWRONGCONFIRM LDAA #$03 STAA SFLAG * LDAA #$FA 1010 for incorrect confirm (didn't go HIGH) * STAA PORTB * LDY #$0700 * JSR YDELAY BRA OSSDONE OSNOCONFIRM LDAA #$01 STAA SFLAG * LDAA #$F3 0011 for no confirmation from receiver * STAA PORTB * LDY #$0700 * JSR YDELAY BRA OSSDONE * ********************************************** ** PROCEDURE : OSRECV ** RECV starts here - all labels start with OS ** MUST save all registers, all get trashed ** ** ARGUMENTS: ** set RFLAG to sources mask ** (where bit = 1 if want to receive from that source) ** ** RETURN: ** sets RFLAG = 0 if success, ** = 1 if no msg or failure ** = 2 if COLLISION ** places msg in RBUF, msg source in SRC ** places length of MSG in RLEN ********************************************** ********************************************** ** ** Handshake protocol (receiver): ** ** 1. sample line ** 2. if line LOW, then sender trying to send ** 3. sample every TDELAY (= 50) until line HIGH again ** 4. send confirmation: wait TDELAY, hold line LOW for 2*TDELAY, ** 5. reset to HIGH ** 6. monitor line in a tight loop for start of transmission (i.e. LOW) ** ********************************************** OSRECV LDAA DDRC store old DDRC on stack and restore at end PSHA * * poll to see if any signal * CLR DDRC enables reads c0-c3 LDAA RFLAG complement RFLAG (for the sources mask) COMA STAA RFLAG LDAA PORTC poll PORTC ORA RFLAG only care about masked srcs (ie = 0) COMA BNE OSCHECKSRC if not zero, then got something LDAA #$01 else return 1 for failure STAA RFLAG JMP OSRECVDONE OSCHECKSRC CMPA #$02 BEQ OSSRC CMPA #$04 BEQ OSSRC CMPA #$08 BEQ OSSRC JMP OSTOOMANYSRC OSSRC STAA SRC set src of message * * wait for sender to release line (i.e. line = HIGH) * OSPOLLB * LDAA #$FE 1110 to signal got LOW * STAA PORTB LDAA PORTC wait for it to go high again ANDA SRC BNE OSCONFIRM LDX #TDELAY JSR DELAY BRA OSPOLLB * * Sending Confirmation of length 2*TDELAY * OSCONFIRM * LDAA #$F3 0011 to signal start of confirm * STAA PORTB LDAA #$0E enables write c1-c3, read c0 STAA DDRC LDX #TDELAY wait, then send confirmation JSR DELAY LDAA SRC COMA send LOW STAA PORTC LDX #TDELAY2 hold low for 2*TDELAY JSR DELAY LDAA #$FF then return to high STAA PORTC ******************************************* *** RECEPTION *** Recieve bytes in RBUF *** Number of bytes is recieved in header *** Synchronize by detecting START ZERO BIT and *** moving towards middle of the pulse *** recieve header byte first and set RLEN *** Then recieve RLEN bytes, one byte at a time ******************************************* CLR DDRC enables reads c0-c3 * * Wait for START ZERO bit * LDY #TDELAY2 OSTIGHT LDAA PORTC tight loop, wait for low (zero) ANDA SRC BEQ OSSHIFT got zero - synced DEY BNE OSTIGHT BRA OSNOPREAMBLE Timeout - did not get the first zero bit * * Shift towards middle of the pulse * (because you probably detected very close to the edge) * OSSHIFT LDX #TDELAY delay some time (tdelay comparable to send zero) JSR DELAY LDX #QTDELAY delay for 1/4 tdelay JSR DELAY *** NOTE : cycle counting from this point on * * READ HEADER = TAG(3):RLEN(5) * LDAB #$00 (2) clear B to receive header LDY #$08 (4) JSR OSREADBITS STAB RLEN (3) store TAG:LENGTH ANDB #LENMASK (2) mask out tag bits * * setup * STAB TMPC (3) store counter in memory LDX #RBUF need 2 byte address for indirect addressing STX TMPA copy address of data buffer to TMPA * * Read RLEN * 8 BITS * each delay should be comparable to send bits * OSREADBYTE LDAB #$00 (2) clear B to receive msg LDY #$08 (4) receive 8 bits LDX TMPA (4) NOP, add 6 cycles NOP (2) JSR OSREADBITS read 8 bits of data into B LDX TMPA (4) STAB 0,X (4) store B to TMPA INX (3) and increment TMPA STX TMPA (4) DEC TMPC (6) decrement counter BNE OSREADBYTE * END of cycle counting CLR RFLAG success OSRECVDONE PULA restore DDRC STAA DDRC RTS ***************************************************** * * PROCEDURE : OSREADBITS * reads 8 bits into accumulator B * Y must be set = 8 (number of bits) and B must be clear * trashes X, A, Y * OSREADBITS LSRB (2) LDAA PORTC (3) ANDA SRC bit received (3) BEQ OSRZERO if bit=0, (3) ORB #$80 recv a "1" (2) BRA OSCONTRECV (3) OSRZERO ORB #$00 recv a "0" (2) [only for cycle counting] BRA OSCONTRECV (3) [only for cycle counting] OSCONTRECV LDX #TDELAY JSR DELAY CMPA SRC (3) NOP, just to add 3 cycles DEY BNE OSREADBITS RTS ***************************************************** ** ERROR STATES ** ** FLAG = 0 if error OSNOPREAMBLE * LDAA #$F9 1001 to signal no preamble * STAA PORTB * LDY #$0F00 * JSR YDELAY LDAA #$01 failure STAA RFLAG BRA OSRECVDONE OSTOOMANYSRC * LDAA #$F6 0110 to signal collision = too many sources * STAA PORTB * LDY #$0F00 * JSR YDELAY LDAA #$02 collision failure STAA RFLAG BRA OSRECVDONE * ************************************************ * PROCEDURE: OSPOLL * * Polls for a message from any neighbor * Trashes B,X,Y * may trash RBUF/RLEN/SRC contents if new message * * ARGUMENTS: CLR RFLAG before calling * RETURNS: RFLAG = 0 received message * and SRC=source,RLEN=TAG:LENGTH,RBUF=message * (see OSRECV for more details) * : RFLAG = 1 no message * : RFLAG = 2 collision * : does not return if NEW PROGRAM ************************************************ * polls line for message using OSRECV * if RFLAG=0, then checks the TAG * if TAG=DATA, simply return to caller * if TAG=VERSION, compare version * if different, call OSRECVPROG * if TAG=OS, perform the OS call indicated in RBUF ************************************************ * * Now handles unrestricted RPC requests. * ************************************************ OSPOLL PSHA LDAA #$0E check for messages from all neighbors STAA RFLAG JSR OSRECV call OSRECV to poll line LDAA RFLAG check for success BNE OSPOLLRETURN * * check tag * if = DATA, return to caller * if = VERSION, goto OSRECVPROG * LDAA RLEN ANDA #TAGMASK BEQ OSPOLLRETURN TAG = 0 = DATA, so return CMPA #TAGVERSION BEQ GOTVERSION TAG = 001 = VERSION CMPA #TAGOS BEQ GOTOSCALL TAG = 111 = OS CALL LDAA #$01 TAG = anything else is an error STAA RFLAG = ignore the data OSPOLLRETURN PULA RTS GOTOSCALL LDAA #$01 return receipt (DATA = 0 - 1 byte) STAA SLEN LDAA SRC STAA DST LDAA #OSREQOK STAA SBUF CLR SFLAG JSR OSSEND send return receipt LDX RBUF CMPX #TAGOSRPC is it an RPC request BEQ GOTOSRPC yes - it's an RPC BRA OSPOLLRETURN there are no other requests as of yet GOTOSRPC LDAA RLEN ANDA #LENMASK CMPA #$08 RPC requests are only 8 bytes long BNE OSPOLLRETURN if not - it's an imposter LDAA OSRPCRUNNING change this to modify max OSRPCRUNNING BNE OSPOLLRETURN another RPC is running, so leave INC OSRPCRUNNING an RPC is running LDX RBUF+2 load the address LDY RBUF+4 set up registers LDD RBUF+6 load the last register JSR 0,X call it DEC OSRPCRUNNING it's no longer running LDAA #$01 STAA RFLAG act like nothing happened BRA OSPOLLRETURN and return * * GOT a VERSION # * send ack based on comparison with current version # * GOTVERSION LDAA SRC STAA DST LDAA RBUF extract version number CMPA VERSION BNE OSRECVPROG if different version, then send "yes" LDAA #NOACK setup NO message STAA SBUF LDAA #$01 STAA SLEN CLR SFLAG JSR OSSEND else same version, so send no ack LDAA #$01 ignore message and return STAA RFLAG PULA RTS OSPOLLRETURN * * Send yes acknowledgement then receive the program * call OSRECV until see program * * * Count contains the current message number. Bit 7 is 1 only if this is the * last message to be sent. OSRECVPROG CLR COUNT starting over if from bad transmit LDS #STACK-6 set stack to known state OSRPBIGLOOP INC COUNT start with one LDAA COUNT STAA PORTB CMPA #$01 is this the first time? BNE OSRPSENDACK if not, send count LDAA #YESACK setup YES message (diff version #'s) OSRPSENDACK STAA SBUF send one byte of data to DST LDAA #$01 STAA SLEN CLR SFLAG JSR OSSEND LDX #$FFFF wait 65535 times for program message OSRPGETMESSAGE LDAA DST get program only from sender STAA RFLAG by setting RFLAG mask PSHX JSR OSRECV PULX LDAA RFLAG BEQ OSRPGOTMESSAGE have I got the message DEX if not, time out BEQ OSRUNUSERPROG if timed out, try to run the program BRA OSRPGETMESSAGE otherwise, try again OSRPGOTMESSAGE LDAA RBUF TAB ANDB #$7F strip bit 7 CMPB COUNT are they in sync? BNE OSRUNUSERPROG no - try to run program STAA COUNT yes they are in sync - good! LDAA DST save destination (wiped out by RAMPROG) PSHA ******************************************* * Program has been recieved * copy New Program to EEPROM * CHANGE LATER TO GET THE PROGRAM IN CHUNKS * * RAMPROG = RAM program that copies RBUF contents to EEPROM * First place RAMPROG in RAM (at location LOCRAMPROG) * Then transfer execution to RAM (jsr LOCRAMPROG) * copy RBUF contents to USERPROG * return execution to EEPROM (rts) * then clear stack of OSPOLL return address and jump to USERPROG ******************************************* * * copy RAMPROG to EEPROM * LDD #RAMENDPROG SUBD #RAMBEGINPROG RAMPROG must be < 256, so TBA put low 8 bits of program length in A LDX #RAMBEGINPROG x = EEPROM address to read RAMPROG program LDY #LOCRAMPROG y = RAM address to store program JSR OSCOPY * locate version number - try to send to neighbors * * execute program in RAM * OSRUNPROG LDX #LOCRAMPROG start address of RAM program JSR 0,X jump to RAM subroutine PULA restore the destination STAA DST (wiped out by RAMPROG) LDAA COUNT so, are we done? BNE OSRPBIGLOOP bottom of loop LDS #STACK restore stack to good condition * * returned from RAM * OSRUNUSERPROG LDAA EECOUNT check to see if program is valid BEQ OSRUPGOOD if not, far jump to RPC Request Program STAA PORTB flash count # JMP OSREQPROG ask for program OSRUPGOOD LDAA USERPROG load version number STAA VERSION store program version number in heap STAA PORTB display VERSION # CLR DDRC enable reads from c0-c3 LDX #$0200 run OSSENDPROG 512 times OSRUPSPLOOP PSHX JSR OSSENDPROG attempt to send program to neighbors PULX DEX BNE OSRUPSPLOOP CLRB initialize everything CLRA CLR PORTB CLR RBUF no RPC calls can be running while CLR OSRPCRUNNING a new program is loaded LDX #USERPROG+1 JMP 0,X start newly loaded user program ***-------------------------------------------*** * Beginning of RAM program to copy msg to EEPROM ***-------------------------------------------*** RAMBEGINPROG BSR RAMCOPYCOUNT save count - program about to be written LDX #RBUF+1 copy from RBUF, skip over the count LDAA COUNT calculate place to put in memory ANDA #$7F mask off high bit DECA formula: (COUNT-1)*(LENMASK-1)+#USERPROG LDAB #LENMASK-1 MUL ADDD #USERPROG +#USERPROG XGDY store the address to copy to RAMGOTINDX LDAA RLEN DECA count is not copied and takes a byte ANDA #LENMASK A = length BSR RAMWRITE copy it LDAA COUNT check to see if transfer done ANDA #$80 BEQ RAMRETURN if not, just return CLR COUNT clear the count - transfer done BSR RAMCOPYCOUNT store it in EEPROM RAMRETURN RTS return to EEPROM caller RAMCOPYCOUNT LDX #COUNT LDY #EECOUNT LDAA #$01 BSR RAMWRITE RTS RAMWRITE LDAB #$16 STAB PPROG set to BYTE erase mode STAB 0,Y write junk data to address to be erased INCB STAB PPROG turn on high voltage BSR RAMWAIT10MS wait 10ms CLR PPROG LDAB #$02 STAB PPROG set to BYTE program mode LDAB 0,X get byte from RAM STAB 0,Y write byte into EEPROM LDAB #$03 STAB PPROG set EELAY and EEPGM BSR RAMWAIT10MS wait 10ms CLR PPROG INY INX DECA BNE RAMWRITE continue to next loop iteration RTS RAMWAIT10MS PSHX LDX #D10ms RAMWAITLOOP DEX BNE RAMWAITLOOP PULX RTS RAMENDPROG NOP * ************************************************ * PROCEDURE: OSSENDPROG * * attempt to send a program to all your neighbors * ARGUMENTS : * RETURNS: nothing * * foreach neighbor * send version number * get back yes/no (abort after timeout) * if yes, copy userprog into SBUF and OSSEND * ************************************************* * * STEP 1: Send Version Number * loop through neighbors to see if anyone needs a new program * OSSENDPROG LDAB #$02 loop through all neightbors LDAA EECOUNT check to see if program is valid BNE OSSENDPGMRET don't send it if it ain't valid NBRLOOP LDAA VERSION SBUF = VERSION number STAA SBUF LDAA #$01 length of the message = 1 (for version #) ORA #TAGVERSION STAA SLEN STAB DST attempt to send to all 3 neighbors PSHB push B on stack CLR SFLAG JSR OSSEND SEND version number LDAA SFLAG if send was successful, wait for ack BNE DONTTRANSMIT CLR COUNT LDX #$1000 poll 4096 times for initial ack WAITACKLOOP LDAA DST only listen to msgs from destination STAA RFLAG PSHX push X on stack JSR OSRECV PULX restore value of X LDAA RFLAG check if received successfully BEQ ACKRECEIVED DEX poll for ack again BNE WAITACKLOOP * * Neighbor did not respond * DONTTRANSMIT LDAA #$F6 show 0110 if not successful * * Try the next neighbor * SENDNEXTNBR PULB restore value of B for counting nbrs LSLB CMPB #$10 sent to all 3 neighbors BNE NBRLOOP OSSENDPGMRET RTS * * if ack = yes, then transmit program * ACKRECEIVED INC COUNT start with one LDAA COUNT CMPA #$01 is it the first time - yesack BEQ YESACKRECV CMPA RBUF in sync? BEQ OSSENDPROGBYTES yes - go ahead PULB no - restore count BRA NBRLOOP try the whole thing again YESACKRECV LDAA #YESACK CMPA RBUF YESACK received? BNE SENDNEXTNBR if not, go to the next neighbor OSSENDPROGBYTES LDAA COUNT STAA PORTB DECA LDAB #LENMASK-1 MUL ADDD #USERPROG XGDX OSSPCHKEND PSHX XGDX PULX ADDD #LENMASK-1 CPD #USERPROGEND BLO OSSPSEND LDAA COUNT ORA #$80 STAA COUNT OSSPSEND LDY #SBUF+1 Y stores the send buffer address LDAA #LENMASK A stores the counter TAB ORB #TAGPROGRAM STAB SLEN store the length + tag of the message DECA JSR OSCOPY length to copy is one less (minus count) LDAA COUNT STAA SBUF store COUNT as first byte CLR SFLAG JSR OSSEND send message LDAA COUNT ANDA #$80 is it the last message? BNE SENDNEXTNBR if so, go to the next neighbors LDX #$FFFF poll 65535 times while neighbor is processing BRA WAITACKLOOP for another (mid-program) ack ***************************************************** * * Utilities * ***************************************************** * ************************************ * * PROCEDURE: OSCOPY * * args: X = from address * Y = to address * A = number of bytes ************************************ OSCOPY LDAB 0,X STAB 0,Y INY INX DECA BNE OSCOPY RTS ** DELAY PROCEDURES ** * * DELAY delays for X DELAY DEX BNE DELAY RTS * * DELAYPOLL delays for Y=700 and polls for programs. * DELAYPOLL STAA PORTB LDY #$0700 DPOLLLOOP LDX #$00FF BSR DELAY PSHY CLR RFLAG JSR OSPOLL PULY DEY BNE DPOLLLOOP RTS * * LIGHTDELAY shows lights in A, and then delays for Y=0700. * It does not poll. * LIGHTDELAY STAA PORTB LDY #$0700 * * Y*256 CONTAINS THE FACTOR YDELAY LDX #$00FF (Also called by OS) BSR DELAY DEY BNE YDELAY RTS * ************************** DELAYINT ******************************* * Delay, but be open to INTerruptions (ie. DATA) coming in * This delay will automatically change the phase of reception. * * Requires: * B=0 Assume a standard delay of $0400, and display A in lights * B=1 Specify a delay factor * Y= Delay factor if B=1 * Returns: * B=0 Nothing interesting happened * B=1 A message was received (SRC contains source) * B=2 A new message was received * B=3 The new message has a different stamp on it * B=4 The light sensor was activated * B=5 The button was pressed * Y= When interruption occurred ******************************************************************* DELAYINT CMPB #$01 BEQ DINTLOOP STAA PORTB LDY #$0400 DINTLOOP PSHY JSR POLLNEWMESSAGE PULY CMPB #$00 BNE DINTMESSAGE BSR SENSOR CMPB #$00 BNE DINTSENSE PSHY PULX BSR DELAY DEY BNE DINTLOOP CLRB Nothing interesting happened RTS DINTSENSE ADDB #$03 A sensor was hit DINTMESSAGE RTS A message was received * * SENSOR returns: * B=0 Nothing interesting happened * B=1 The light sensor was hit * B=2 The button was pressed * SENSOR LDAB PORTC BITB #$01 BEQ SENSELIGHT BITB #$80 BEQ SENSEBUTTON CLRB RTS SENSEBUTTON LDAB #$02 RTS SENSELIGHT LDAB #$01 RTS * * LESSAB returns the lessor of A and B in A and the greator in B * this SHOULD be a machine-language instruction! * LESSAB CBA BLS LESSRETURN PSHB TAB PULA LESSRETURN RTS * * Broadcast messages * * * RESEND broadcasts the message in RBUF * * Requires: * Y= How many times to broadcast * Returns: * B=0 Nothing interesting happened * B=1 A message was received * B=2 A new message was received * B=3 The new message has a different stamp * B=4 The light sensor was hit * B=5 The button was pressed * Y= When the interruption occurred * RESEND PSHA PSHY LDX #RBUF LDY #SBUF JSR MSGCOPY LDAA RLEN STAA SLEN CLRB PULY JSR BROADCAST PULA RTS * * STAMP identifies this machine as the originator of the message in SBUF. * STAMP PSHA PSHB INC MSGID new MSGID for new message LDAA MSGID STAA OUTMSGID stamp message ID LDD SENDID STD OUTMSGSENDID stamp machine ID PULB PULA RTS * * Send an OS Remote Procedure Call (proxy) to invoke any (unrestricted) * subroutine on another machine. * * * OSRPC requires destination in DST * X=address for remote machine to JSR to (preserved) * A,B,Y are copied to remote machine * if DST = 0, then the message is broadcasted * * Returns: * B=0 RPC received successfully * B=1 RPC timed out (approx. 1 second) * B=2 RPC misinterpreted (uh-oh!) OSRPC PSHA PSHX PSHY STX SBUF+2 store address for remote machine STY SBUF+4 mirror Y on other end STD SBUF+6 mirror A, B on other end LDX #TAGOSRPC invoke a remote subroutine STX SBUF put the OS call in the buffer LDAA #$08 8 byte message STAA SLEN JSR OSREQUEST make the request PULY PULX PULA RTS * * Examples of RPC: * * * OSREQPROG requests a program to be transferred and never returns * OSREQPROG LDX #OSSENDPROG CLR DST BSR OSRPC JSR DELAYPOLL BRA OSREQPROG * * OSSETRAM stores the value of A in the memory address pointed to by Y * used by RPC to set memory locations * * To set a memory location, simply set DST to neighbor or 0 to broadcast: * LDX #OSSETMEM * JSR OSRPC * OSSETRAM STAA 0,Y RTS * * OSREQUEST sends an OS request and waits for a return receipt * DST=destination, if DST=0, message is broadcasted * SBUF=message * * Returns: * B=0 Request received successfully * B=1 Request timed out (approx. 1 second for broadcast) * B=2 Request misinterpreted (uh-oh!) * OSREQUEST PSHA LDAA SLEN ORA #TAGOS tag it as an OS request STAA SLEN LDAA #OSREQOK LDY #$0100 JSR SENDCHECKRET PULA RTS * * SENDCHECKRET * * Broadcast or send a message while waiting for a return receipt * * Requires: * A=return receipt to check for * Y=timeout * SBUF=message to send * DST=destination, if DST=0 then the message is broadcasted * SENDCHECKRET PSHA LDAA DST is DST=0? BEQ SCHKBROAD broadcast SCHKSEND PSHY CLR SFLAG otherwise, just send it to DST JSR OSSEND send it LDAA DST STAA RFLAG JSR OSPOLL check for response (only from DST) PULY LDAA RFLAG BEQ SCHK message received DEY BNE SCHKSEND not time out - try again BRA SCHKTIMEOUT timed out SCHKBROAD CLRB message in SBUF BSR BROADCAST broadcast it CMPB #$00 BEQ SCHKTIMEOUT timed out CMPB #$04 sensor hit BHS SCHKBROAD try it again until timeout SCHK LDAA RLEN message received CMPA #$01 one byte of data? PULA BNE SCHKMIS no - message misinterpreted CMPA RBUF correct return receipt? BNE SCHKMIS CLRB successful RTS SCHKMIS LDAB #$02 message has been misinterpreted RTS uh-oh SCHKTIMEOUT PULA return stack LDAB #$01 timed out RTS * **************************************************************************** * BROADCAST requires: * B=0 Broadcast a message in SBUF of length SLEN * B=1 Broadcast a number in A (preserves A) * Y= How many times to broadcast (#$1000 takes approx. 10 sec.) * It continuously broadcasts and returns: * B=0 Nothing interesting happened * B=1 A message was received * B=2 A new message was received * B=3 The new message has a different stamp * B=4 The light sensor was hit * B=5 The button was pressed * Y= When the interruption occurred * * This section would, ideally, become a background task kicked off or managed * from a high level by the program and run by the OS using interrupts or a * separate thread. **************************************************************************** BROADCAST PSHA Load B into X CLRA XGDX PULA BLOOP PSHY PSHX DEX Figure out what you're broadcasting. BEQ BNUM BSR BROADMESS BRA BPOLL BNUM BSR BROADNUM BPOLL BSR POLLNEWMESSAGE PULX PULY CMPB #$00 BNE BRETURN A new message came in JSR SENSOR CMPB #$00 BNE BSENSE DEY BNE BLOOP CLRB Nothing interesting happened BRETURN RTS BSENSE ADDB #$03 RTS * * BROADNUM takes a number in A to broadcast (preserves A) * BROADNUM PSHA STAA SBUF LDAA #$01 STAA SLEN BSR BROADMESS PULA RTS * * BROADMESS trashes all reg's - need to add error detection/handling * BROADMESS takes: * length of MSG in SLEN * msg in SBUF * BROADMESS LDAB #$02 loop through all neighbors NEIGHBORS PSHB CLR SFLAG STAB DST send to all three neighbors JSR OSSEND PULB LSLB CMPB #$10 BNE NEIGHBORS RTS * * POLLNEWMESSAGE takes a message in RBUF and uses MSGCOMPARE to compare it * with the new message coming in. * * Returns: * B=0 no message received * B=1 message received (hmmm... broken???) * B=2 new message received * B=3 new message has different stamp * trashes X, Y * POLLNEWMESSAGE LDX #RBUF Save the message LDY #TMPMESSAGE PSHA PSHX PSHY LDAA RLEN ANDA #LENMASK JSR OSCOPY Copy it CLR RFLAG JSR OSPOLL Poll for messages PULY PULX LDAA RLEN ANDA #LENMASK LDAB RFLAG BNE PNOMESSAGE Was there a message? BSR MSGCOMPARE Is it the same as the old RBUF? PMESSAGE INCB PULA RTS PNOMESSAGE CLRB PULA RTS * * MSGCOMPARE requires: * X=Pointer to first message * Y=Pointer to second message * A=Length of message * and returns: * B=0 messages are the same * B=1 messages are different * B=2 messages have different stamps * MSGCOMPARE PSHA ANDA #LENMASK Mask off type BSR OSCOMPARE CMPB #$00 Are the two messages the same? BEQ MSGCOMPRETURN PSHX Check stamps PSHY XGDX ADDD #OUTMSGID-#SBUF XGDX XGDY ADDD #OUTMSGID-#SBUF XGDY LDAA #$01 BSR OSCOMPARE CMPB #$00 Are the two message ID's the same PULY PULX BNE MSGDIFFSTAMP PSHX PSHY XGDX ADDD #OUTMSGSENDID-#SBUF XGDX XGDY ADDD #OUTMSGSENDID-#SBUF XGDY INCA This one is two bytes BSR OSCOMPARE PULY PULX INCB CMPB #$01 Are the two sender ID's the same BEQ MSGCOMPRETURN MSGDIFFSTAMP LDAB #$02 MSGCOMPRETURN PULA RTS * * MSGCOPY takes a message pointed to by X and copies it to Y * MSGCOPY PSHA LDAA #LENMASK JSR OSCOPY PULA RTS * * OSCOMPARE compares two buffers pointed to by X and Y, of length A * and returns: * B=0 buffer contents are the same * B=1 buffer contents are different * OSCOMPARE PSHX PSHY PSHA OSCOMPLOOP LDAB 0,X CMPB 0,Y BNE OSCOMPDIFF INY INX DECA BNE OSCOMPLOOP CLRB The messages are the same B=0 BRA OSCOMPRETURN OSCOMPDIFF LDAB #$01 The messages are different B=1 OSCOMPRETURN PULA PULY PULX RTS * * User configuration section - routines to allow user to configure demo * * * SETNUM allows the user to set the number in A * (light increments number, button sets it) * * * CONFIGNUM applies the mapping below to the number obtained by SETNUM * * Requires: number to change in B * Returns: changed number in B * * Note: B is used to facilitate further mappings with the D register * SETNUM STAA PORTB display the number LDY #$0400 pick a pleasing amount of time to JSR YDELAY delay for cycle to pick number SETSENSE JSR SENSOR CMPB #$01 the light was hit BNE NOLIGHT INCA ANDA #$0F BRA SETNUM NOLIGHT CMPB #$02 button was hit, so end BNE SETSENSE RTS DONE BRA DONE DONE has to be at the end of the file ORG $FFFE FDB RESET *