TCNT = 0x100E
OUT_PORT = 0x1008
DATA_RATE = 1000 ; bits per second
TOC_COUNT = 1000 ; 2000000 / DATA_RATE / 2
TOC3 = 0x101A
TCTL1 = 0x1020
TFLG1 = 0x1023
MOTORS = 0x7000
LEFT_FORWARD = 0b00100000
LEFT_BACKWARD = 0b00100010
RIGHT_FORWARD = 0b00010000
RIGHT_BACKWARD = 0b00010001
OFF = 0x0
RIGHT_ANGLE_TIME = 16
CMD_START_RECEIVE = 0b1 ; 1
CMD_STOP_RECEIVE = 0b10 ; 2
CMD_FORWARD = 0b100 ; 4
CMD_BACKWARD = 0b101 ; 5
CMD_TURN_LEFT = 0b110 ; 6
CMD_TURN_RIGHT = 0b111 ; 7
CMD_ROTATE_LEFT = 0b1000 ; 8
CMD_ROTATE_RIGHT = 0b1001 ; 9
CMD_DRAW_SQUARE = 0b1010 ; 10
CMD_DRAW_SHAPE = 0b1011 ; 11
CMD_WAIT = 0b00001100 ; 12
TCTL2 = 0x1021 ;[edg4b, edg4a, edg1a, edg1b, edg2b, edg2a, edg3b, edg3a]
TMSK1 = 0x1022 ;[7, 6, 5, 4, 3, IC1, IC2, IC3]
TFLG1 = 0x1023 ;[7, 6, 5, 4, 3, IC1F, IC2F, IC3F]
TIC3DATA = 0x1014
TIC3VECTOR = 0xBFEA
IN_PORT = 0x1000
IN_PORT_MASK = 1
MY_ADDRESS = 0b101
;==================
; READ-ONLY DATA
;==================
.section .rodata
; header: slave address, master address, data length, reserved, reserved, reserved
samplePacket: .byte 0b00001000, 0, samplePacketEnd-samplePacketData, 0, 0, 0
; data
samplePacketData: .byte CMD_START_RECEIVE, CMD_FORWARD, 15, CMD_BACKWARD, 15
;.byte CMD_TURN_RIGHT, 10, CMD_TURN_LEFT, 10, CMD_ROTATE_RIGHT, 15, CMD_ROTATE_LEFT, 15
;.byte CMD_DRAW_SQUARE, CMD_WAIT, 20, CMD_DRAW_SHAPE
.byte CMD_FORWARD, 1
.byte CMD_WAIT, 1
.byte CMD_FORWARD, 1
.byte CMD_WAIT, 1
.byte CMD_FORWARD, 1
.byte CMD_WAIT, 1
.byte CMD_STOP_RECEIVE
samplePacketEnd:
myAddress: .byte MY_ADDRESS
;==================
; DATA
;==================
.section .data
time: .byte 20 ; an argument to the wait subroutine
startReceiveMode: .byte 0 ; boolean for "are we in Start Receive mode?"
doCommandMode: .byte 0 ; boolean for "should we be doing commands?"
rbInsertionPointer: .word ringBuffer
rbExtractionPointer: .word ringBuffer
rbCount: .byte 0 ; how many command-time pairs are in the ring buffer
receiving: .byte 0 ; boolean - are we currently receiving data?
lastTIC: .word 0 ; what was the last TIC value
mask: .byte 0b10000000 ; which bit are we on?
byteCount: .byte 0 ; how many bytes have we received?
;==================
; UNINITIALIZED
;==================
.section .bss
; The packet contains all the bytes sent in one packet.
; The receiver ISR puts the bytes here.
; It is the job of decodePacket to move command-time pairs to the ring buffer
thePacket: rmb 261 ; max packet size is 261 bytes
; The ring buffer has ONLY command-time pairs. If a DrawSquare or DrawShape is received, the decodePacket fakes a time
ringBuffer: rmb 200 ; reserve 200 bytes for ring buffer
ringBufferEnd:
;==================
; TEXT
;==================
.section .text
.global _start
_start:
lds #_stack
ldaa #0b00000011 ; set both edges on edg3
staa TCTL2
ldaa #1
staa TMSK1 ; set enable TIC3
ldaa #1 ; clear interrupt flag
staa TFLG1
; set up jump table entry
ldx #0x00E2
ldaa #0x7E
staa 0,x
ldy #theISR
sty 1,x
ldx #theISR
stx TIC3VECTOR
; set DDRD for transmitting
ldaa #0b00111111
staa 0x1009
ldaa #255
staa OUT_PORT ; quiet voltage is high
cli
9:
ldaa 0x7FFF
anda #0b10000000
bne 1f
; execute this code if the start button is pressed
; send some data
jsr sendZero ; start bit
ldx #samplePacket
ldab #(samplePacketEnd-samplePacket)
2:
ldaa 0,x
jsr sendByte
inx
decb
bne 2b
jsr sendOne ; stop bit
jsr sendOne ; stop bit
3: ; debounce loop
ldaa 0x7FFF
anda #0b10000000
beq 3b
jsr wait100ms
1:
ldaa 0x7FFF
anda #0b01000000
bne 4f
; execute this code if the stop button is pressed
ldaa #1
staa doCommandMode ; no need to debounce this one
4:
jsr doCommand ; do a command, if there is one, and if ; doCommmandMode!=0
jsr stopMotors
bra 9b ; loop forever
; The interrupt subroutine
theISR:
sei
ldaa receiving
bne 1f
; this is the start edge we just got
ldaa #0x76
staa receiving
ldaa #0
staa byteCount
ldaa #0b10000000
staa mask
bra theISRPreExit
1:
; not the start edge
ldd TIC3DATA
subd lastTIC ; thisTIC - lastTIC -> D
cmpa #6 ; if A<6 then D<1536 and this edge is not a bit
bge 2f
bra theISRExit ; throw away this edge
2:
; it is a bit
ldab byteCount
ldx #thePacket
abx ; get x to point to the byte
ldaa mask
cmpa #0b10000000
bne 101f
clra
staa 0,x ; if it is a new byte, clear it
101:
ldab IN_PORT
andb #IN_PORT_MASK
beq 3f
; it is a 1
ldaa 0,x
oraa mask
staa 0,x
bra 4f
3:
; it is a 0
ldaa 0,x
oraa mask
eora mask
staa 0,x
4:
; adjust the mask
ldaa mask
clc
rora
bcc 5f ; if the mask was not 0000 0001
ldaa #0b10000000 ; the mask was 1 reset to 1000 0000
ldab byteCount ; and increment the byte count
incb
stab byteCount
5:
staa mask
; if the packet is complete call decodePacket
ldab byteCount
cmpb #3 ; byte three is the data length
blo 6f ; packet obiously not complete
ldx #thePacket
ldaa 2,x ; Data length -> A
adda #6 ; packet length -> A
suba byteCount
bne 6f ; packet not complete
; check if we've gotten two stop bits
;ldab byteCount
;ldx #thePacket
;abx
;ldaa 0,x
;cmpa #0xC0
ldaa mask
anda #0b00100000
beq 6f
; OK, we have a whole packet
ldaa #0
staa receiving
staa byteCount
ldaa #0b10000000
staa mask
jsr decodePacket
6:
theISRPreExit:
ldx TIC3DATA
stx lastTIC
theISRExit:
ldaa TFLG1
oraa #1
staa TFLG1
rti
sendByte:
;byte is in A
bita #0b10000000
beq 1f
jsr sendOne
bra 2f
1:
jsr sendZero
2:
bita #0b01000000
beq 3f
jsr sendOne
bra 4f
3:
jsr sendZero
4:
bita #0b00100000
beq 5f
jsr sendOne
bra 6f
5:
jsr sendZero
6:
bita #0b00010000
beq 7f
jsr sendOne
bra 8f
7:
jsr sendZero
8:
bita #0b00001000
beq 9f
jsr sendOne
bra 10f
9:
jsr sendZero
10:
bita #0b00000100
beq 11f
jsr sendOne
bra 12f
11:
jsr sendZero
12:
bita #0b00000010
beq 13f
jsr sendOne
bra 14f
13:
jsr sendZero
14:
bita #0b00000001
beq 15f
jsr sendOne
bra 16f
15:
jsr sendZero
16:
rts
sendOne:
psha
pshb
ldaa #0
staa OUT_PORT
ldd TCNT
addd #TOC_COUNT
std TOC3
ldaa TFLG1
oraa #0b00100000
staa TFLG1
1:
ldaa TFLG1
anda #0b00100000
beq 1b
ldaa #255
staa OUT_PORT
ldd TCNT
addd #TOC_COUNT
std TOC3
ldaa TFLG1
oraa #0b00100000
staa TFLG1
2:
ldaa TFLG1
anda #0b00100000
beq 2b
pulb
pula
rts
sendZero:
psha
pshb
ldaa #255
staa OUT_PORT
ldd TCNT
addd #TOC_COUNT
std TOC3
ldaa TFLG1
oraa #0b00100000
staa TFLG1
1:
ldaa TFLG1
anda #0b00100000
beq 1b
ldaa #0
staa OUT_PORT
ldd TCNT
addd #TOC_COUNT
std TOC3
ldaa TFLG1
oraa #0b00100000
staa TFLG1
2:
ldaa TFLG1
anda #0b00100000
beq 2b
pulb
pula
rts
decodePacket:
pshy
pshx
psha
pshb
ldx #thePacket
ldaa 0,x
cmpa myAddress
beq 1f ; the address is ours
cmpa #0b00001111
beq 1f ; the address is the broadcast address
bra decodePacketExit ; this packet is not intended for us
1: ; this packet is intended for us
inx
; don't need to do anything w/ master address for now
inx
ldab 0,x ; store data length in B
; next 3 are reserved
inx
inx
inx
; Hacked in because the start receive must be first if we're not in start receive mode
ldaa startReceiveMode
bne 9f
inx ; if we are not in start mode...
ldaa 0,x
cmpa #CMD_START_RECEIVE
bne decodePacketExit ; we need to exit if the first byte is not a StartReceive command
dex
9:
; The Main Loop of this subroutine
; current command is at X+1
; remaining data length is in B
2:
inx
ldaa 0,x ; get command in A
; CASE 1: start receive
cmpa #CMD_START_RECEIVE
bne 3f
ldaa #0
staa rbCount ; set count=0
ldaa #1
staa startReceiveMode ; set startReceiveMode = 1
ldy rbInsertionPointer
sty rbExtractionPointer ; move extraction pointer to insertion pointer
decb
beq decodePacketExit ; last byte? then exit
bra 2b ; loop
3:
; CASE 2: stop receive
cmpa #CMD_STOP_RECEIVE
bne 4f
ldaa #0
staa startReceiveMode ; set startReceiveMode = 0
decb
beq decodePacketExit ; last byte? then exit
bra 2b ; loop
4:
; CASE 3: command-time pair, but not in startReceiveMode
; TODO this case can be eliminated since the startReceive must come at the beginning of a packet
; WARNING note the CASE 4 pula
psha
ldaa startReceiveMode
bne 5f
pula
decb ; first byte of command-time pair
inx
decb ; second byte of command-time pair
beq decodePacketExit
bra 2b ; loop
5: ; CASE 4: command-time pair in startReceiveMode WARNING must pula from case 3
pula
ldy rbInsertionPointer
staa 0,y ; store first byte
iny ; increment insertion pointer
cpy #ringBufferEnd
bne 81f
ldy #ringBuffer
81:
cmpa #CMD_DRAW_SQUARE ; Pay Attention!!! - we're faking time arguments for the two commands that don't come with times
beq noTimeProvided
cmpa #CMD_DRAW_SHAPE
beq noTimeProvided
; a time argument is provided by the packed
decb
inx
ldaa 0,x ; get next byte
staa 0,y ; store second byte
bra 6f
; no time argument is provided by the packet, so let's fake it
noTimeProvided:
ldaa #20 ; 2 seconds per side should be OK
staa 0,y ; store second byte (faked)
6:
iny
cpy #ringBufferEnd
bne 82f
ldy #ringBuffer
82:
sty rbInsertionPointer ; save moved insertion pointer
ldaa rbCount
inca
staa rbCount ; rbCount++
decb
bne 2b ; more bytes? then loop
decodePacketExit:
pulb
pula
pulx
puly
rts
; If rbCount > 0 this subroutine will
; Pick the command from the ring buffer (1 byte)
; Pick the time from the ring buffer (1 byte)
; Do the command for the amount of time
; rbExtractionPointer += 2
; rbCount--
doCommand:
pshx
psha
pshb
ldaa doCommandMode
beq doCommandExit ; if doCommandMode == 0, exit
ldaa rbCount
beq doCommandExit ; if count == 0, exit
deca
staa rbCount ; rbCount--
ldx rbExtractionPointer
ldaa 0,x ; store command in A
inx
cpx #ringBufferEnd
bne 61f
ldx #ringBuffer
61:
ldab 0,x ; store time in B
inx
cpx #ringBufferEnd
bne 62f
ldx #ringBuffer
62:
stx rbExtractionPointer ; rbExtractionPointer += 2
stab time ; pass the time argument to whatever function will be called
deca
bne 1f
; start receive
1:
deca
bne 2f
; stop receive
2:
deca
bne 3f
; nothing
3:
deca
bne 4f
jsr moveForward
4:
deca
bne 5f
jsr moveBackward
5:
deca
bne 6f
jsr turnLeft
6:
deca
bne 7f
jsr turnRight
7:
deca
bne 8f
jsr rotateLeft
8:
deca
bne 9f
jsr rotateRight
9:
deca
bne 10f
pshx
ldx #1000
jsr drawSquare
pulx
10:
deca
bne 11f
pshx
ldx #1000
jsr drawShape
pulx
11:
deca
bne 12f
jsr stopMotors ; wait command
jsr wait
12:
doCommandExit:
ldaa rbCount
bne 2001f
ldaa #0
staa doCommandMode ; clear doCommandMode if the ring buffer is empty
2001:
pulb
pula
pulx
rts
stopMotors:
psha
ldaa #OFF
staa MOTORS
pula
rts
moveForward:
psha
ldaa #(LEFT_FORWARD | RIGHT_FORWARD)
staa MOTORS
pula
jsr wait
rts
moveBackward:
psha
ldaa #(LEFT_BACKWARD | RIGHT_BACKWARD)
staa MOTORS
pula
jsr wait
rts
turnLeft:
psha
ldaa #(RIGHT_FORWARD)
staa MOTORS
pula
jsr wait
rts
turnRight:
psha
ldaa #(LEFT_FORWARD)
staa MOTORS
pula
jsr wait
rts
rotateLeft:
psha
ldaa #(LEFT_BACKWARD | RIGHT_FORWARD)
staa MOTORS
pula
jsr wait
rts
rotateRight:
psha
ldaa #(LEFT_FORWARD | RIGHT_BACKWARD)
staa MOTORS
pula
jsr wait
rts
drawSquare:
psha
pshb
pshy
; do 4 times
ldaa #4
1:
; turn left
ldy time
ldab #RIGHT_ANGLE_TIME
stab time ; set time
jsr turnLeft
sty time ; restore time
; drive forward
jsr moveForward
deca
bne 1b
; return
puly
pulb
pula
rts
drawShape:
psha
pshb
pshy
; do 3 times
ldaa #3
1:
; turn left
ldy time
ldab #RIGHT_ANGLE_TIME * 120 / 90
stab time ; set time
jsr turnLeft
sty time ; restore time
; drive forward
jsr moveForward
deca
bne 1b
; return
puly
pulb
pula
rts
; This subroutine waits for the number of tenths of seconds
; specified by the "time" variable
wait:
psha
ldaa time
1:
jsr wait100ms
deca
bne 1b
pula
rts
wait100ms:
pshx
ldx #100
1:
bsr wait1ms
dex
bne 1b
pulx
rts
wait1ms:
psha ; 1 ms wait
tpa
psha
pshx
ldx #200
2: dex
nop
nop
bne 2b
pulx
pula
tap
pula