# ******************************************************************************
#
# Copyright (c) 2000-2001 by Wayne Gramlich and Bill Benson
# All rights reserved.
#
# Permission to use, copy, modify, distribute, and sell this software
# for any purpose is hereby granted without fee provided that the above
# copyright notice and this permission are retained.  The author makes
# no representations about the suitability of this software for any purpose.
# It is provided "as is" without express or implied warranty.
#
# This is the code that implements the Stepper1 RoboBrick.  Basically
# it just waits for commands that come in at 2400 baud and responds
# to them.  See
#
#   http://web.gramlich.net/projects/robobricks/stepper1/index.html
#
# for more details.
#
# ******************************************************************************

processor pic12c509 cp=off wdte=off mclre=off fosc=intrc

# Define processor constants:
constant clock_rate 4000000						
constant clocks_per_instruction 4
constant instruction_rate clock_rate / clocks_per_instruction
				
# Define serial communication control constants:
constant baud_rate 2400
constant instructions_per_bit instruction_rate / baud_rate
constant delays_per_bit 3
constant instructions_per_delay instructions_per_bit / delays_per_bit

# Stepper bit numbers:
constant stepper0_bit 0
constant stepper1_bit 1
constant stepper2_bit 2
# Note: on the 8-pin PIC's, GPIO3 is input only:
constant serial_in_bit 3
constant stepper3_bit 4
constant serial_out_bit 5

# Servo bit masks:
constant stepper0_mask (1 << stepper0_bit)
constant stepper1_mask (1 << stepper1_bit)
constant stepper2_mask (1 << stepper2_bit)
constant stepper3_mask (1 << stepper3_bit)
constant serial_in_mask (1 << serial_in_bit)
constant serial_out_mask (1 << serial_out_bit)

# Register definitions:

# Status register:
register status 3
bind c status@0
bind z status@2

# OSCCAL register:
register osccal 5

# The 509 has 4 bits of OSCCAL and the 509A has 6 bits.
constant osccal_lsb 0x10
#constant osccal_lsb 0x4

# Define port bit assignments
port porta a bits_and_byte read_write_static
pin stepper0 porta stepper0_bit write_only
pin stepper1 porta stepper1_bit write_only
pin stepper2 porta stepper2_bit write_only
pin stepper3 porta stepper3_bit write_only
pin serial_in porta serial_in_bit read_only
pin serial_out porta serial_out_bit write_only

string_constants {
    id = 1, 0, 15, 0, 0, 0, 0, 0, 0r'16', 9, 0s'Stepper1A', 15, 0s'Gramlich&Benson'
    waves = 0x10, 4, 2, 1, 0x14, 6, 3, 0x11, 0x10, 0x14, 4, 6, 2, 3, 1, 0x11
}

# Note that the 12-bit PIC's only have a 2-level deep stack.
# The code starts in the main procedure (located at the end of this code)
# The next level of procedure call is either get_byte or send_byte.
# Lastly, the lowest level of procedure called is delay.  It all fits,
# but just barely.

bank 0

# Define global values that are read and written by main procedure
# and read by delay procedure.  See comment in front of delay
# procedure that talks about global register accessibility.
# Note that servos=4, so we are defining 9 registers below.

global adjust_low byte
global adjust_high byte
global current_low byte
global current_high byte
global desired_low byte
global desired_high byte
global middle_high byte
global middle_low byte
global slow_rate byte
global fast_rate byte
global ramp_rate byte
global ramp_amount byte
global complement byte
global power_down bit
global wave_table byte
global wave_mask byte
global wave_offset byte
global interrupt_pending bit
global interrupt_enable bit

# We are really tight on data registers.  Push data registers
# into register bank 1 for every procedure execept delay to
# spread the register bank load.

bank 0

procedure get_byte {
    arguments_none
    returns byte

    # This procedure will wait for a byte to be received from
    # serial_in_bit.  It calls the delay procedure for all delays.

    variable count byte
    variable char byte

    # Why does the delay procedure wait for a third of bit?  Well, it
    # has to do with the loop immediately below.  If we catch the
    # start bit at the beginning of a 1/3 bit time, we will be
    # sampling data at approximately 1/3 of the way into each bit.
    # Conversely, if we catch the start near the end of a 1/3 bit
    # bit time, we will be sampling data at approximately 2/3 of the
    # way into each bit.  So, what this means is that our bit sample
    # times will be somewhere between 1/3 and 2/3 of bit (i.e. in
    # the middle of the bit.

    # It would be nice to tweak the code to shorter delay times
    # (1/4 bit, 1/5 bite, etc.) but then it gets too hard to get
    # the bookeeping done in the delay routine.  A 12-bit PIC
    # running at 4MHz (=1MIPS), only has 138 instructions available
    # for the delay routine when at 1/3 of bit.

    # Wait for a start bit:
    while (serial_in) {
	call delay()
    }

    # Skip over start bit:
    call delay()
    call delay()
    call delay()

    # Sample in the middle third of each data bit:
    char := 0
    count_down count 8 {
	call delay()
	char := char >> 1
	if (serial_in) {
	    char := char | 0x80
	}
        call delay()
        call delay()
    }

    # Skip over 2/3's of stop bit:
    call delay()
    call delay()
    return char
}

procedure send_byte {
    argument char byte
    returns_nothing

    variable count byte

    # This procedure will send byte to serial_out_bit.

    # send the start bit:
    serial_out := 0
    call delay()
    call delay()
    call delay()

    # send the data:
    count_down count 8 {
	serial_out := char@0
	char := char >> 1
	call delay()
	call delay()
	call delay()
    }
    
    # send stop bit
    serial_out := 1
    call delay()
    call delay()
    call delay()
}

# In order for the uniform delay stuff to work, the delay routine must have
# all of its local variables in register bank 0 *AND* all of the global
# variables it accesses in register bank 0.  That way, the uCL peep-hole
# optimizer never attempts to stuff in any instructions to patch up
# register bank accessibility.  Any tweaking by the peep-hole optimizer
# messes up the uniform delay instruction counting.

bank 0

procedure delay {
    arguments_none
    returns_nothing
    uniform_delay instructions_per_delay

    # This procedure will delay for a third of bit at 2400 baud.
    # It is responsible for keeping the servo pulses going on
    # a regular basis.

    variable counter byte

    porta := porta & serial_out_mask | (waves[current_low & wave_mask + wave_offset] ^ complement)
    
    counter := counter - 1
    if (z) {
	# Adjust the position:
	counter := ramp_rate
	current_high := current_high + adjust_high
	current_low := current_low + adjust_low
	if (c) {
	    current_high := current_high + 1
	}
    }
}

bank 1

procedure end_point_update {
    arguments_none
    returns_nothing

    # This procedure will figure out all of magic constants needed
    # when the end-points are adjusted.

    variable direction bit

    if (desired_high > current_high) {
	direction := 1
    } else_if (desired_high < current_high) {
	direction := 0
    } else_if (desired_low > current_low) {
	direction := 1
    } else_if (desired_low > current_low) {
	direction := 1
    } else {
	# They are equal; think about turning off the coils:
    }

    if (direction) {
	adjust_high := 0
	adjust_low := 1
	middle_high := desired_high - current_high
	middle_low := desired_low - current_low
    } else {
	adjust_high := 0xff
	adjust_low := adjust_high
	middle_high := current_high - desired_high
	middle_low := current_low - desired_low
    }
    if (!c) {
	middle_high := middle_high + 1
    }
    # Divide middle by 2:
    assemble {
	bcf c___byte c___bit
	rrf middle_high f
	rrf middle_low f
    }
}

procedure reset {
    arguments_none
    returns_nothing

    current_low := 0
    current_high := 0
    desired_low := 0
    desired_high := 0
    slow_rate := 255
    fast_rate := 255
    ramp_rate := 1
    ramp_amount := 1
    complement := 0
    wave_table := 0
    interrupt_pending := 0
    interrupt_enable := 0
}

# The main procedure is loaded with switch statements.  On the 12-bit
# PIC's, switch statements have to live in the first 256 bytes of
# each code bank.  For this reason, we shove main into code bank 1.
# If we, try to put main in code bank 0, it pushes the first bytes
# of several other routines out of the first 256 bytes, which is also
# a no-no of the 12-bit PIC's.

origin 0x200
bank 1

procedure main {
    arguments_none
    returns_nothing

    variable command byte
    variable temp byte
    variable glitch byte
    variable index byte

    call reset()
    glitch := 0
    index := 0

    # Loop around waiting for a command:
    loop_forever {
	# Wait for a command byte:
        command := get_byte()

	# Dispatch on the command:
	switch (command >> 6) {
	  case 0 {
	    # Command = 01xx xxxx:
	    if (command@5) {
		# Decrement Desired (Command = 001d dddd):
		desired_low := desired_low - 1
		if (desired_low = 0xff) {
		    desired_high := desired_high - 1
		}
	    } else {
		# Increment Desired (Command = 000i iiii):
		desired_low := desired_low + 1
		if (z) {
		    desired_high := desired_high + 1
		}
	    }
	    call end_point_update()
	  }
	  case 1 {
	    # Command = 01xx xxxx:
	    switch ((command >> 3) & 7) {
	      case 0 {
		# Command = 0100 0xxx:
		temp := get_byte()
		switch (command & 7) {
		  case 0 {
		    # Set Desired High (Command = 0100 0000):
		    desired_high := temp
		    call end_point_update()
		  }
		  case 1 {
		    # Set Desired Low (Command = 0100 0001):
		    desired_low := temp
		    call end_point_update()
		  }
		  case 2 {
		    # Set Current High (Command = 0100 0010):
		    current_high := temp
		    call end_point_update()
		  }
		  case 3 {
		    # Set Current Low (Command = 0100 0011):
		    current_low := temp
		    call end_point_update()
		  }
		  case 4 {
		    # Set Slow Rate (Command = 0100 0100):
		    slow_rate := temp
		  }
		  case 5 {
		    # Set Fast Rate (Command = 0100 0101):
		    fast_rate := temp
		  }
		  case 6 {
		    # Set Ramp Rate (Command = 0100 0110):
		    ramp_rate := temp
		  }
		  case 7 {
		    # Set Ramp Amount (Command = 0100 0111):
		    ramp_amount := temp
		  }
		}
	      }
	      case 1 {
		# Command = 0100 1xxx:
		switch (command & 7) {
		  case 0 {
		    # Read Desired High (Command = 0100 1000):
		    temp := desired_high
		  }
		  case 1 {
		    # Read Desired Low (Command = 0100 1001):
		    temp := desired_low
		  }
		  case 2 {
		    # Read Current High (Command = 0100 1010):
		    temp := current_high
		  }
		  case 3 {
		    # Read Current Low (Command = 0100 1011):
		    temp := current_low
		  }
		  case 4 {
		    # Read Slow Rate (Command = 0100 1100):
		    temp := slow_rate
		  }
		  case 5 {
		    # Read Fast Rate (Command = 0100 1101):
		    temp := fast_rate
		  }
		  case 6 {
		    # Read Ramp Rate (Command = 0100 1110):
		    temp := ramp_rate
		  }
		  case 7 {
		    # Read Ramp Amount (Command = 0100 1111):
		    temp := ramp_amount
		  }
		}
		call send_byte(temp)
	      }
	      case 2, 3 {
		# Set Complement Mask (Command = 0101 cccc):
		complement := (command << 1) & 0x10 | command & 7
	      }
	      case 4 {
		# Command = 0110 0xxx:
		switch (command & 7) {
		  case 0, 1, 2, 3 {
		    # Set Wave Table (Command = 0110 00ww):
		    wave_table := command & 3
		    switch (wave_table) {
		      case 0 {
			# Wave Drive:
			wave_mask := 3
			wave_offset := 0
		      }
		      case 1 {
			# Two Phase:
			wave_mask := 3
			wave_offset := 4
		      }
		      case 2, 3 {
			# Two Phase:
			wave_mask := 7
			wave_offset := 8
		      }
		    }
		  }
		  case 4, 5 {
		    # Set Power Down Bit (Command = 0110 010p):
		    power_down := command@0
		  }
		  case 6 {
		    # Read Power Down Bit (Command = 0110 0110):
		    temp := 0
		    if (power_down) {
			temp := temp + 1
		    }
		    call send_byte(temp)
		  }
		  case 7 {
		    # Read Wave Table (Command = 0110 0111):
		    call send_byte(wave_table)
		  }
		}
	      }
	      case 5 {
		# Command = 0110 1xxx:
		switch (command & 7) {
		  case 0 {
		    # Set Desired (Command = 0110 1000):
		    temp := get_byte()
		    desired_low := get_byte()
		    desired_high := temp
		    call end_point_update()
		  }
		  case 1 {
		    # Set Current (Command = 0110 1001):
		    temp := get_byte()
		    current_low := get_byte()
		    current_high := temp
		    call end_point_update()
		  }
		  case 2 {
		    # Read Desired (Command = 0110 1010):
		    call send_byte(desired_low)
		    call send_byte(desired_high)
		  }
		  case 3 {
		    # Read Current (Command = 0110 1011):
		    call send_byte(current_low)
		    call send_byte(current_high)
		  }
		  case 4 {
		    # Read Complement Mask (Command = 0110 1100):
		    call send_byte((complement >> 1) & 8 | complement & 7)
		  }
		  case 5 {
		    # Reset (Command = 0110 1101):
		    call reset()
		  }
		  case 6 {
		    # Stop (Command = 0110 1110):
		    # Code to quickly stop goes here:
		  }
		  case 7 {
		    # Clear (Command = 0110 1111):
		    current_high := 0
		    current_low := 0
		    desired_high := 0
		    desired_low := 0
		    call end_point_update()
		  }
		}
	      }
	      default 7 {
		# Do nothing:
	      }
	    }
	  }
	  case 3 {
	    # Command = 11xx xxxx:
	    switch ((command >> 3) & 7) {
	      case 5 {
		# Command = 1110 1xxx:
		switch (command & 7) {
		  case 0, 1, 2, 3, 4, 5, 6 {
		    # Command = 1110 10xx, 1110 110x, or 1110 1110:
		    # Do nothing:
		  }
		  case 7 {
		    # Read Interrupt Bits (Command = 1110 1111):
		    if (interrupt_enable) {
			temp := 2
		    } else {
			temp := 0
		    }
		    if (interrupt_pending) {
			temp := temp + 1
		    }
		    call send_byte(temp)
		  }
		}
	      }
	      case 6 {
		# Set Interrupt Bits (Command = 1111 0xxx):
		switch ((command >> 1) & 3) {
		  case 0, 1 {
		    # Set Interrupt Bits (Command = 1111 00ep):
		    interrupt_enable := command@1
		    interrupt_pending := command@0
		  }
		  case 2 {
		    # Set Interrupt Pending (Command = 1111 010p):
		    interrupt_pending := command@0
		  }
		  case 3 {
		    # Set Interrupt Enable (Command = 1111 010e):
		    interrupt_enable := command@0
		  }
		}
	      }
	      case 7 {
		# Shared commands (Command = 1111 1xxx):
		switch (command & 7) {
 		  case 0 {
		    # Clock Decrement (Command = 1111 1000):
		    osccal := osccal - osccal_lsb
		  }
		  case 1 {
		    # Clock Increment (Command = 1111 1001):
		    osccal := osccal + osccal_lsb
		  }
 	          case 2 {
		    # Clock Read (Command = 1111 1010):
		    call send_byte(osccal)
		  }
		  case 3 {
		    # Clock Pulse (Command = 1111 1011):
		    call send_byte(0)
		  }
 		  case 4 {
		    # ID Next (Command = 1111 1100):
		    call send_byte(id[index])
		    index := index + 1
		    if (index >= id.size) {
			index := 0
		    }
		  }
		  case 5 {
		    # ID Reset (Command = 1111 1101):
		    index := 0
		  }
 		  case 6 {
		    # Glitch Read (Command = 1111 1110):
		    call send_byte(glitch)
		    glitch := 0
		  }
		  case 7 {
		    # Glitch (Command = 1111 1111):
		    if (glitch != 0xff) {
		        glitch := glitch + 1
		    }
		  }
		}
	      }
	    }
	  }
	} 
    }
}












