Please consider a donation to the Higher Intellect project. See or the Donate to Higher Intellect page for more info.

How to code vectordemos

From Higher Intellect Vintage Wiki
Revision as of 15:45, 21 May 2020 by Netfreak (talk | contribs) (Created page with "<pre> VECTORS, VECTORS, VECTORS ... How to code vectordemos - an introduction by Asterix of Movement ... -------------------------------------------...")
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

                       VECTORS, VECTORS, VECTORS ...

How to code vectordemos - an introduction by Asterix of Movement ...

 This text is an addition to How to code written by Comrade J of SAE.
It was written by Carl-Henrik Sk}rstedt during his easter holidays.
Hi ho to all friends of movements....
Any comments on this text/additions to vectors.txt should be Emailed to
[email protected].se, or by mail: Rullstensg6-210,90655Ume},Swe
If you think I should be clearer on some points, or if you think I've
totally forgotten something, just report this for later issues..

     _              _
     /|            |\
/|\ /                \
 | /                  \


1. Preface

2. ** Introduction to vectors
      .What is a vector?
      .Three dimensions?
    2.1 Vector operations

3. ** Coding techniques
      .How can I present a 3d point on a 2d monitor?
      .A way to represent real numbers with integer numbers
      .How can I use sin and cos in machine language?

4. ** Vector rotations
      .Two dimensions
      .Three dimensions

5. ** Polygons
      .Polygon algorithm
      .Creating objects from polygons

6. ** Planes in three dimensions

7. ** Special techniques
      .Different sorting algorithms
      .Vector Balls


A  ** Example sources
      1 Calculate rotation constants (optimized rotation)
      2 An old line-drawing routine for filled vectors
      3 The quicksort in 68000 assembler
      4 The insersort in 68020 assembler

B  ** Some info...
      1 a little convincing about the polygon elimination formula
      2 how to make a fill-linedraw out of a normal blitter-line routine
      3 an alternate approach to rotations in 3-space by M. Vissers
      4 a little math hint for more accurate vector calculations

1. Preface

    The sources of this text has more or less indirectly been some books
  from my school. Some sources worth mentioning are:
  Elementary Linear Algebra (by Howard Anton, released by John Wiley)
  Calculus - A complete course (By Robert K. Adams)
  The DiscWorld series (by T. Pratchett)

  By reading this text, you should also be able to know what it
  is you're doing. Not just converting given formulas to 680x0 code,
  but also know what the background of it is. If you know
  the theory behind your routine, you also know how to optimize it!
  NO text will here mention GLENZ-vectors, since they are
  amazingly depressive.

  This text is meant for democoders on all computers that
  supports a good graphic interface, which is fast enough
  to do normal inconvex objects in one frame (not PC).
  sqr() means sqare root in this text.

  I'm curious about what support commodore has for this kind
  of programming in their latest OS, it could be a great Idea
  if rotations etc that used many multiplications was
  programmed in ROM. The methods described are used by
  most well-known demo-coders in "vector" demos.

  The rights of this part stays with the author.
  I've coded Blue House I+2, most of Rebels Megademo II,
  my own fantasic and wonderful cruncher (not released),
  Amed (also not released), some intros, and the rubiks snake
  in Rebels Aars-intro, and the real slideshow from ECES.
  Sorry for most of my demos not working on other machines than
  real A500's, but that's the only computer I've used for

  The meaning of this text is that it shall be a part of
  How To Code.txt and that the same rules works for this
  text as for that.
  The rights of this part stays with the author.
  Sourcecodes should work with most assemblers except for
  Insert sorting, which needs a 68020 assembler.

  Hi to all my friends who really supported me by comments like:
  "How can you write all that text?"
  "Who will read all that?"
  "Can I have it first, so I can get more bbs-access?"
  "Why are you writing that?"
  "I want to play Zool!" (-My youngest brother)
  "My dog is sick..."
  "You definitely have the right approach to this!"
  "" (-Commodore Sweden)
  (But in swedish of course!)

  The reason why Terry Pratchetts DiscWorld series is taken
  as a serious source is that he is a great visualizer of
  strange mathematical difficulties. If you ever have
  problems with inspiration, sit back, read and try to imagine
  how demos would look like in the DiscWorld...

  Now read this text and impress me with a great AGA-demo...

  (C) MOVEMENT 1993.

  "Death to the pis" /T. Domar

2. Introduction to vectors.

What is a vector?
  If you have seen demos, you have probably seen effects that is called,
  in a loose term, vectors. They may be balls, filled polygons, lines,
  objects and many other things.
  The thing that is in common of these demos are the vector
  calculations of the positions of the objects. It can be in one, two
  or three Dimensions (or more, but then you can't see the ones above 3)

  You can for example have a cube. Each corner on the cube
  represent a vector TO the center of rotation.
  All vectors go FROM something TO something, normally we use
  vectors that goes from a point (0,0) to a point (a,b).
  This vector has the quantity of (a,b).

  Definition of vector:

  or, in a laymans loose terms: a line.
  A line have a length that we can call r, and a direction we can
  call t.
  We can write this vector (r,t) = (length,angle).
  But there is also another way, which is more used
  when dealing with vector objects with given coordinates.

  The line from (0,0) to (x,y) has the length sqr(x*x+y*y)
  and that is the VALUE of the vector. The direction can be
  seen as the angle between the x-axis and the line described
  by the vector.

  If we study this in two dimensions, we can have an example vector
  as following:

     ^ y
     |     _.(a,b)
     |     /|
     |    /
     |   /
     |  /  V
     | /
     |/\ - t=angle between x-axis and vector V
    (0,0)          x

  We can call this vector V, and, as we can see, it goes from
  the point (0,0) and (a,b). We can denote this vector as V=(a,b).
  Now we have both a value of V (The length between (0,0) and (a,b))
  and a direction of it (the angle in the diagram)

  If we look at the diagram, we can see that the length of the vector
  can be computed with pythagoras theorem, like:

  and t is the angle (Can be calculated with t=tan(y/x))

Three Dimensions?
  Now, if we have seen what a vector is in two dimensions, what is
  a vector in three?

  In three dimensions, every point has three coordinates,
  and so must then the vector have.


  Now the length of the vector becomes:

  What happens to the angle now?

  Here we can have different definitions, but let's think a little.
  If we start with giving ONE angle, we can only reach points on one
  PLANE, but we want to get a direction in SPACE.

  If we try with TWO angles, we will get a better result.
  One angle can represent the angle between the z-axis and the
  vector, the other the rotation AROUND the z-axis.

  For more problems in this area (there's many) study calculus
  of several variables and specially polar transformations in
  triple integrals, or just surface integrals in vector fields.

2.1 Vector operations:

(if you have two, or one dimension you have two or one variable instead of
three. if you have more you have ofcourse as many variables as dimensions)

* The SUM of two vectors (U=V+W) are defined as:
  V=(vx,vy,vz), W=(wx,wy,wz)=>
  => U=(vx+wx,vy+wy,vz+wz)

* The negation of a vector U=-V is defined as
  V=(x,y,z) => U=(-x,-y,-z)

* The differance between two vectors U=V-W are defined as

* A vector between two points (FROM P1(x1,y1,z1) TO P2(x2,y2,z2))
  can be computed:
  (V goes FROM P1 TO P2)

* A vector can be multiplied by a constant like:

* A coordinate system can be "Translated" to a new point with the
  translation formula:
  Where (k,l,m) is the OLD point where the NEW coordinate system
  should have its point (0,0,0)
  This is a good operation if you want to ROTATE around A NEW POINT!

* A vector can be rotated (Check chapter 4)
  The vector is always rotated around the point (0,0,0) so you
  may have to TRANSLATE it.

* We can take scalar product and cross-product of vectors
  (see any book about introduction to linear algebra
   for these. everything is evaluated in this text, so you
   don't have to know what this is)

3. Coding techniques

Presenting a three dimensional point on a two dimensinal screen
  Assume that you have a point in space (3d) that you want to
  take a photo of. A photo is 2d, so this should give us
  some sort of an answer.

  Look at the picture below:

   /   Screen (="photo")
  .  |/
   \ | ^y
    \| |
 <---+-x <- Eye of observer
z    |

  Inspecting this gives us the following formula:

  Projected Y = Distance of screen * Old Y / ( Distance of point )
  (The distances is of course the Z coordinates from the Eyes position)

  And a similar way of thinking gives us the projection of X.

    New Y=k*y/(z+dist)

  (where k is a constant for this screen, dist is the
   distance from the ROTATION point to the EYE on the

A way of presenting real numbers with Integers

  Until now we have only seen a lot of formulas, but how can we
  use them in Assembler where we only can have bytes/words/longwords?
  (If you don't have a FPU, and only want people with FPU's to be
  able to see your demos)

  For 68000 coding (compatible with all better processors)
  it is comfortable to be able to do multiplictations etc.
  with words (680x0,{x>=2} can do it with longwords, but this won't
  work very good with lower x's).

  But we need the fract parts of the numbers too, so how do we do?
  We can try to use numbers that are multiplied by a constant p.
  Then we can do the following operation:

  [cos(a)*p] * 75    (for example from a list with cos(x) mult. with p)

  But as you can see this number grows for each time we do another
  multiplication, so what we have to do is to divide by p again:

  [cos(a)*p] * 75 / p

  If you are a knower of digital electronics, you say "Oh no,
  not a division that takes so long time". But if you choose
  p carefully (i.e. p = 2 or 4 or 8 ...) you can use
  shifting instead of clean division. Look at this example:

	mulu	10(a0),d0	;10(a0) is from a list of cos*256 values
        asr.l   #8,d0		;and we "divide" by 256!

  Now we have done a multiplication of a fixed point number!
   (A hint to get the error a little smaller:
    clear a Dx-register and use an addx after the asr,
    and you will get a round-off error instead:

 	 moveq	#0,d7
  	 mulu	10(a0),d0
 	 asr.l	#8,d0
	 addx.l	d7,d0
    This halves the error!)

   The same thinking comes in with divisions, but in the other way:

        ext.l	d0
        ext.l	d1
        asl.l	#8,d0		;"Multiply" by 256
 	divs	d1,d0		;and divide by z*256 ...

  Additions and subtractions are the same as normal integer
  operations:  (no shifting needed)

	add.w	10(a0),d0

	sub.w	16(a1),d1

  So,  With multiplications you MUL first, then LSR.
       With divisions you LSL first, then DIV.

  If you wish to have higher accuracy with the multiplications,
  the 68020 and higher processors offers a cheap way to do
  floating point operations (32-bit total) instead.
  You can also do integer multiplications 32*32->32,
  and use 16-bit coses and sins instead, which enables
  you to use 'swap' instead of 'lsr'.

How can I use Sin and Cos in my assembler code?
  The easiest and fastest way is to include a sinus-list
  in you program. Make a basic-program that
  counts from 0 to 2*pi, for example 1024 times.
  Save the values and include them into your code.

  If you have words and 1024 different sinus values
  then you can get sinus and cosinus this way:

        lea	sinuslist(pc),a0        ;sinuslist is calculated list
	and.w	#$7fe,d0                ;d0 is angle
	move.w	(a0,d0.w),d1		;d1=sin(d0)
        add.w	#$200,d0
	and.w	#$7fe,d0
	move.w	(a0,d0.w),d0		;d0=cos(original d0)

  Your program could look like this:  (AmigaBasic, HiSoft basic)
  (NEVER use AmigaBasic on other processors than 68000)

   open "ram:sinuslist.s" for output as 1
    for L=0 to vals
     if nu=8 then print #1,pretxt;:nu=0 else print #1,',';
     print #1,y$;
    next L
   close 1

  You can of course do a program that calculates the
  sins in assembler code, by using ieee-libs or
  coding your own floating point routines.
  the relevant algoritm is... (for sinus)

 indata: v=angle (given in radians)
         Laps=number of terms (less=faster and more error, integer)

   1> Mlop=1
   2> FOR terms=1 TO Laps
   2.1> Temp=Talj/Dfac
   2.2> Result=sign*(Result+Temp)
   2.3> Talj=Talj*Ang2
   2.4> Mlop=Mlop+1
   2.5> Dfac=Dfac*Mlop
   2.6> sign=-sign
   3> RETURN sin()=Result

  where the returned sin() is between -1 and 1...
  The algorithm uses MacLaurin polynoms, and are therefore
  recommended only for values that are not very far away from 0.

4. The rotation of vectors.

* In two dimensions

  Now we know what a vector is, and we want to rotate it.
  This is very simple, if we have a vector given
  with lenght and angle, we just add the rotation-angle to the
  angle and let the length be like it is:
  rotate V=(r,t) with a -> V'=(r,t+a)

  But normally we don't have this simple case, we have a
  vector given by two coordinates like:
  V=(x,y) where x and y are coordinates in the xy-plane.

  In THIS text we denote the rotation of a vector V=(r,t) with
  rot(V,a). With this I mean the rotation of the vector V with the
  angle a.

  The rotation of this vector could have been done
  by transforming V to a vector of a length and a direction,
  but since this involves taking squares, tangens, squareroots
  etc., we would like a faster method.
  Here is where trigonometry comes in.

  But let us first assume that we have a vector V=(x,0)
  what would be the rotation of this vector?


  Now, let us rotate with an angle of a:
/|\y' /|
 |   /
 | /
 |/\a x'

  What is the new vectors composants (x',y') ?

  Remember these "definitions":

   Hypotenuse/side close to angle

   Hypotenuse/side not close to angle
Length>/ |< Length * sin(a)
      /a |
  Length * cos(a)

  If we put in this in the original rotation formula (V'=rot(V,a)=V(r,t+a))
  we can see that we can convert r and t to x and y with:


  Let's get back to our problem of the rotated vector V=(x,0).
  Here is r=x (=sqrt(x*x+0*0)), t=0 (=arctan(0/x)
  if we put this in our formula we get:

  V=(r,t) if r=x, t=0

  If we rotate this vector with the angle a we get:


  And if we translate back to our coordinate denotion:

                           ^We insert x=r, t=0

  And that is the formula for rotation of a vector that has no y-composant.
  For a vector V=(0,y) we get:
  r=y, t=pi/2 (=90 degrees) since we now are in the y-axis,
  which is 90 degrees from the x-axis.

  V=(r,t) => V'=(r,t+a) => V'=(r*cos(t+a),r*sin(t+a)) =>

    Now, there are a few trigonometric formulas that says that cos(pi/2+a)=
    =sin(a) and sin(pi/2+a)=-cos(a)

  We get:
  V'=( y * sin(a) , y * ( -cos(a) ) )

  But if we look in the general case, we have a vector V that
  has both x and y composants. Now we can use the single-cases
  rotation formulas for calculating the general case with
  an addition:

  Vx'=rot((x,0),a) = (x*cos(a)         ,x*sin(a))
+ Vy'=rot((0,y),a) = (        +y*sin(a),        -y*cos(a))
  V' =rot((x,y),a) = (x*cos(a)+y*sin(a),x*sin(a)-y*cos(a))

  (Vx' means rotation of V=(x,0) and Vy' is rotation of V=(0,y))
  And we have the rotation of a vector given in coordinates!

.. .
 . rot( (x,y), a)=( x*cos(a)+y*sin(a) , x*sin(a)-y*cos(a) )
        x-composant ^^^^^^^^^^^^^^^^    ^^^^^^^^^^^^^^^^^ y-composant

* Three dimensions

  Now we are getting somewhere!

  In the 2 dimensions case, we rotated x and y coordinates,
  and we didn't see any z coordinates that changed.
  Therefore we call this a rotation around the Z axis.

  Now, the simpliest thing to do in three dimensions is to
  still do the same thing, just rotate around any axis
  to get the new coordinate. Leave out the variable that
  represents the coordinate of the current rotation-axis,
  and you can use the very same expression.

  If you want to rotate only one or two coordinates, you can use
  the normal method of rotation, because then you
  won't have to calculate a 3x3 transformation matrix.
  But if you have more points, I recommend the
  optimized version.

  But there are optimizations in this field, but let's first
  look at ONE way to do this:


   Assume we want to rotate V=(x,y,z) around the z-axis
   with the angle a, around y with b and around x with c.
   The first rotation we do is around the Z-axis:
   U=(x,y) (x,y from V-vector) =>
   => U'=rot(U,a)=rot((x,y),a)=(x',y')

   Then we want to rotate around the Y-axis:
   W=(x',z) (x' is from U' and z is from V) =>
   => W'=rot(W,b)=rot((x',z),b)=(x'',z')

   And finally around the X-axis:
   T=(y',z') (y' is from U' and z' is from W') =>
   => T'=rot(T,c)=rot((y',z'),c)=(y'',z'')

   The rotated vector V' is the coordinate vector
   (x'',y'',z'') !

   With this method we can extend out rot-command to:

   V''= rot(V,angle1,angle2,angle3) where V is the original vector!
   ( V''= rot((x,y,z),angle1,angle2,angle3) )

  I hope that didn't look too complicated.
  As I said, there are optimizations of this method.
  These optimizations can be skipping one rotation of the
  above ones, or some precalculations.

  ORDER is very important. You won't get the same answer
  if you rotate X,Y,Z with the same angles as before.

  For xyz vectors we can write the equations to
  form the rotations:

  let c1=cos(angle1), c2=cos(angle2), c3=cos(angle3),
  s1=sin(angle1), s2=sin(angle2), s3=sin(angle3)


   x' = x*c1+y*s1
   y' = x*s1-y*c1

   x''= x'*c2+z*s2	<- Rotated x-coordinate
   z' = x'*s2-z*c2

   y''= y'*c3+z'*s3     <- Rotated y-coordinate
   z''= y'*s3-z'*c3	<- Rotated z-coordinate

  which gives:

   x''= (x*c1+y*s1)*c2+z*s2= c2*c1 *x + c2*s1 *y + s2 *z
        ^^^^^^^^^^^=x'       ^^^^^ xx   ^^^^^ xy   ^^ xz

   y''= (x*s1-y*c1)*c3+((x*c1+y*s1)*s2-z*c2)*s3=
        c3*s1 *x - c3*c1 *y + s3*s2*c1 *x + s3*s2*s1 *y - s3*c2 *z=

        (s3*s2*c1+c3*s1) *x + (s3*s2*s1-c3*c1) *y + (-s3*c2) *z
        ^^^^^^^^^^^^^^^^ yx   ^^^^^^^^^^^^^^^^ yy   ^^^^^^^^ yz

   z''= (x*s1-y*c1)*s3-((x*c1+y*s1)*s2-z*c2)*c3=
        s3*s1 *x - s3*c1 *y - c3*s2*c1 *x - c3*s2*s1 *y + c3*c2 *z=

        (-c3*s2*c1+s3*s1) *x + (-c3*s2*s1-c3*c1) *y + (c3*c2) *z
        ^^^^^^^^^^^^^^^^^ zx   ^^^^^^^^^^^^^^^^^ zy   ^^^^^^^ zz

  Now, look at the pattern of the solutions,
  for x'' we have calculated something times the original (x,y,z),
  the same for y'' and z'', What is the connection?

  Say that you rotate many given vectors with three angles that are the
  same for all vectors, then you get this scheme of multiplications.
  When you rotated as above you had to use twelve multiplications
  to do one rotation, but now we precalculate these 'constants'
  and manage to get down to nine multiplications!
  (x,y,z is the original (x,y,z) coordinate.
   c1=cos(angle1), s1=sin(angle1), c2=cos(angle2) and so on...)

  If you want to rotate a lot of coordinates with the same angles you
  first calculate these values:

  Then, for each coordinate, you use the following multiplication
  to get the rotated coordinates:
                       x''=xx * x + xy * y + xz * z
                       y''=yx * x + yy * y + yz * z
                       z''=zx * x + zy * y + zz * z

  So, you only have to calculate the constants once for every new angle,
  and THEN you use nine multiplications for every point you wish to
  rotate to get the new set of points.

  Look in the end of this text for an example of how this can be
  implemented in 68000-assembler.

  If you wish to skip on angle, you can optimize further.
  if you want to remove angle3, set c3=1 and all s3=0
  and put into your constant-calculation and it will be
  optimized for you.

  What method you want to use depends of course on how much you want to
  code, but I prefer the optimized version since it's more
  to be proud of... If you only rotate a few points with the same
  angles, the first (non-optimized) version might be the choice.

  If you want to, you can check that the transformation matrix has
  a determinant equal to 1.

5. Polygons!

  The word "polygon" means many corners, which means that it
  has a lot of points (corners) with lines drawn to.
  If you have, for example, 5 points, you can draw
  lines from point 1 to point 2, from point 2 to point 3,
  from point 3 to point 4 and from point 4 to point 5.
  If you want a CLOSED polygon you also draw a line from
  point 5 to point 1.



Open polygon of points above:

      / |
     /  /
    /  /

Closed polygon of points above:

      / |
     /  /
    /  /

   "Filled vectors" is created by drawing polygons, and filling inside.
   Normally the following algorithm is used:

   First you define all "corners" on the polygon as vectors,
   which allows you to rotate it and draw it in new angles,
   and then you draw one line from point 1 to point 2, and so on.
   The last line is from point 5 to point 1.
   When you're finished you use a BLITTER-FILL operation to fill the

   You will need a special line drawing routine for drawing these lines
   so the BLITTER-FILL works, I have an example of a working line-drawing
   routine in the appendices (K-seka! Just for CJ!).
   Further theory about what demands there are on the line drawing
   routine will be discussed later (App. B 2).

   There are also other ways to get a filled area (mostly for computers
   without blitter, or for special purposes on those that have)
   Information about that will be in later issues.

Creating objects from polygons

  An object is in this text a three-dimensional thing created
  with polygons. We don't have to think about
  what's inside, we just surround a mathematically defined
  sub-room with polygons.

  But what happends to surfaces that is on the other side of the object?
  and if there are hidden "parts" of the object, what can we do about them?

  We begin with a cube, it is easy to imagine, and also the rotation of it.
  we can see that no part of the cube is over another part of
  the cube in the viewers eyes. (compare, for example, with a torus, where
  there are sometimes parts that hides other parts of the same object)
  Some areas are of course AIMING AWAY FROM THE VIEWER, but we can
  calculate in what direction the polygon is facing (to or from the viewer)

  Always define the polygons in objects in the same direction
  (clockwise or non-clockwise) in all of the object. imagine that
  you stand on the OUTSIDE MIDDLE of the plane, and pick all points
  in a clockwise order. Which one you start with has nothing to
  do with it, just the order of them.

  Pick three points from a plane (point1, point2 and point 3)
  If all three points are not equal to any of the other points,
  these points define a plane.
  You will then only need three points to define the direction
  of the plane. Examine the following calculation:

  (This is AFTER 3d->2d projection, so there's no z-coordinate.
   If you want to know what this does, look in appendix b)

  This needs three points, which is the minimum number of coordinates
  a polygon must be, to not be a line or a point (THINK).
  This involves two multiplications per plane, but that isn't
  very much compared to rotation and 3d->2d projection.

  But let us study what this equation gives:
  If c is negative, the normal vector of the plane which the three points
  span is heading INTO the viewer ( = The plane is fronting the
  viewer => plane should be drawed )...
  If c is POSITIVE, the normal vector of the plane is heading
  AWAY from the viewer ( = The plane cannot be seen by the viewer =>
  DON'T draw the plane) ...

  But to question 2, what happends if parts of the object
  covers OTHER parts of the object...

Convex and INCONVEX objects

  A convex object has NO parts that covers other parts of the
  same object, viewed from all angles.

  An inconvex object has parts that covers other parts of the
  same object, viewed from some angle.

  For convex objects, this means that you can draw a straigt
  line from every point inside the object to every other point
  in the object without having no line that passes outside
  the domain of the object.

  If you have a CONVEX object, you can draw ALL lines around
  the visible planes and then fill with the blitter, because
  no drawn polygon will ever cover another polygon.
  With some struggle you can also find ways to omit some lines,
  since they will be drawn twice.

  INCONVEX objects offers some further problems,
  the easiest way to use INCONVEX objects is
  to split them into smaller CONVEX objects. This works for
  all objects, even if you can have some problem doing it.

  Of course, you can skip a lot of planes that will be "inside"
  the Inconvex object.

  When you have splitted the object you simply draw
  each convex object into a TEMPORARY memory area,
  and treat it like a VECTORBALL (Sorting and stuff),
  Which should be discussed in later parts of this text.

  The Z coordinate can be taken from the middle of all z-values
  in the object (The sum of all Z's in the object divided by
  the number of coordinates)

  When you're sorting the objects, you can sometimes have problems
  with parts of the inconvex object getting in the wrong order
  since you've picked a point at random from the OUTSIDE of the
  convex object, which the current object is sharing with
  another convex object. One way to solve this problem
  is to take a middle point that is inside the convex object,
  by adding all the Z-values around the object and dividing
  by the number of coordinates that you have added.
  In this case, you should take points from at least two planes in
  the object.

Object optimization

  Assume that you have a CONVEX object.
  If it is closed, you have almost as few points as you have
  planes. If you have a list to every coordinate
  that exist (no points are the same in this list) that
  for each polygon shows what point you should fetch for
  this coordinate, you can cut widely on the number of
  For example:

 /* A cube */
 /* order is important! Here is clockwise */


      dc.l pt4,pt3,pt2,pt1,end_of_plane
      dc.l pt5,pt6,pt2,pt1,end_of_plane
      dc.l pt6,pt7,pt3,pt2,end_of_plane
      dc.l pt7,pt8,pt4,pt3,end_of_plane
      dc.l pt8,pt5,pt1,pt4,end_of_plane
      dc.l pt5,pt6,pt7,pt8,end_of_plane
  pt1 dc.w -1,-1,-1
  pt2 dc.w 1,-1,-1
  pt3 dc.w 1,-1,1
  pt4 dc.w -1,-1,1
  pt5 dc.w -1,1,-1
  pt6 dc.w 1,1,-1
  pt7 dc.w 1,1,1
  pt8 dc.w -1,1,1

  Now, you only have to rotate the points pt1-pt8, which is eight points.
  If you had computed four points for each plane, you would have to
  compute 24 rotations instead.

6. Planes in three dimensions


  Lightsourcing is a way to find out how much light a
  plane recieves from either a point of light (spherical)
  or a plane of light (planar). If the colour of the plane
  represents the light that falls on it, the object will
  be a bit more realistic.

  What we are interested in is the ANGLE of the VECTOR from the
  NORMAL of the plane to the LIGHTSOURCE (=point of light)
  (this is for a spherical lightsource, like a lamp or
  something. If you are interested in planar light, like
  from the sun, you are interested in the ANGLE between the
  NORMAL of the plane and the LIGHTSOURCE VECTOR)

  We are interested of the COSINE of the given angle.

  Anyway, to get the normal of the plane you can pick three
  points in the polygon, create two vectors of these.
*  we pick (x1,y1,z1) and (x2,y2,z2) and (x3,y3,z3)
   we create two vectors V1 and V2:

   To get the normal of these we take the cross product of them:

                |  i     j     k  |
    N = V1xV2 = |x2-x1 y2-y1 z2-z1| =
                |x3-x1 y3-y1 z3-z1|

               n1                                       n2
*   = ((y2-y1)*(z3-z1)-(y3-y1)*(z2-z1),-((x2-x1)*(z3-z1)-(x3-x1)*(z2-z1)),
*      ,(x2-x1)*(y3-y1)-(x3-x1)*(y2-y1))

   Now, we have N. We also have the LIGHTSOURCE coordinates (given)

   To get COS of the ANGLE between two vectors we can use the scalar
   product between N and L (=lightsource vector) divided
   by the length of N and L:

   <N,L>/(||N||*||L||) =

*  (n1*l1+n2*l2+n3*l3)/(sqr(n1*n1+n2*n2+n3*n3)*sqr(l1*l1+l2*l2+l3*l3))
*  (can be (n1*l1+n2*l2+n3*l3)/k if k is a precalculated constant)

   This number is between -1 and 1 and is cos of the angle between
   the vectors L and N. the SQUARE ROOTS take much time, but
   if you keep the object intact (do only rotations/translatins etc.)
   and always pick the same points in the object,
   then ||N|| is intact and can be precalculated.

   If you make sure the length of L is always 1, you won't have
   to devide by this, which saves many cycles.

   The number will, as said, be between -1 and 1. You may have
   to multiply the number with something before dividing
   so that you have a larger range to pick colours from.
   If the number is negative, set it to zero.

   The number can be NEGATIVE when it should be POSITIVE,
   this is because you took the points in the wrong order,
   but you only have to negate the result instead.

   If you didn't understand a thing of this, look on the formulas
   with a '*' in the border. n1 means the x-coordinate of N, n2
   the y-coordinate and so on, and the same thing with L.

7. Special techniques

Sorting Algorithms

  When you come to sorting, most books begin with "Bubble-sorting"
  Bubble sorting is enourmously slow, and is described here only
  for explaining terms. But I don't advise you to code routines
  that uses this method since it's SLOOOOOOOOOOOOW....
  A better way is to use Insert Sorting (which, in fact, is sorting,
  Acro!) or Quick Sorting or whatever you can find in
  old books (you have them, I'm sure!) about basic/pascal/c/
  or whatever (and even a few assembler books!!!) contains
  different methods of sorting. Just make sure you don't use

Method 1) Bubble sorting

  Assume that you have a list of VALUES and WEIGHTS.
  The heaviest weights must fall to the bottom, and bringing the
  VALUES with it. The values in this case can be the
  2d->3d projected x and y coordinates plus bob information.
  The Weights can be the Z coordinates before projection.

  Begin with the first two elements, check what element
  is the HEAVIEST, and if it is ABOVE the lighter element,
  move all information connected with the WEIGHT and the
  WEIGHT to the place where the light element was,
  and put the light data where the heavy was.
  (This operation is called a 'swap' operation)

  Step down ONE element and check element 2 and 3..
  step further until you're at the bottom of
  the list.

  The first round won't fix the sorting, you will have to
  OBJECTS minus one!!!!

  The many comparisions vote for a faster technique...


  1> FOR outer loop=1 TO Items-1
  1.1> FOR inner loop=1 TO Items-1
  1.1.1> IF Item(inner loop)>Item(inner loop+1)> Swap Item(inner loop),Item(inner loop+1)

  (Items is the number of entries to sort, Item() is the weight of
   the current item)

Method 2) Insert sorting

  Assume the same VALUES and WEIGHTS as above.
  To do this, you have to select a wordlength for the
  sorting-table (checklist) and a size of the checklist.

  The wordlength depends on the number of entries you have,
  and the size of every entry. Normally, it's convienient
  to use WORDS. The size of the checklist is the range
  of Z-values to sort, or transformed Z-values.
  If you, for example, know that your Z-values lies within
  512-1023 you can first decrease every z-value by 512,
  and then lsr' it once, which will give you a checklist size
  of 256 words.
  You will also need a second buffer to put your sorted
  data in, this 2ndBUF will be  like a copy of the original
  list but with the entries sorted.

  For this method I only present an algorithm, it's
  easier to see how it works from that than from some
  strange text.

  checklist(x) is the x'th word in the checklist.

  1>  CLEAR the checklist (set all words=0)
  2>  TRANSFORM all weights if necessary.
  3>  FOR L=0 TO number of objects
  3.1> ADD ENTRYSIZE TO checklist(transformed weight)
  4>  FOR L=0 TO checklist size-1
  4.1> ADD checklist(L),checklist(L+1)
  5>  FOR L=0 TO number of objects
  5.1> PUT ENTRY at 2ndBUF(checklist(transformed weight))
  5.2> ADD ENTRYSIZE TO checklist(transformed weigth)

  Now, your data is nicely sorted in the list 2ndBUF, the
  original list is left as it was (except for Z-transformation).
  (ENTRYSIZE is the size of the entry, so if you have x,y,z coordinates
  in words, your size is 3 words=6 bytes.)
  Also try to think a little about what you get when you
  transform. The subtraction is useful since it minimizes the
  loops, but lsr-ing the weights take time and makes the
  result worse. Of course you don't have to scan the list every time,
  just make sure that you know what the lowest possible and the
  higest possible weight is.

Method 3) the Quick-Sort
  This is another kind of sorting, and here it is most efficient
  to use pointers, so that each entry have a pointer to NEXT entry.

  you can one entry like this:


  (offsets are from sortlist start address...)

  To access this routine you will have to give a FIRST entry
  and number of entries. In the original execution, first entry
  is of course 0 (=first entry) and the number of entries is
  of course the total number of entries.
  You must set all previous/next pointers to link a chain.

  Quicksort is recursive, which means that you will have to
  call the routine from within itself. This is not at
  all complicated, you just have to put some of your
  old variables on the stack for safe-keeping.

  What it does is this:
+> The first entry in the list is the PIVOT ENTRY.
|  For each other ENTRY, we put it either BEFORE or AFTER
|  the PIVOT. If it is lighter than the PIVOT we put it BEFORE,
|  otherwise we put it AFTER.
|  Now we have two new lists, All entries BEFORE the PIVOT,
|  and all entries AFTER the PIVOT (but not the pivot itself,
|  which is already sorted).
|  Now we quicksort All entries BEFORE the pivot separately
+< and then we quicksort all entries AFTER the pivot.
   (We do this by calling on the routine we're already in)
   This may cause problems with the stack if there's too
   many things to sort.

   The recursion loop is broken when there's <=1 entry
   to sort.

   Contrary to some peoples belief, you don't need any extra
   lists to solve this.


Inparameters: (PivotEntry=first element of list
               List size=size of current list)
1> If list size <= 1 then exit
2> PivotWeight=Weight(PivotEntry)
3> for l=2nd Entry to list size-1
3.1> if weight(l) > PivotWeight
3.1.1> insert entry in list 1
3.2> ELSE
3.2.1> insert entry in list 2
4> Sort list 1 (bsr quicksort(first entry list 1, size of list 1))
5> Sort list 1 (bsr quicksort(first entry list 2, size of list 2))
6> Link list 1 -> PivotEntry -> list 2

  (PivotEntry = FirstEntry, it don't have to be like this, but I prefer
   it since I find it easier.)

Vector Balls
  Vector balls is simple. It is just to calculate where
  the ball is (with rotations, translations or whatever
  it can be). Sometimes you also calculate the size
  of the ball and so on.

  You don't have to have balls. You can have the Convex
  parts of an Inconvex filled object, or you can
  have images of whatever you like. In three dimensions
  you will have the problem with images (balls or whatever)
  that should be in front of others because it is
  further away from you. Here is where SORTING
  comes in. If you BEGIN blitting the image that
  is most distant to you, and step closer for
  each object, you get a 3d-looking screen.
  The closest image will be the closest.

  Normally, you start with clearing the screen you're not
  displaying at the moment (Parts of it anyway. A person
  in Silents only cleared every second line...)

  Then (while the blitter is working) you start rotating,
  sorting and preparing to finally bob the images out

  and when you've checked that the blitter is
  finished, you start bobbing out all images,
  and when the frame is displayed, you swap
  screens so you display your finished screen
  the next frame.


Appendix A: Example sources.

A 1. An example of an optimized rotation matrix calculation

* For this routine, you must have a sinus table of 1024 values,
* and three words with angles and a place (9 words) to store
* the transformation matrix.
*    __   .
*  /( |( )|\/ '(|)
* /  )|(|\|/\   |)


		lea	Coses_Sines(pc),a0
		lea	Angles(pc),a2
		lea	Sintab(pc),a1

		move.w	(a2),d0
		and.w	#$7fe,d0
		move.w	(a1,d0.w),(a0)
		add.w	#$200,d0
		and.w	#$7fe,d0
		move.w	(a1,d0.w),2(a0)
		move.w	2(a2),d0
		and.w	#$7fe,d0
		move.w	(a1,d0.w),4(a0)
		add.w	#$200,d0
		and.w	#$7fe,d0
		move.w	(a1,d0.w),6(a0)
		move.w	4(a2),d0
		and.w	#$7fe,d0
		move.w	(a1,d0.w),8(a0)
		add.w	#$200,d0
		and.w	#$7fe,d0
		move.w	(a1,d0.w),10(a0)


		lea	Constants(pc),a1
		move.w	6(a0),d0
		move.w	(a0),d1
		move.w	d1,d2
		muls	d0,d1
		asr.l	#8,d1
		move.w	2(a0),d3
		muls	d3,d0
		asr.l	#8,d0
		move.w	d0,(a1)
		;neg.w	d1
		move.w	d1,2(a1)
		move.w	4(a0),4(a1)
		move.w	8(a0),d4
		move.w	d4,d6
		muls	4(a0),d4
		asr.l	#8,d4
		move.w	d4,d5
		muls	d2,d5
		muls	10(a0),d2
		muls	d3,d4
		muls	10(a0),d3
		add.l	d4,d2
		sub.l	d5,d3
		asr.l	#8,d2
		asr.l	#8,d3
		move.w	d2,6(a1)
		neg.w	d3
		move.w	d3,8(a1)
		muls	6(a0),d6
		asr.l	#8,d6
		neg.w	d6
		move.w	d6,10(a1)
		move.w	10(a0),d0
		move.w	d0,d4
		muls	4(a0),d0
		asr.l	#8,d0
		move.w	d0,d1
		move.w	8(a0),d2
		move.w	d2,d3
		muls	(a0),d0
		muls	2(a0),d1
		muls	(a0),d2
		muls	2(a0),d3
		sub.l	d1,d2
		asr.l	#8,d2
		move.w	d2,12(a1)
		add.l	d0,d3
		asr.l	#8,d3
		neg.w	d3
		move.w	d3,14(a1)
		muls	6(a0),d4
		asr.l	#8,d4
		move.w	d4,16(a1)


Coses_Sines	dc.w	0,0,0,0,0,0
Angles		dc.w    0,0,0
Constants	dc.w	0,0,0,0,0,0,0,0,0

;Sintab is a table of 1024 sinus values with a radius of 256
;that I have further down my code...

A 2. A line drawing routine for filled vectors in assembler:

* written for kuma-seka ages ago, works fine and
* can be optimized for special cases...
* the line is (x0,y0)-(x1,y1) = (d0,d1)-(d2,d3) ...
* Remember that you must have DFF000 in a6 and
* The screen start address in a0.
* Only a1-a7 and d7 is left unchanged.
*    __   .
*  /( |( )|\/ '(|)
* /  )|(|\|/\   |)

Screen_widht=40	;40 bytes wide screen...
fill_lines:	;(a6=$dff000, a0=start of bitplane to draw in)

	cmp.w	d1,d3
	beq.s	noline
	ble.s	lin1
	exg	d1,d3
	exg	d0,d2
lin1:	sub.w	d2,d0
	move.w	d2,d5
	asr.w	#3,d2
	ext.l	d2
	sub.w	d3,d1
	muls	#Screen_Widht,d3	;can be optimized here..
	add.l	d2,d3
	add.l	d3,a0
	and.w	#$f,d5
	move.w	d5,d2
	eor.b	#$f,d5
	ror.w	#4,d2
	or.w	#$0b4a,d2
	swap	d2
	tst.w	d0
	bmi.s	lin2
	cmp.w	d0,d1
	ble.s	lin3
	move.w	#$41,d2
	exg	d1,d0
	bra.s	lin6
lin3:	move.w	#$51,d2
	bra.s	lin6
lin2:	neg.w	d0
	cmp.w	d0,d1
	ble.s	lin4
	move.w	#$49,d2
	exg	d1,d0
	bra.s	lin6
lin4:	move.w	#$55,d2
lin6:	asl.w	#1,d1
	move.w	d1,d4
	move.w	d1,d3
	sub.w	d0,d3
	ble.s	lin5
	and.w	#$ffbf,d2
lin5:	move.w	d3,d1
	sub.w	d0,d3
	or.w	#2,d2
	lsl.w	#6,d0
	add.w	#$42,d0
bltwt:	btst	#6,2(a6)
	bne.s	bltwt
	bchg	d5,(a0)
	move.l	d2,$40(a6)
	move.l	#-1,$44(a6)
	move.l	a0,$48(a6)
	move.w	d1,$52(a6)
	move.l	a0,$54(a6)
	move.w	#Screen_Widht,$60(a6)	;width
	move.w	d4,$62(a6)
	move.w	d3,$64(a6)
	move.w	#Screen_Widht,$66(a6)	;width
	move.l	#-$8000,$72(a6)
	move.w	d0,$58(a6)
noline:	rts

A 3. The quicksort in 68000 assembler:

* Sorts a list that looks like:
* Next entry offset.w, (x,y,z).w.
* all offsets must be set except for first entry's previous offset
* and the last entry's next offset.
* Offsets are FROM FIRST ADDRESS of sorting list
* a5=first address of sorting list!
*    __   .
*  /( |( )|\/ '(|)
* /  )|(|\|/\   |)


QuickSort	;(a5=start of sortlist,
		; d0=0 (pointer to first entry, first time=0)
		; d1=number of entries)

		cmp.w	#1,d1
		ble.s   .NothingToSort		;don't sort if <=1 entries
		moveq	#0,d4			;size list 1
		moveq	#0,d5			;size list 2
		move.w	d0,d6			;first Nentry=d0

		move.w  WghtOffs(a5,d0.w),d2	;d2=Pivot weight
		move.w  NextOffs(a5,d0.w),d3	;d3=2nd entry
		subq.w	#2,d1			;Dbf-loop+skip first

..Permute        cmp.w	WghtOffs(a5,d3.w),d2	;entry weight<pivot weight?
		ble.s	.Lower

		move.w	d6,NextOffs(a5,d3.w)	;Insert BEFORE Nentry
		addq.w	#1,d4			;increase size of list 1
		move.w	d3,d6                   ;Set new Nentry

		bra.s	.Done			;Continue the loop...

..Lower		move.w	NextOffs(a5,d0.w),NextOffs(a5,d3.w)
		move.w	d3,NextOffs(a5,d0.w)	;insert AFTER first entry
		addq.w	#1,d5			;size of list 2

..Done		move.w	NextOffs(a5,d3.w),d3	;Get next entry
		dbf	d1,.permute

		move.w	d0,-(a7)                ;save Fentry..

		move.w	NextOffs(a5,d0.w),d0	;Sort at entry after first
		move.w	d5,d1			;Size of list 2

		movem.w	d4/d6,-(a7)		;Save important registers
		bsr	QuickSort               ;and sort list 2
		movem.w	(a7)+,d4/d6		;d1 is now First Entry...
		move.w	(a7)+,d1

		move.w	d0,NextOffs(a5,d1.w)	;Put first entry of
						;list 2 after Fentry...
		move.w	d6,d0			;Sort at Nentry
		move.w	d4,d1			;size of list 1

		bsr	QuickSort		;no important registers
		;Now the offset to the first entry is in d0!
		;to get the other values in the correct order
		;just go down the list (using nextoffs.)
       		;First object is the heaviest...


A 4. The Insert Sort in 68020 assembler:

* This isn't exactly as the algorithm described earlier,
* it begins with creating a list and then stores the ADDRESSES of the
* sorted data in 2ndBUF instead...
* This sorts all lists, just specify offset to weight (word) and
* size of each entry. You don't need any pre-formatting.
* note that you HAVE TO change a line if you want this to work
* on 68000.. I've got a scaled index at one point. replace it
* with the lines after the semicolon.
*    __   .
*  /( |( )|\/ '(|)
* /  )|(|\|/\   |)


		;(a5=start of data
		; a4=start of checklist
		; a3=start of 2ndBUF
		; d0 is lowest value of entries
		; d1 is highest value
		; d2 is number of entries

		movem.l	a4/a5,-(a7)

		sub.w	d0,d1           ;max size of checklist this sort.
        	subq.w	#1,d2
		subq.w	#1,d1		;Dbf-loops...

		move.w	d1,d3		;clear used entries
..ClearChecklist	clr.w	(a4)+
		dbf	d3,.ClearCheckList

		move.w	d2,d3		;transform...
..Transform	sub.w	d0,WghtOffs(a5)
		addq.w	#EntrySize,a5
		dbf	d3,.Transform

                movem.l	(a7),a4/a5

		move.w	d2,d3 		;Insert next line instead for
..AddisList	move.w	WghtOffs(a5),d0	;68000 compatibility...
		addq.w	#4,(a5,d0.w*2)	;add.w d0,d0 addq.w #4,(a5,d0.w)
		addq.w	#EntrySize,a5
		dbf	d3,.AddisList

		moveq	#-4,d0		; #-lwdsize
..GetMemPos	add.w   d0,(a4)
                move.w	(a4)+,d0
		dbf	d1,.GetMemPos

		movem.l	(a7)+,a4/a5
..PutNewList	move.w	WghtOffs(a5),d0
		move.w	(a4,d0.w),d0
		move.l	a5,(a3,d0.w)
		addq.w	#EntrySize,a5
		dbf	d2,.PutNewList

		;In this case you have a list of ADDRESSES to
		;each object. I made it this way to
		;make it more flexible (you maybe have more
		;data in each entry than me?).


Appendix B: Some other info...

B 1: 'Proof' of hidden-plane elimination equation

  I presented the following equation:
  as a calculation of the normal vector of the plane
  that the polygon in question spanned.

  We had three points:

  If we select p1 as base-point, we can construct the following
  vectors of the rest of the points:


  Where p and q in the z value denotes that we are not interested in
  this value, but we must take it in our calculations anyway.
  (These values are NOT the same as the original z-values
   after the 2d->3d projection)

   Now, we can get the normal vector of the plane that these vectors
   span by a simple cross-product:

   V1 x V2 =

  |  i       j      k|
= |(x3-x1) (x2-x1)  p|  (if i=(1,0,0), j=(0,1,0), k=(0,0,1))
  |(y3-y1) (y2-y1)  q|  (p and q are non-important)

  But we are only interested in the Z-direction of the
  result-vector of this operation, which is the same as
  getting only the Z-coordinate out of the cross-product:

   Z of (V1xV2) = (x3-x1)*(y2-y1)-(x2-x1)*(y3-y1)

  Now if Z is positive, this means that the resultant vector
  is aiming INTO the screen (positive z-values)
  QED /Asterix

B 2. How to make a fill line out of the blitters line-drawing
  You can't use the blitter line-drawing as it is and
  draw lines around a polygon without a few special changes.

  To make a fill-line routine out of a normal-lineroutine:

   First, make sure it draws lines as it should,
   many line-drawers I've seen draws lines to wrong points
   Make sure you use Exclusive or- instead of or-minterm
   Always draw lines DOWNWARDS. (or UPWARDS, if you prefer that)
   Before drawing the line and before blit-check, eor the FIRST
   Use fill-type line mode.

B 3: An alternate approach to rotations in 3-space by M. Vissers

/* This is a text supplied by Michael Vissers, and was a little
longer. I removed the part about projection from 3d->2d,
which was identical to parts of my other text in chapter 3.
If you know some basic linear algebra, this text might be
easier to melt than the longer version discussed in chapter 4.
If you didn't get how you were supposed to use the result in
chapter 4 then try this part instead. */

[ ] All you have to do is using these 3D matrices :

(A/B/G are Alpha,Beta and Gamma.) /* A,B,C = Angles of rotation */

|  cosA  -sinA  0  |    |  cosB   0  -sinB  |    |   1    0      0    |
|  sinA   cosA  0  |    |   0     1    0    |    |   0   cosG  -sinG  |
|   0      0    1  |    |  sinB   0   cosB  |    |   0   sinG   cosG  |

These are the rotation matrices around the x,y and z axis'. If you would
use these you'll get 12 muls'. 4 four for each axis. But, if you multiply
these three matrices with eachother you'll get only 9 muls'. Why 9 ???
Simple : after multiplying you'll get a 3x3 matrice, and 3*3=9 !

It doesn't matter if you do not know how to multiply these matrices. It's
not important here so I'll just give the 3x3 matrice after multiplying :

(c = cos, s = sin, A/B/G are Alpha,Beta and Gamma.)

        |     cA*cB               -cB*sA          sB   |
        | cG*sA-sB*cA*sG      cA*cG+sG*sA*sB     cB*sG |
        |-sG*sA-sB*cA*cG     -cA*sG+sA*sB*cG     cG*cB |

I hope I typed everything without errors :) Ok, how can we make some
coordinates using this matrice. Again, the trick is all in multiplying.
To get the new (x,y,x) we need the original points and multiply these with
the matrice. I'll work with a simplyfied matrice. (e.g. H = cA*cB etc...)

                        x   y   z   ( <= original coordinates)
        New X =     |   H   I   J   |
        New Y =     |   K   L   M   |
        New Z =     |   N   O   P   |


        New X = x * H + y * I + z * J
        New Y = x * K + y * L + z * M
        New Z = x * N + y * O + z * P

Ha ! That's a lot more than 9 muls'. Well, actually not. To use the matrice
you'll have to precalculate the matrice.

Always rotate with your original points and store them somewhere else.
Just change the angles to the sintable to rotate the shape.
If you rotate the points rotated the previous frame you will lose all detail
until nothing is left.

So, every frame looks like this :      - pre calculate new matrice with
                                         given angles.
                                       - Calculate points with stored
[ ]
The resulting points are relative to (0,0). So they can be negative to.
Just use a add to get it in the middle of the screen.

NOTE: Always use muls,divs,asl,asr etc. Data can be both positive and
      negative. Also, set the original coordinates as big as possible,
      and after rotating divide them again. This will improve the
      quality of the movement.

(Michael Vissers)

B 4: A little math hint for more accurate vector calculations

When doing a muls with a value and then downshifting the value, use
and 'addx' to get roundoff error instead of truncated error, for
	moveq	#0,d7
	muls	(a0),d0		;Do a muls with a sin value *256
	asr.l	#8,d0
	addx.w	d7,d0		;roundoff < trunc

When you do a 'asr' the last outshifted bit goes to the x-flag.
if you use an addx with source=0 => dest=dest+'x-flag'.
This halves the error, and makes complicated vector objects
less 'hacky'.

 /)    __   .
((   /( |( )|\/  '(|)
 )) /  )|(|\|/\    |)