|
|
(16 intermediate revisions by 2 users not shown) |
Line 1: |
Line 1: |
| Status so far: | | Status so far: |
| | | |
− | * 3D Bresenham line drawing algorithm has been programmed on-chip. | + | * 3D [http://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm Bresenham line drawing algorithm] has been programmed on-chip. |
| * text parser has been implemented | | * text parser has been implemented |
− | * serial port VP has '''not''' been integrated yet | + | * serial port VP has been integrated |
− | * keyboard and switch driver has '''not''' been implemented yet. | + | * keyboard and switch driver has been implemented |
| + | * "emergency stop" and end-keys have been implemented in a way. Currently movement just stops if one of these keys is pressed. |
| | | |
− | See the source code below.
| + | You can view the source [[3d stepper source|here]]. |
| | | |
− | ;=======================================================================
| + | |
− | ;TITLE: stepper.src
| + | == So What Does It Do == |
− | ;
| + | |
− | ;PURPOSE: control up to 3 stepper motors to move in straight lines
| + | The firmware assumes the following: |
− | ; in space. Take commands from a serial port.
| + | * Three stepper motors connected to rc (x and y) and rb.4-7 (z) |
− | ;
| + | * Some serial communications device attached to ra.3 (SX sending) and rb.0 (SX receiving). The software is configurable to work either through a MAX232 ic or by connecting the pins directly to some rs-232 device. In the latter case, out- and input actions become active-low. |
− | ;AUTHOR: Danny Havenith
| + | |
− | ; Copyright (c) 2006 Danny Havenith
| + | The firmware communicates through the serial port and assumes a 9600bps 8N1 connection (this is configurable, but must be tuned with the clock frequencies, because some some bitrates are not possible with some clock speeds). |
− | ; Use, modification and distribution is subject to the Boost Software
| + | |
− | ; License, Version 1.0. (See copy at
| + | At boot, the SX will send a welcome message and a prompt. The other end of the communciations line (the driver) is supposed to send ascii commands over the serial line, separated by newlines (ascii code 13). The SX will only accept commands when it has sent a prompt charachter ('>'). The driver should therefore always wait for a prompt before sending a command. Internally, there is one command buffer, so that the SX can execute one command and receive the next command at the same time. |
− | ; http://www.boost.org/LICENSE_1_0.txt)
| + | |
− | ;
| + | The ultimate (and currently the only-) command is the 'move' command: |
− | ;REVISIONS:
| + | |
− | ;
| + | m shhhh,shhhh,shhhh |
− | ;CONNECTIONS:
| + | |
− | ; ra.0-2 3 keyboard row outputs
| + | 'm' denotes the 'move' command and directs the controller to perform a number of half-steps on each axis. 's' stands for an optional sign character, 'h' means an optional hex digit. Instead of comma's, any non-hex character can be used. There is no need for whitespace after the command, or in fact at any location at all. Examples of valid move commands are: |
− | ; rb.1-3 3 keyboard column inputs for keyboard matrix
| + | |
− | ; ra.0 is the row output for the 'emergency keys'
| + | m0,ff,c0 |
− | ; (min, max, emergency stop) and is always enabled when motors move
| + | M1FX2fX3f |
− | ; This pin is also connected to a 'busy'-led.
| + | m de,-10,-ff |
− | ; ra.3 serial in
| + | mde-10-ff |
− | ; rb.0 serial out
| + | |
− | ; rb.4-7 Z-axis stepper motor output
| + | |
− | ; rc.0-3 X-axis stepper motor output
| + | Movement is specified in half-steps, relative to the current position. |
− | ; rc.4-7 Y-axis stepper motor output
| |
− | ;
| |
− | ;
| |
− | ;DETAILS:
| |
− | ; Contains 2 VPs: 1) stepper motor controller 2) serial in/out
| |
− | ;=======================================================================
| |
− |
| |
− |
| |
− | ;-------------------------- DEVICE DIRECTIVES --------------------------
| |
− |
| |
− | DEVICE SX28,OSCHS1,TURBO
| |
− | DEVICE STACKX, OPTIONX
| |
− |
| |
− | IRC_CAL IRC_SLOW
| |
− |
| |
− | RESET Initialize
| |
− | ;------------------------------ CONSTANTS ------------------------------
| |
− | Frequency EQU 4_000_000 ; clock frequency
| |
− | BaudRate EQU 9600 ; serial port baudrate
| |
− | SamplesPerBit EQU 3 ; samples per serial bit
| |
− | HalfStepsPerSecond EQU 80 ; stepper motor half steps/sec
| |
− |
| |
− | ; some derived constants (derived from the ones above)
| |
− | ; clock ticks per interrupt
| |
− | InterruptPeriod EQU Frequency/(SamplesPerBit * BaudRate) + 1
| |
− | ; value to put in W to obtain the clock ticks per interrupt
| |
− | ; formulated in this particular way to get rid of 'Literal
| |
− | ; truncated to 8 bits' warning
| |
− | RetiwValue EQU 256-InterruptPeriod
| |
− | ; Interrupts per stepper motor step
| |
− | StepperDelay EQU Frequency/(HalfStepsPerSecond*InterruptPeriod)
| |
− |
| |
− | ; port definitions
| |
− | OutputXY EQU rc ; port C outputs the signals for X(0-3) and Y(4-7)
| |
− | OutputZ EQU rb ; port B outputs the signals for Z on bits(0-3)
| |
− | KeyboardColumns EQU ra ; port A serves keyboard columns (in a.0-a.2)
| |
− | KeyboardRows EQU rb ; port B serves keyboard rows (in b.1-b.3)
| |
− | SerialOut EQU ra.3 ; pins ra.3 and rb.0 are serial output and
| |
− | SerialIn EQU rb.0 ; input respectively.
| |
− |
| |
− | ;------------------------------ VARIABLES ------------------------------
| |
− | ORG $08
| |
− | ; 'global' bank, registers that can be accessed regardles of the current
| |
− | ; bank.
| |
− | ErrorTreshold DS 2 ; Error treshold (for Bresenhams)
| |
− | Step1 DS 2 ; Step size in the second dimension
| |
− | Step2 DS 2 ; Step size in the third dimension
| |
− |
| |
− | ; temporary register for interrupt routines
| |
− | InterruptScratch DS 1
| |
− | ; this byte contains the sign bits for x (.0),y (.1) and z (.2)
| |
− | Signs DS 1
| |
− |
| |
− | ORG $10
| |
− | ; bank with registers for the line drawing algorithm
| |
− | LineDrawingBank EQU $
| |
− |
| |
− | StepsToGo DS 2 ; steps left on current line
| |
− | Error2 DS 2 ; cumulative errr in the first dimension
| |
− | Error1 DS 2 ; cumulative error in the second dimension
| |
− | StepperDelayCount DS 2 ; timeout between steps
| |
− | PosX DS 1 ; X, Y and Z pos. 8 bits precision is more
| |
− | PosY DS 1 ; than enough.
| |
− | PosZ DS 1
| |
− |
| |
− | Direction0 DS 1 ; mapping of dimensions (0, 1, 2) to
| |
− | Direction1 DS 1 ; axes (x, y, z) and direction (up or down).
| |
− | Direction2 DS 1
| |
− |
| |
− | StepperState DS 1 ; current stepper state. see below.
| |
− |
| |
− | ; interruptable states, these are pre-empted by a move command
| |
− | StepperIdle EQU 0 ; Do Nothing
| |
− | StepperIdleKeys EQU 1 ; Handle keyboard
| |
− | StepperCountKeys EQU 2 ; Timeout and handle keys
| |
− |
| |
− | ; non-interruptable states. No new vector can be programmed
| |
− | ; while the driver is in one of these states
| |
− | StepperUpdatePos1 EQU 4 ; Update position 1
| |
− | StepperUpdatePos2 EQU 5; Update position 2
| |
− | StepperOutputPos EQU 6 ; latch x,y,z position to outputs
| |
− | StepperCounting EQU 7 ; wait a while before moving to next position
| |
− | ; update position 0 if wait ends
| |
− |
| |
− |
| |
− | ORG $30
| |
− | ; this bank contains the results of the commands
| |
− | ; more specifically, the x,y and z vector values
| |
− | ; are kept here...
| |
− | CommandBank EQU $
| |
− |
| |
− | Coordinates EQU $
| |
− | ;coordinates must start at an even address see the parse function
| |
− | GoX DS 2
| |
− | GoY DS 2
| |
− | GoZ DS 2
| |
− |
| |
− | ; command parser variables
| |
− | CurrentNumber DS 2 ; accumulator for the number we're currently parsing
| |
− | ParserState DS 1 ; current state
| |
− | NextChar DS 1 ; next character to parse
| |
− | CurrentCoordinate DS 1; which coordinate we're parsing.
| |
− | CurrentCommand DS 1;
| |
− |
| |
− | TextIndex DS 1;
| |
− | ; parser states
| |
− | ParserExpectCommand EQU 0
| |
− | ParserExpectNumber EQU 1
| |
− | ParserInHexNumber EQU 2
| |
− | ParserExpectReturn EQU 3
| |
− |
| |
− |
| |
− |
| |
− |
| |
− | ;---------------------------- DEBUG SETTINGS ---------------------------
| |
− |
| |
− | FREQ Frequency
| |
− |
| |
− | ; WATCH <Symbol>,<bit count>,<format>
| |
− |
| |
− | WKED_W equ $0A ;Write MIWU/RB Interrupt edge setup, 0 = falling, 1 = rising
| |
− | WKEN_W equ $0B ;Write MIWU/RB Interrupt edge setup, 0 = enabled, 1 = disabled
| |
− | ST_W equ $0C ;Write Port Schmitt Trigger setup, 0 = enabled, 1 = disabled
| |
− | LVL_W equ $0D ;Write Port Schmitt Trigger setup, 0 = enabled, 1 = disabled
| |
− | PLP_W equ $0E ;Write Port Schmitt Trigger setup, 0 = enabled, 1 = disabled
| |
− | DDIR_W equ $0F ;Write Port Direction
| |
− |
| |
− | RA_latch equ %00000000 ;SX18/20/28/48/52 port A latch init
| |
− | RA_DDIR equ %11110111 ;see under pin definitions for port A DDIR value
| |
− | RA_LVL equ %00000000 ;SX18/20/28/48/52 port A LVL value
| |
− | RA_PLP equ %11111111 ;SX18/20/28/48/52 port A PLP value
| |
− |
| |
− | RB_latch equ %00000000 ;SX18/20/28/48/52 port B latch init
| |
− | RB_DDIR equ %00000001 ;SX18/20/28/48/52 port B DDIR value
| |
− | RB_ST equ %11111111 ;SX18/20/28/48/52 port B ST value
| |
− | RB_LVL equ %00000000 ;SX18/20/28/48/52 port B LVL value
| |
− | RB_PLP equ %11111111 ;SX18/20/28/48/52 port B PLP value
| |
− |
| |
− | RC_latch equ %00000000 ;SX18/20/28/48/52 port C latch init
| |
− | RC_DDIR equ %00000000 ;SX18/20/28/48/52 port C DDIR value
| |
− | RC_ST equ %11111111 ;SX18/20/28/48/52 port C ST value
| |
− | RC_LVL equ %00000000 ;SX18/20/28/48/52 port C LVL value
| |
− | RC_PLP equ %11111111 ;SX18/20/28/48/52 port C PLP value
| |
− |
| |
− | ;-------------------------- MACRO DEFINITIONS --------------------------
| |
− | ; The following are mainly some macros that define 16 bit, little endian
| |
− | ; versions of the 8-bit instruction set that we all know and have grown to
| |
− | ; love.
| |
− |
| |
− | ; 16-bit little endian decrement
| |
− | Dec16 MACRO address
| |
− | sub address, #1 ; remember dec doesn't change C flag
| |
− | sc
| |
− | dec address + 1
| |
− | ENDM
| |
− |
| |
− | ; 16-bit little endian addition
| |
− | ; This addition sets carry flag accordingly (but not the zero flag)
| |
− | Add16 MACRO dest, src ; little endian 16 bit addition
| |
− | mov W, src
| |
− | add dest, W
| |
− | mov W, src + 1
| |
− | snb C
| |
− | movsz W, ++src + 1
| |
− | add dest + 1, W
| |
− | ENDM
| |
− |
| |
− | ; 16-bit little endian subtraction
| |
− | Sub16 MACRO dest,src
| |
− | sub dest, src ; sub lsb
| |
− | subb dest + 1, /c ; sub one extra if carry not set (= borrow)
| |
− | sub dest + 1, src + 1; subtract w from msb
| |
− | ENDM
| |
− |
| |
− |
| |
− | ; 16-bit, little endian compare, sets zero and carry flag
| |
− | Cmp16 MACRO dest, src
| |
− | LOCAL End
| |
− | mov w, src + 1
| |
− | mov w, dest + 1 - w
| |
− |
| |
− | jnz End ; if msb equal...
| |
− | mov w, src ;...compare lsb
| |
− | mov w, dest - w ;
| |
− | End
| |
− | ENDM
| |
− |
| |
− | ; It's 16 bit
| |
− | ; It's Little endian (or it doesn't matter)
| |
− | ; And, it copies.
| |
− | Copy16 MACRO dest, src
| |
− | mov dest, src
| |
− | mov dest + 1, src + 1
| |
− | ENDM
| |
− |
| |
− | ; test a 16 bit value for zero, start with the msb, to safely test
| |
− | ; when another 'thread' (interrupt) is decreasing the value.
| |
− | Test16 MACRO dest
| |
− | test dest + 1
| |
− | snz
| |
− | test dest
| |
− | ENDM
| |
− |
| |
− | Clr16 MACRO dest
| |
− | clr dest
| |
− | clr dest + 1
| |
− | ENDM
| |
− |
| |
− | ; move a 16 bit literal into RAM
| |
− | MovLit16 MACRO dest, literal16
| |
− | mov dest, #(literal16 // 256)
| |
− | mov dest + 1, #((literal16 / 256) // 256)
| |
− | ENDM
| |
− |
| |
− | ; rather complex macro to generate code that
| |
− | ; produces a line. It puts the absolute components
| |
− | ; in the GoX, GoY and GoZ registers and then composes
| |
− | ; the last three bits of the 'signs' register.
| |
− | ; the expression '||x' takes the absolute value of x.
| |
− | DoLine MACRO x,y,z
| |
− | bank CommandBank
| |
− | MovLit16 GoX, ||x
| |
− | MovLit16 GoY, ||y
| |
− | MovLit16 GoZ, ||z
| |
− |
| |
− | ; the following uses the fact that the expression:
| |
− | ; ||(a+1) - ||a + 1
| |
− | ; is zero for a < 0
| |
− | ; is two otherwise. (for integer a)
| |
− | mov signs, # ( (||(x+1) - ||x + 1) / 2 + (||(y+1) - ||y + 1) + (||(z+1) - ||z + 1) * 2)
| |
− | call @Move
| |
− | ENDM
| |
− |
| |
− | ; convert from ascii-coded hex digit to value
| |
− | ; jump if not a true hex digit
| |
− | ; note: this code changes the contents of the fr register
| |
− | ; note: this could probably be optimized
| |
− | ConvertHexOrJump MACRO fr,address
| |
− | LOCAL is_digit
| |
− | cjb fr, #'0', address
| |
− | cja fr, #'z', address
| |
− | cjbe fr, #'9', is_digit
| |
− | or fr, #$20
| |
− | cjb fr, #'a', address
| |
− | cja fr, #'f', address
| |
− | sub fr, #('a' - 10 - '0')
| |
− | is_digit sub fr, #'0'
| |
− | ENDM
| |
− |
| |
− | ;-------------------------- INTERRUPT ROUTINE --------------------------
| |
− | ORG 0
| |
− |
| |
− | Interrupt
| |
− |
| |
− | ; Stepper motor controller.
| |
− | ; This VP controls up to 3 stepper motors. It implements Bresenhams algorithm
| |
− | ; to move in a straight line in 3D coordinate space.
| |
− | Stepper
| |
− | bank LineDrawingBank
| |
− | mov w, StepperState
| |
− | jmp pc + w
| |
− | jmp :ZeroStepperOutputs ; idle, do noting
| |
− | jmp :EndStepper ; idle, handle keyboard
| |
− | jmp :EndStepper ; timeout while handling keyboard
| |
− | jmp :EndStepper ; state 3 not used
| |
− |
| |
− | ; non-interruptable states
| |
− |
| |
− | jmp :DoUpdatePos1 ; update pos1
| |
− | jmp :DoUpdatePos2 ; update pos2
| |
− | jmp :OutputStepper; latch x,y,z-positions to outputs
| |
− |
| |
− | ; state #7 no jump, just sstart executing here
| |
− | ; wait a while before moving to next position
| |
− | Dec16 StepperDelayCount
| |
− | Test16 StepperDelayCount
| |
− | jnz :EndStepper
| |
− |
| |
− | ; transition to update position state
| |
− | mov StepperState, #StepperUpdatePos1
| |
− | MovLit16 StepperDelayCount, StepperDelay
| |
− |
| |
− | ; make a step in the first direction
| |
− | Dec16 StepsToGo
| |
− | mov w, Direction0
| |
− | call MakeStep
| |
− | jmp :EndStepper
| |
− |
| |
− | ; set all stepper outputs to zero
| |
− | ; this should decrease current when steppers are
| |
− | ; idle.
| |
− | :ZeroStepperOutputs
| |
− | clr OutputXY
| |
− | mov w, OutputZ
| |
− | and w, #%00001111
| |
− | mov OutputZ, w
| |
− | jmp :EndStepper
| |
− |
| |
− |
| |
− | ; update position 1 state
| |
− | :DoUpdatePos1
| |
− | Add16 Error1, Step1
| |
− | jc :Change1
| |
− | Cmp16 Error1, ErrorTreshold
| |
− | jnc :EndUpdatePos1
| |
− |
| |
− | :Change1
| |
− | Sub16 Error1, ErrorTreshold
| |
− | mov w, Direction1
| |
− | call MakeStep
| |
− | :EndUpdatePos1
| |
− | mov StepperState, #StepperUpdatePos2
| |
− | jmp :EndStepper
| |
− |
| |
− |
| |
− | ; update position 2 state
| |
− | :DoUpdatePos2
| |
− | Add16 Error2, Step2
| |
− | jc :Change2
| |
− | Cmp16 Error2, ErrorTreshold
| |
− | jnc :TransitionToOutputPos
| |
− |
| |
− | :Change2
| |
− | Sub16 Error2, ErrorTreshold
| |
− | mov w, Direction2
| |
− | call MakeStep
| |
− |
| |
− | :TransitionToOutputPos
| |
− | ; transition to outputpos state
| |
− | mov StepperState, #StepperOutputPos
| |
− | jmp :EndStepper
| |
− |
| |
− |
| |
− | ; output positions state
| |
− | :OutputStepper ; now translate x,y and z to stepper
| |
− | ; line outputs
| |
− |
| |
− | ; first move x and y to combined
| |
− | ; XY output (4 bits each)
| |
− | mov w, PosX
| |
− | call ExpandPos
| |
− | mov InterruptScratch, w
| |
− | mov w, PosY
| |
− | call ExpandPos2
| |
− | or w, InterruptScratch
| |
− | mov OutputXY, w
| |
− | mov w, PosZ
| |
− | call ExpandPos2 ; translate z pos to high bits
| |
− | mov InterruptScratch, w
| |
− | mov w, OutputZ
| |
− | and w, #%00001111
| |
− | or w, InterruptScratch
| |
− | mov OutputZ, w
| |
− | mov StepperState, #StepperCounting
| |
− |
| |
− | Test16 StepsToGo
| |
− | jnz :EndStepper ; if steps to go is zero, transition to idle
| |
− | mov StepperState, #StepperIdle
| |
− |
| |
− | :EndStepper
| |
− |
| |
− | EndInterrupt mov w, #RetiwValue
| |
− | retiw
| |
− |
| |
− |
| |
− | MakeStep ; increase or decrease an axis dimension, depending on the value of w
| |
− | ; w.7 determines whether we increase or decrease (1 = increase)
| |
− | ; w.1 and w.0 determine which axis (0 = x, 1 = y, 2 = z, 3 = crash horibly)
| |
− | mov InterruptScratch, w
| |
− | jb InterruptScratch.7, :Increase
| |
− | and w, #3
| |
− | jmp PC + w
| |
− | jmp :DecreaseX
| |
− | jmp :DecreaseY
| |
− | jmp :DecreaseZ
| |
− | :Increase and w, #3
| |
− | jmp PC + w
| |
− | jmp :IncreaseX
| |
− | jmp :IncreaseY
| |
− | jmp :IncreaseZ
| |
− |
| |
− | :IncreaseZ inc PosZ
| |
− | retp
| |
− | :IncreaseY inc PosY
| |
− | retp
| |
− | :IncreaseX inc PosX
| |
− | retp
| |
− | :DecreaseZ dec PosZ
| |
− | retp
| |
− | :DecreaseY dec PosY
| |
− | retp
| |
− | :DecreaseX dec PosX
| |
− | retp
| |
− |
| |
− | ExpandPos ; convert from number (0-3) to
| |
− | ; bit pattern (0001, 0010, 0100, 1000)
| |
− | and w, #7
| |
− | jmp PC+w
| |
− | ; retw %0001, %0010, %0100, %1000
| |
− | retw %0001, %0011, %0010, %0110, %0100, %1100, %1000, %1001
| |
− |
| |
− | ExpandPos2 ; easiest way to create a bit pattern in upper
| |
− | ; bits is to have another version of Expand
| |
− | ; instead of swapping
| |
− | and w, #7
| |
− | jmp PC+w
| |
− | ; retw %00010000, %00100000, %01000000, %10000000
| |
− | retw %00010000, %00110000, %00100000, %01100000, %01000000, %11000000, %10000000, %10010000
| |
− |
| |
− |
| |
− | ;------------------------ INITIALIZATION ROUTINE -----------------------
| |
− |
| |
− | Initialize
| |
− | ;<group comment>
| |
− | mov m, #ST_W ;point MODE to write ST register
| |
− | mov w,#RB_ST ;Setup RB Schmitt Trigger, 0 = enabled, 1 = disabled
| |
− | mov !rb,w
| |
− | mov w,#RC_ST ;Setup RC Schmitt Trigger, 0 = enabled, 1 = disabled
| |
− | mov !rc,w
| |
− |
| |
− | mov m, #LVL_W ;point MODE to write LVL register
| |
− | mov w,#RA_LVL ;Setup RA CMOS or TTL levels, 0 = TTL, 1 = CMOS
| |
− | mov !ra,w
| |
− | mov w,#RB_LVL ;Setup RB CMOS or TTL levels, 0 = TTL, 1 = CMOS
| |
− | mov !rb,w
| |
− | mov w,#RC_LVL ;Setup RC CMOS or TTL levels, 0 = TTL, 1 = CMOS
| |
− | mov !rc,w
| |
− |
| |
− | mov w,#RA_PLP ;Setup RA Weak Pull-up, 0 = enabled, 1 = disabled
| |
− | mov !ra,w
| |
− | mov w,#RB_PLP ;Setup RB Weak Pull-up, 0 = enabled, 1 = disabled
| |
− | mov !rb,w
| |
− | mov w,#RC_PLP ;Setup RC Weak Pull-up, 0 = enabled, 1 = disabled
| |
− | mov !rc,w
| |
− |
| |
− | mov m, #DDIR_W ;point MODE to write DDIR register
| |
− | mov w,#RA_DDIR ;Setup RA Direction register, 0 = output, 1 = input
| |
− | mov !ra,w
| |
− | mov w,#RB_DDIR ;Setup RB Direction register, 0 = output, 1 = input
| |
− | mov !rb,w
| |
− | mov w,#RC_DDIR ;Setup RC Direction register, 0 = output, 1 = input
| |
− | mov !rc,w
| |
− |
| |
− | mov w,#RA_latch ;Initialize RA data latch
| |
− | mov ra,w
| |
− | mov w,#RB_latch ;Initialize RB data latch
| |
− | mov rb,w
| |
− | mov w,#RC_latch ;Initialize RC data latch
| |
− | mov rc,w
| |
− |
| |
− | ; zero all ram (SX28)
| |
− | clr fsr ;reset all ram banks
| |
− | :zero_ram sb fsr.4 ;are we on low half of bank?
| |
− | setb fsr.3 ;If so, don't touch regs 0-7
| |
− | clr ind ;clear using indirect addressing
| |
− | incsz fsr ;repeat until done
| |
− | jmp :zero_ram
| |
− |
| |
− | mov !option,#%10011111 ;enable rtcc interrupt
| |
− |
| |
− | ;---------------------------- MAIN PROGRAM ----------------------------- | |
− |
| |
− | Main
| |
− | bank CommandBank
| |
− |
| |
− | mov TextIndex, #TestCommands
| |
− | :nextchar
| |
− | mov m, #TestCommands >> 8
| |
− | mov w, TextIndex
| |
− | iread
| |
− | or w, #0
| |
− | jz MainLoop
| |
− | mov NextChar, w
| |
− | call @ParseCharacter
| |
− | bank CommandBank
| |
− | inc TextIndex
| |
− | jmp :nextchar
| |
− |
| |
− | MainLoop jmp MainLoop
| |
− |
| |
− |
| |
− |
| |
− | TestCommands
| |
− | dw 'm200,200,200', 13
| |
− | dw 'M0,-200,0', 13
| |
− | dw 'm-200,200,2000', 13
| |
− | dw 'm0,-200,0', 13
| |
− | dw 0
| |
− | ORG $200
| |
− | Move ; program the vector in GoX, GoY and GoZ
| |
− | ; into the Bresenham registers and start the
| |
− | ; algorithm.
| |
− | ; If the Bresenham routine is busy, wait until it's
| |
− | ; finished
| |
− |
| |
− | bank LineDrawingBank
| |
− | ; wait until the stepper is interruptible
| |
− | :Wait jb StepperState.2, :Wait
| |
− |
| |
− | ; now start to determine which value is the largest
| |
− | ; Bresenhams algoritm is driven by the largest component
| |
− | ; on every 'tick' a step is made in that direction and other
| |
− | ; directions may or may not make a step in that same tick.
| |
− | bank CommandBank
| |
− | Cmp16 GoX, GoY
| |
− | jnc :YOrZIsLargest ; jump if (Y > X)
| |
− | :XOrZIsLargest
| |
− | Cmp16 GoX, GoZ
| |
− | jnc :ZIsLargest ; jump if (Z > X)
| |
− |
| |
− | :XIsLargest ; x is largest, order becomes x, y, z
| |
− | Copy16 ErrorTreshold, GoX
| |
− | Copy16 Step1, GoY
| |
− | Copy16 Step2, GoZ
| |
− |
| |
− | bank LineDrawingBank
| |
− |
| |
− | ; first set the direction registers
| |
− | ; the direction registers map Bresenhams registers
| |
− | ; (0, 1, and 2) to the three axis (x, y and z)
| |
− | ;
| |
− | ; these are coded 0 for x, 1 for y and 2 for z
| |
− | ; we set them to a value 'shifted left' (times 2)
| |
− | ; because we're going to shift them to the right
| |
− | ; lateron to add the direction bits.
| |
− | mov Direction0, #0 ; 2 times 0, for x
| |
− | mov Direction1, #2 ; 2 times 1, for y
| |
− | mov Direction2, #4 ; 2 times 2, for z
| |
− |
| |
− | ; now distribute the direction (up or down) bits
| |
− | rr Signs
| |
− | rr Direction0 ; x to Direction0
| |
− | rr Signs
| |
− | rr Direction1 ; y to Direction1
| |
− | rr Signs
| |
− | rr direction2 ; z to Direction2
| |
− |
| |
− | jmp :Finalize
| |
− |
| |
− | :YOrZIsLargest
| |
− | Cmp16 GoY, GoZ
| |
− | jnc :ZIsLargest
| |
− |
| |
− | :YIsLargest ; y is largest, order becomes y, x, z
| |
− | Copy16 ErrorTreshold, GoY
| |
− | Copy16 Step1, GoX
| |
− | Copy16 Step2, GoZ
| |
− | bank LineDrawingBank
| |
− |
| |
− | ; first set the direction registers
| |
− | ; the direction registers map Bresenhams registers
| |
− | ; (0, 1, and 2) to the three axis (x, y and z)
| |
− | ;
| |
− | ; these are coded 0 for x, 1 for y and 2 for z
| |
− | ; we set them to a value 'shifted left' (times 2)
| |
− | ; because we're going to shift them to the right
| |
− | ; lateron to add the direction bits.
| |
− | mov Direction0, #2 ; 2 times 1, for y
| |
− | mov Direction1, #0 ; 2 times 0, for x
| |
− | mov Direction2, #4 ; 2 times 2, for z
| |
− |
| |
− | ; now distribute the direction (up or down) bits
| |
− | rr Signs
| |
− | rr Direction1 ; x to Direction1
| |
− | rr Signs
| |
− | rr Direction0 ; y to Direction0
| |
− | rr Signs
| |
− | rr direction2 ; z to Direction2
| |
− |
| |
− | jmp :Finalize
| |
− | :ZIsLargest ; z is largest, order becomes z, x, y
| |
− | Copy16 ErrorTreshold, GoZ
| |
− | Copy16 Step1, GoX
| |
− | Copy16 Step2, GoY
| |
− | bank LineDrawingBank
| |
− |
| |
− | ; first set the direction registers
| |
− | ; the direction registers map Bresenhams registers
| |
− | ; (0, 1, and 2) to the three axis (x, y and z)
| |
− | ;
| |
− | ; these are coded 0 for x, 1 for y and 2 for z
| |
− | ; we set them to a value 'shifted left' (times 2)
| |
− | ; because we're going to shift them to the right
| |
− | ; lateron to add the direction bits.
| |
− | mov Direction0, #4 ; 2 times 2, for z
| |
− | mov Direction1, #0 ; 2 times 0, for x
| |
− | mov Direction2, #2 ; 2 times 1, for y
| |
− |
| |
− | ; now distribute the direction (up or down) bits
| |
− | rr Signs
| |
− | rr Direction1 ; x to Direction1
| |
− | rr Signs
| |
− | rr Direction2 ; y to Direction2
| |
− | rr Signs
| |
− | rr direction0 ; z to Direction0
| |
− |
| |
− | BREAK :Finalize
| |
− | :Finalize
| |
− | ; Error1 = ErrorTreshold/2
| |
− | Copy16 Error1, ErrorTreshold
| |
− | rr Error1 + 1
| |
− | rr Error1
| |
− |
| |
− | ; Error2 = Error1
| |
− | Copy16 Error2, Error1
| |
− | | |
− | ; Set StepsToGo
| |
− | Copy16 StepsToGo, ErrorTreshold
| |
− | MovLit16 StepperDelayCount, StepperDelay
| |
− | | |
− | ; now set the state to 'output position'
| |
− | ; this will activate the stepper.
| |
− | mov StepperState, #StepperOutputPos
| |
− | retp
| |
− | | |
− | ; parse an input character
| |
− | ; This routine is used to parse input command strings
| |
− | ; an example input string would be
| |
− | ; 'm 1ff, 0, FFFF'
| |
− | ; which stands for 'Move 511, 0, 65535'
| |
− | ParseCharacter
| |
− | ; parser states
| |
− | ;ParserExpectCommand EQU 0
| |
− | ;ParserExpectCoordinate EQU 1
| |
− | ;ParserInCoordinate EQU 2
| |
− |
| |
− | bank CommandBank
| |
− | mov w, ParserState
| |
− | jmp PC + w
| |
− | jmp :DoExpectCommand
| |
− | jmp :DoExpectNumber
| |
− | jmp :DoInNumber
| |
− | :DoExpectReturn
| |
− | mov w, #13 ; carriage return
| |
− | mov w, NextChar - w
| |
− | sz ; do nothing unless we have a cr
| |
− | retp
| |
− | mov ParserState, #ParserExpectCommand
| |
− | jmp Move ; call the 'move' routine and return
| |
− | :DoExpectCommand
| |
− | mov w, #'M'
| |
− | mov w, NextChar - w
| |
− | and w, #~$20 ; tolower
| |
− | sz ; return if it is not 'm' (the only command we support so far
| |
− | retp
| |
− | Clr16 GoX
| |
− | Clr16 GoY
| |
− | Clr16 GoZ
| |
− | mov Signs, #$07; all positive
| |
− | mov CurrentCoordinate, #1 ; start parsing x
| |
− | mov ParserState, #ParserExpectNumber
| |
− | retp
| |
− | :DoExpectNumber | |
− | ConvertHexOrJump NextChar, :EndExpectNumber
| |
− | mov ParserState, #ParserInHexNumber
| |
− | mov CurrentNumber, NextChar
| |
− | clr CurrentNumber + 1
| |
− | :EndExpectNumber
| |
− | cse NextChar, #'-' ; return if not '-'...
| |
− | retp
| |
− | mov w, /CurrentCoordinate
| |
− | and Signs, w ; reset the corresponding sign bit
| |
− | retp
| |
− |
| |
− | :DoInNumber ConvertHexOrJump NextChar, :EndInNumber
| |
− | ; now CurrentCoordinate = CurrentCoordinate * 16 + nextdigit
| |
− | ; todo: optimize
| |
− | clc
| |
− | rl CurrentNumber
| |
− | rl CurrentNumber + 1
| |
− | rl CurrentNumber
| |
− | rl CurrentNumber + 1
| |
− | rl CurrentNumber
| |
− | rl CurrentNumber + 1
| |
− | rl CurrentNumber
| |
− | rl CurrentNumber + 1
| |
− | mov w, NextChar
| |
− | and w, #$0f
| |
− | or CurrentNumber, w
| |
− | retp
| |
− |
| |
− | :EndInNumber
| |
− | ; calculate the destination address
| |
− | mov w, #Coordinates
| |
− | add w, CurrentCoordinate
| |
− |
| |
− | and w, #$fe
| |
− |
| |
− | ; copy the parsed number to the address
| |
− | mov FSR, w
| |
− | mov IND, CurrentNumber
| |
− | inc FSR
| |
− | mov IND, CurrentNumber + 1
| |
− |
| |
− | ; if this was not the last coordinate,
| |
− | ; goto ExpectNumber state
| |
− | ; else goto ExpectReturn state
| |
− | mov w, #ParserExpectNumber
| |
− | snb CurrentCoordinate.2
| |
− | mov w, #ParserExpectReturn
| |
− | mov ParserState, w
| |
− |
| |
− | clc
| |
− | rl CurrentCoordinate
| |
− |
| |
− | ; immediately reparse this character
| |
− | jmp ParseCharacter
| |
− |
| |
− |
| |
− |
| |
− |
| |
− |
| |
− |
| |
− | ;----------------------------- SUBROUTINES -----------------------------
| |