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