*
* 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
*