english
version "1.0"
identify "xyz"

module stepper

#: This module provides an interface to some stepper motors.

import
    character
    delay
    format
    integer
    logical
    out_stream
    string
    system
    unsigned

define stepper				#: Stepper motor state
    record
	debug_stream	out_stream	#: Debugging {out_stream}
	delay		delay		#: Delay object
	microseconds	unsigned	#: Number of microseconds between steps
	print_stream	out_stream	#: Print stream
	w		integer		#: Current W position
	w_offset	integer		#: Bit position for W
	w_maximum	integer		#: Maximum value of W
	w_minimum	integer		#: Minimum value of W
	x		integer		#: Current X position
	x_offset	integer		#: Bit position for X
	x_maximum	integer		#: Maximum value of X
	x_minimum	integer		#: Minimum value of X
	y		integer		#: Current Y position
	y_offset	integer		#: Bit position for Y
	y_maximum	integer		#: Maximum value of Y
	y_minimum	integer		#: Minimum value of Y
	z		integer		#: Current Z position
	z_offset	integer		#: Bit position for Z
	z_maximum	integer		#: Maximum value of Z
	z_minimum	integer		#: Minimum value of Z
    generate allocate, erase, print


procedure create@stepper
    takes
	w_maximum integer
	x_maximum integer
	y_maximum integer
	z_maximum integer
	debug_stream out_stream
    returns stepper

    #: This procedure will create and return a new {stepper} object.
    #, The maximum allowed values for the  w, x, y, and z axis are
    #, {w_maximum}, {x_maximum}, {y_maximum}, and {z_maximum}, respectively.

    zero :@= integer_convert@(0)
    delay :@= create@delay()
    #printer :@= open@printer("/dev/lp1")
    #assert printer !== ??
    #print_stream :@= printer.out_stream
    print_stream:: out_stream := ??

    initialize stepper:: stepper := allocate@stepper()
	stepper.debug_stream := debug_stream
	stepper.delay := delay
	stepper.microseconds := 10
	stepper.print_stream := print_stream
	stepper.w := zero
	stepper.w_offset := integer_convert@(6)
	stepper.w_maximum := w_maximum
	stepper.w_minimum := zero
	stepper.x := zero
	stepper.x_offset := integer_convert@(4)
	stepper.x_maximum := x_maximum
	stepper.x_minimum := zero
	stepper.y := zero
	stepper.y_offset := integer_convert@(2)
	stepper.y_maximum := y_maximum
	stepper.y_minimum := zero
	stepper.z := zero
	stepper.z_offset := zero
	stepper.z_maximum := z_maximum
	stepper.z_minimum := zero
    return stepper


procedure goto@stepper
    takes
	stepper stepper
	w integer
	x integer
	y integer
	z integer
    returns_nothing

    #: This procedure will cause the stepper motors controlled by {stepper}
    #, to goto position ({w}, {x}, {y}, {z}) in a close approximiation to
    #, line in 4-space.


    debug_stream :@= stepper.debug_stream
    #format@format4[integer, integer, integer, integer](debug_stream,
    #  "goto(%d%, %d%, %d%, %d%)\n\", w, x, y, z)

    if w > stepper.w_maximum
	w := stepper.w_maximum
    else_if w < stepper.w_minimum
	w := stepper.w_minimum
    if x > stepper.x_maximum
	x := stepper.x_maximum
    else_if x < stepper.x_minimum
	x := stepper.x_minimum
    if y > stepper.y_maximum
	y := stepper.y_maximum
    else_if y < stepper.y_minimum
	y := stepper.y_minimum
    if z > stepper.z_maximum
	z := stepper.z_maximum
    else_if z < stepper.z_minimum
	z := stepper.z_minimum

    #format@format4[integer, integer, integer, integer](debug_stream,
    #  "limit(%d%, %d%, %d%, %d%)\n\", w, x, y, z)

    old_w :@= stepper.w
    old_x :@= stepper.x
    old_y :@= stepper.y
    old_z :@= stepper.z

    dw :@= w - old_w
    dx :@= x - old_x
    dy :@= y - old_y
    dz :@= z - old_z

    #format@format4[integer, integer, integer, integer](debug_stream,
    #  "delta(%d%, %d%, %d%, %d%)\n\", dw, dx, dy, dz)

    steps :@= maximum@(maximum@(absolute@(dw), absolute@(dx)),
      maximum@(absolute@(dy), absolute@(dz)))

    #format@format1[integer](debug_stream, "steps=%d%\n\", steps)

    one :@= integer_convert@(1)
    index :@= one
    loop
	while index <= steps
	stepper.w := old_w + (dw * index) / steps
	stepper.x := old_x + (dx * index) / steps
	stepper.y := old_y + (dy * index) / steps
	stepper.z := old_z + (dz * index) / steps
	step@(stepper)
	index :+= one

    format@format4[integer, integer, integer, integer](debug_stream,
      "(%d%, %d%, %d%, %d%)\n\", stepper.w, stepper.x, stepper.y, stepper.z)


procedure step@stepper
    takes
	stepper stepper
    returns_nothing

    #: This procedure will cause the stepper motors controlled by {stepper}
    #, to goto the new values in {stepper}.  It is assumed that the new values
    #, in {stepper} have changed by at most +1 or -1.  This is a helper
    #, routine for {goto}@{stepper}.

    mask :@= integer_convert@(3)
    character :@= character_convert@(unsigned_convert@(
      ((stepper.w & mask) << stepper.w_offset) |
      ((stepper.x & mask) << stepper.x_offset) |
      ((stepper.y & mask) << stepper.y_offset) |
      ((stepper.z & mask) << stepper.z_offset) ))
    print_stream :@= stepper.print_stream
    put@(character, print_stream)
    flush@(print_stream)
    sleep@(stepper.delay, stepper.microseconds)




