    ;  Routine to draw one pixel at desired location.
    ;  After the pedantic Geodesic Publications code, Linesdemo.
    ;  C call is   pixel ( &bitmap, color number, x, y );
    ;  Bitmap pointer passed on stack to facilitate dual-playfield calls.
    ;  Compiler = Manx 3.6A

    ;  Thomas J. Eshelman   Reading, Pa.    12/20/88

                                 CSEG

                            public   _pixel

                                 DSEG

bm_BytesPerRow equ   0
bm_Depth       equ   5
bm_Planes      equ   8

                                 CSEG
_pixel:

   movem.l  d2-d7/a2-a6,-(sp)          Save non-scratch registers.

    ;At this point, we have 44 bytes for the 11 registers and 4 bytes the
    ;system used to save the return address when we called _pixel between
    ;us and the arguments we passed and want to retrieve from the stack.
    ;Hence, the first argument is located 48 bytes above the bottom.
    ;This is because the stack decreases as it gets larger.  (It was invented
    ;by an Congressional Committee, you see.)  The LAST argument saved to the
    ;stack is the FIRST one available to come off.  Hence, the first one to
    ;come off, the one with the lowest address, is the bitmap pointer.

   move.l   48(a7),a5                  Pointer to desired bitmap.
   movem.w  52(a7),d0-d2               MOVEM pops args in SAME order pushed.

   move.w   d0,d3                      Copy color value for the helluva it.

   move.w   d2,d7                      Copy Y coordinate.
   mulu     bm_BytesPerRow(a5),d7      Byte offset of line containing Y.

   ;d7 now holds the offset value, in number of bytes from the beginning of
   ;each display plane, of the first byte of the line in which we find the
   ;'Y' coordinate.  All we need do now is find out how far to the right, on
   ;this line, of course, we must move in order to locate 'X' coordinate.

   move.w   d1,d4                      Copy X coordinate.

   ;The next 2 instructs divide X by 8 in order to convert BITS (the coord.
   ;being expressed in terms of bits) into BYTES.  We have the second step
   ;to guarantee an even result.  (Shifting is much faster than division).

   lsr.w    #4,d4
   lsl.w    #1,d4

   ;The resulting base offset from the beginning of a line is added to the
   ;the Y offset so we now have the offset of the byte in the plane that
   ;we want to use to draw the pixel.  From here on,  we must advance bit
   ;by bit (yuk, yuk, yuk!).

   add.w    d4,d7
   ext.l    d7

   move.w   d1,d4                      Recopy X coordinate.

   ;This simple move yields how many BITS further right we must advance
   ;prior to drawing the pixel.  Consider an X value of 150.  This is binary
   ;1001 0110.  Divided by 8 is 18 with 6 left over.  So we will move left
   ;18 bytes followed by 6 bits to draw.  The number of those bits we must
   ;advance is necessarily contained in the rightmost nybble. Please extract.

   andi.w   #$f,d4                     Thank you.

   ;Set a bit in the high order byte of a register word that will represent
   ;the bit to be moved right, if at all.  This bit represents the pixel we
   ;will draw or erase.  Obviously, we then shift this bit into the exact
   ;position we determined by the algorithm above. (In the above example,
   ;this would be 6 bits right, which yields a literal hex value of $0200.
   ;Don't worry about 'value'.  That is for illustration only.  We are only
   ;concerned with the physical bit position.)

   move.w   #$8000,d5
   lsr.w    d4,d5

   move.w   d5,d6                      d5 has the OR mask for pixel draw.
                                            ;0000 0010 0000 0000
   not.w    d6                         Prepare an AND mask for pixel erase.
                                            ;1111 1101 1111 1111

   move.b   bm_Depth(a5),d0            Number of planes to draw into.
   ext.w    d0

   subq.w   #1,d0                      Subtract one from Depth for dbra.

   clr.w    d1                         Actual Depth or Plane counter.

10$:

   ;Multiply the counter by 4 and add it to the PLANEPTR value so that it
   ;points to the next plane.  (0, 4, 8, 12, 16 byte offsets).  bm_Planes is
   ;but an Array of Pointers, each pointing the to base of the next plane
   ;comprising our bitmap.

   move.w   d1,d2
   lsl.w    #2,d2                      Plane number times 4.

   movea.l  bm_Planes(a5,d2),a3        Retrieve this PLANEPTR.

   add.l    d7,a3                      Come to byte in which we shall plant
   ;                                   our pixel.

   ;Achtung!  For the user's color choice, see to it that the bits for each
   ;plane are set or reset so that the pixel of this color are in fact
   ;displayed.  For example, if the user asked for color 5, then to draw a
   ;pixel in that color in plane 1, we must be sure to zero the bit.

   btst     d1,d3                      Test the color bit in our plane.
   beq      20$                        Branch if unset.

   ;a3 points to the byte containing the coordinate.  However, we will OR a
   ;WORD against this BYTE.  Observe, supra, it is the HIGH order BYTE of d5
   ;and d6 that contain the 'shift mask' (which in our example is $0200.
   ;That makes the high order byte of said word, $02).

   ;Observe, also, that with the 68000, the high byte of the register is the
   ;lower address in memory.  Therefore, we are applying the mask to the
   ;exact BYTE referenced by (a3) and to the byte above that, (which latter
   ;is of no effect because it always gets ORed with zeros).

   or.w     d5,(a3)                    Set the coordinate bit.  Behold!!
   bra      30$                        Test next plane.

20$:

   and.w    d6,(a3)                    Reset the coordinate bit.  Zap!

30$:

   addq.w   #1,d1                      Advance to next plane, and to the
   dbra     d0,10$                     corresponding binary color bit.

   movem.l  (sp)+,d2-d7/a2-a6          Pop user stack.
   rts

                                  end
