Decoded: Sopwith (1984) by David L. Clark
Source file: SWGRPH.ASM
Beginner friendly, line-by-line code walkthrough by MaiZure

SWGRPH.ASM is assembly to drive the CGA display in Sopwith.

Original code:
http://davidlclark.com/page/sopwith-source-code

Original code with line numbers
http://www.maizure.org/projects/decoded-sopwith/SWGRPH_linenum.txt

1     COMMENT
2     COMMENT
3     COMMENT
4     COMMENT
5     COMMENT
6     COMMENT
7     COMMENT
8     COMMENT
9     COMMENT
10    COMMENT
11    COMMENT
12    COMMENT
13    COMMENT
14    COMMENT
15    COMMENT
16    COMMENT
17    COMMENT
18    COMMENT
19    COMMENT
20    COMMENT
21    COMMENT
22    COMMENT
23    COMMENT
24    COMMENT
25    BLANK
26    BLANK
27    Sets the assembler to use small memory model using C calling conventions.
      These are passed as arguments to the assembler in the makefile (SW.MAK). Small
      memory model implies data and stack are in the same 64kb segment.
28    Includes macros provided with MASM
29    BLANK
30    Creates a local code segment, linker will combine upcoming code with other
      code
31    BLANK
32    Imports @AB variable. @AB == 4
33    Imports game header definitions in assembly (see SW.HA)
34    BLANK
35    Function swdisp - Removes and displays all objects
36    Function swground - Displays the ground at the title screen
37    Function swputsym - Draws a graphic at input coordinates
38    Function swputcol - Tests a graphic for collision at a point
39    Function swclrcol - Removes a graphic from the screen
40    Function swpntsym - Draws a pixel color at a specific point
41    Function swpntcol - Tests a specific point for collision
42    Function get_type - Returns the video mode
43    Function set_type - Sets the video mode
44    Function setvdisp - Sets the video RAM as the display source
45    Function setadisp - Sets the aux buffer as the display source
46    BLANK
47    Imports dispoxsplat from SWDISP.C to display the splattered ox
48    BLANK
49    BLANK
50    BLANK
51    COMMENT
52    BLANK
53    BLANK

MAIN GAME RENDERING FUNCTION
54    Define swdisp to display the world objects
55    Store the previous frame
56    Store the DI register
57    Store the SI register
58    Store the ES register
59    BLANK
60    COMMENT
61    COMMENT
62    COMMENT
63    Set video RAM as the active display segment
64    BLANK
65    COMMENT
66    COMMENT
67    COMMENT
68    COMMENT
69    BLANK
70    Move the top object pointer to BX
71    BLANK
72    Label disploop: Loop through objects to display
73    Update flags on BX value (NULL or not)
74    If BX has a value then jump to line 77
75    BX has no value, so all objects have been processed. Return
76    BLANK
77    Label disp00, object processing start. Move obj delete flag to AX
78    Compare AX (delete flag) with the object's draw flag
79    If either flag is zero, nothing to delete/redraw so jump to line 94
80    Check if the object is only 1 pixel in size
81    If only 1 pixel, jump to line 94
82    Move the old symbol pointer in to AX
83    Compare the new graphiv with the old graphic
84    If the symbols do not match then jump to line 94
85    They match, so check if the object is moving. Move Y value to AX
86    Compare Y with the old Y to see if there is vertical movement
87    If the y values are different, it is moving vertically - jump to line
      94
88    Move the object's old x in to AX
89    Add the screen movement factor to AX
90    Check if the new x and the adjusted old x match.
91    If they don't match, then it's moving with x so jump to line 94.
92    If we've made it here, it may be ready to display, test draw on line 121
93    BLANK
94    Label disp0: Time to XOR delete the old object
95    Check for the delete flag by comparing 0 with the flag
96    If it IS zero, then we don't have to delete. Jump to line 103
97    Otherwise, we let's set up for delete. Move the old y in to DI
98    Move the old x in to SI
99    Move the old symbol reference in to DP
100   Move the object's color (it's team) in to AX
101   Call drawsym (line 508)
102   BLANK
103   Checks if the draw flag is asserted or not
104   If it is zero, there's nothing to draw so jump to line 129
105   We have to draw so load the x coordinate in to SI (obj pointer in BX)
106   Compare the object's x coordinate with the left display coordinate
107   If object x is less than screen, its not on screen, jump to line 128
108   Compare the object's x coordinate with the right display coordination
109   Greater than right side? Then still not on screen so jump to line 128
110   It must be on screen, so get it's offset on the screen in to SI
111   Call testox (line 176) to see if it's been hit, which skips drawing. 
112   Ox was hit? Skip drawing by jumping to line 128
113   BLANK
114   Push the old object x coordinate (on screen) in to oldx  
115   Save the object's y in to DI
116   Move the DI in to old y to store its on screen y
117   Move the object's graphic pointer in to BP
118   Move the object's color (team) in to AX
119   Call drawsym to execute drawing on line 508
120   BLANK
121   Check for a display object pointer in the object
122   No display? Then Nothing to do so jump to line 129
123   There is a display so save the object's pointer
124   Call the display function (uses stored BX)
125   Restore BX stored on line 123
126   We're done, so jump to line 129
127   BLANK
128   Object was not drawn so save 0 in to draw flag for future comparison
129   Move the next object pointer in to the current object pointer
130   Redo everything above for the next object on line 72
131   BLANK
132   COMMENT
133   COMMENT
134   COMMENT
135   COMMENT
136   BLANK
137   Procedure dispdel removes deleted objects from the screen
138   BLANK
139   Move the top deleted object pointer in to BX
140   BLANK
141   dispdlop table, 
142   Check of there is actually an object to delete
143   If no object, then go to line 156
144   BLANK
145   Check if the object is to be deleted (delete flag is 1)
146   If it is zero (no delete), then jump to line 154
147   If it needs to be removed, then move the old y coordinate in to DI
148   Move the old x coordinate in to SI
149   Move the graphic pointer in to BP
150   Move the object color/team in to AX
151   Call xor remove procedure on line 508
152   BLANK
153   No need to remove so load the next candidate object in to BX
154   Jump back to line 141 to test for removal again
155   BLANK
156   dispret label, return from deleting objects
157   Done removing objects, check if the ox is splattered
158   If it's splattered, then skip ground display and jump to line 164
159   Otherwise, we need to update the ground display,
160   Check if the ox is still ok
161   If so, jump to finish on line 164
162   The ox was splattered so call the procedure to draw splat in SWDISP.C
163   BLANK
164   Procedure dispdone wraps up the call to swdisp
165   Unset display init
166   Unset forced draw
167   Restore ES that was stored on line 58
168   Restore SI that was stored on line 57
169   Restore DI that was stored on line 56
170   Restore the previous stack frame
171   Return to caller
172   BLANK
173   BLANK
174   COMMENT
175   BLANK
176   Procedure testox, which checks if splattered ox interferes with draws
177   Check for splattered ox
178   No, then nothing to do here, skip to line 189
179   BLANK
180   Store AX
181   Store DX
182   Object x current in SI, move to AX
183   Moves 1/3rd of the screen size in to DL
184   Divides AX by 109, result is in AX
185   Updates flags (sign, zero, and parity)
186   Restore DX stored on line 181
187   Restore AX stored on line 180
188   BLANK
189   Return to caller (line 112)
190   BLANK
191   COMMENT
192   BLANK
193   Procedure dispgrnd displays the ground
194   Clear DF in flags to ensure increments of SI/DI
195   Check if this is the first time we're displaying ground
196   If so, we don't need to erase old ground, jump to line 206
197   We're erasing old ground, but first check if screen is moving
198   It's moving so we need to erase, jump to line 203
199   We're not moving, but check if display is set to forced redraw
200   It is forced so jump to dg0 to erase ground
201   Return to caller, ground doesn't need to update
202   BLANK
203   We need to erase old ground. Load old ground point in to SI
204   Call ground redraw with XOR on line 219
205   BLANK
206   Prepare to draw ground by storing DS to AX  
207   Then move AX to ES (can't move segments directly)
208   Load the ground address in to SI
209   Offset the display in to the ground array, 1 byte/pixel per ground point
210   Save the location in the ground array to BX
211   Load the ground destination area in to DI
212   Set the counter register to half the screen width
213   Move from SI to DI a word (2 bytes) at a time. Hence half screen in CX
214   BLANK
215   Move the ground address in to SI
216   Display the ground using dispg starting on line 219
217   Return
218   BLANK
219   Move the odd raster line offset in to DX
220   Then move it in to BX
221   Move up one line from position in MX
222   BLANK
223   SI is pointing to the beginning of the line, move to AL (i.e. 89)
224   Then to CL
225   Since line 0 is at the bottom of the screen, invert height (89-200=-111)
226   Twos complement -111 becomes 111. Thus a height of 89 is at screen y 111
227   Shift it right, effectively halving it due to interlacing.
228   Move the line width (80) int o CH
229   Multiplies the screen width by the height to find the first pixel
230   BLANK
231   Checks if the first pixel is on the even (base) or odd (roff) line
232   If we're on the correct line, jump to line 238 to prep memory address
233   Otherwise, we're off by 1 line, so add DX (raster offset) to AX
234   The odd offset lines need to flip the offsets, twos complement invert
      BX
235   Same with DX
236   Swap the values in BX (screen width) and DX (odd raster offset)
237   BLANK
238   We're ready to set up memory. Add the display offset to AX
239   AX becomes the target in the display segment
240   BLANK
241   Move the memory location of the ground start in to BP
242   Move BP to the end of the screen display
243   Assert bits 7 and 8 in CH, base pixel is white and even though CGA packs
      4 pixels per byte, we're only going to do each pixel individually and rotate
      as needed
244   Move the first ground height in to AH (for comparing heights each sweep)
245   Set ES to video out segment
246   BLANK
247   Start the draw loop
248   Load the next ground height in to AL
249   Compare this height with the previous height
250   If new height is higher, then jump to line 256
251   If new height is lower, then jump to line 266
252   BLANK
253   If they are the same, then XOR the dot at the address for the height
254   Get the next pixel starting on line 275
255   BLANK
256   We need to move up a line, changing raster segments. Add our neg offset
257   Invert current segment offset
258   Invert other segment offset
259   Exchange them since moving lines moves segments
260   Draw the dot (may not be the endpoint, but we plot anyway for a line)
261   Increment the last height
262   Check if the heights now match
263   If we need to keep moving up, jump back to line 256 and do this again
264   Otherwise, go to the next pixel on line 275
265   BLANK
266   The next ground pixel is below the current, lets change raster segments
267   We've changed basis, like the above case, so negate BX
268   Then DX
269   Then swap again
270   XOR the mask (0xC0 - single dot in CGA)
271   Drop the height comparison
272   Check if we're at the correct height
273   IF not, jump back to line 266 and do this again
274   BLANK
275   Move to the next pixel, we shift the mask two bits. First shift...
276   And second shift...(why not just shift 2 bits outright? Clobber risk?)
277   See if we've shifted our pixel out of the byte (happens every 4 pixels)
278   If not, then we're ready for the next pixel. Jump back to line 247
279   Move to the next byte in video memory
280   Restart the XOR mask (0xC0 = 11000000)
281   Check if we've reached the end of the ground array on screen
282   Not done? Get back to work on line 247
283   We are done, so return to caller
284   BLANK
285   BLANK
286   BLANK

DRAW GROUND FOR TITLE SCREEN
287   COMMENT
288   BLANK
289   Define swground to draw the ground at the title screen
290   Store the previous frame
291   Store the DI register
292   Store the SI register
293   Store the ES register
294   Call dispgrnd, general ground drawing function
295   Restore ES register stored on line 293
296   Restore SI register stored on line 292
297   Restore DI register stored on line 291
298   Restore the previous stack frame
299   Return
300   BLANK
301   BLANK
302   BLANK
303   BLANK
304   COMMENT
305   BLANK

CLEAR COLLISION MASK TEST
306   Defines swclrcol to remove collision test drawings
307   Store the DI register
308   Store the SI register
309   Store the ES register
310   BLANK
311   Clear DF in flags so we move forward in memory
312   Move the display segment in to ES
313   Move the display offset in to SI (Now reading from ES:SI)
314   Move SI to the last line
315   Clear the base rastor lines on line 325
316   Change to start of other raster line, 0x1000 bytes ahead
317   Clear the remaining raster lines on line 325
318   BLANK
319   Restore the ES register stored on line 309
320   Restore the SI register stored on line 308
321   Restore the DI register stored on line 307
322   Return
323   BLANK
324   BLANK
325   Define clearhalf procedure to XOR half of the screen (even or odd lines)
326   Start at the beginning
327   Start the counter at 8
328   Clear the AX register
329   BLANK
330   Start a loop by storing 8 pixels (word == 2 bytes == 8 pixels in CGA)
331   Store another 8 bytes for a total of 16 pixels
332   Store another 8 bytes for a total of 24 pixels
333   Store another 8 bytes for a total of 32 pixels
334   Store another 8 bytes for a total of 40 pixels
335   Store another 8 bytes for a total of 48 pixels
336   Remember we started at the bottom (line 314), so go up one raster line.
      This technically moves us up 2 rows because we're only working in one half of
      the interlace right now
337   Loop 8 time, automatically decrementing CX
338   Return to caller
339   End procedure for clearhalf
340   BLANK
341   BLANK
342   COMMENT
343   BLANK

DRAW A GRAPHIC
344   Procedure swputsym draws a symbol to a location
345   Create a new frame in BX
346   Store the previous frame
347   Store the SI register
348   Store the DI register
349   Store the ES register
350   BLANK
351   Load argument 1 in to SI, the x coordinate
352   Load arugment 2 in to DI, the y coordinate
353   Load argument 3 in to BX, the object pointer
354   Load the object's graphic pointer in to BP
355   Load the object's color in to AX
356   Call drawsym on line 508 to XOR draw it
357   BLANK
358   Restore the ES register stored on line 349
359   Restore the DI register stored on line 348
360   Restore the SI register stored on line 347
361   Restore the previous stack frame
362   Return
363   BLANK
364   BLANK
365   COMMENT
366   BLANK

DRAW A POINT
367   Procedure swputpntsym to draw a colored point
368   Store the previous frame
369   Create a new frame
370   Store the SI register
371   Store the DI register
372   Store the ES register
373   BLANK
374   Load argument 1 in to SI, the x coordinate
375   Load argument 2 in to DI, the y coordinate
376   Load argument 3 in to BP, the color/team
377   Call draw point on line 601
378   BLANK
379   Restore the ES register from line 372
380   Restore the DI register from line 371
381   Restore the SI register from line 370
382   Restore the BP register from line 369
383   Return to caller
384   BLANK
385   BLANK
386   COMMENT
387   BLANK

TEST GRAPHIC COLLISIONS
388   Procedure swputcol draws and tests a graphic for collision
389   Create a new stack frame (but using BX, not BP)
390   Store the previous frame
391   Store the SI register
392   Store the DI register
393   Store the ES register
394   BLANK
395   Load argument 1 in to SI, the x coordinate
396   Load argument 2 in to DI, the y coordinate
397   Load argument 3 in to BX, the pointer to the object
398   Pointer to the object's graphic in BP
399   BLANK
400   Call setup (line 748) to configure display, sets flags for next check
401   If we're testing an entire image, jump to line 415
402   Otherwise, we're testing a point, jump to line 668
403   Jump to line 450 to clear up collision test
404   BLANK
405   COMMENT
406   COMMENT
407   COMMENT
408   COMMENT
409   COMMENT
410   COMMENT
411   COMMENT
412   COMMENT
413   COMMENT
414   BLANK
415   Start collision test
416   Set the return code to zero for now
417   Step 1 - set up the call
418   Store AX
419   Store DI
420   Clear CH
421   Store BX
422   Load the XOR translation table (Starts on line 877 and provides the XOR
      lookup)
423   Call dispputc (line 468) to test the entire segment for collisions
424   BLANK
425   Check if we've moved beyond the 255 width trackable in AL
426   If no wrap occurrend, continue on lin 432
427   Otherwise we need to wrap, by moving AH to AL
428   Clear AH
429   Offset SI by the segment length
430   Jump to end of loop for anther pass (line 438), if needed
431   BLANK
432   Process line for collision:
433   Check if we have a meaningful value in CH
434   If not, then continue without testing on line 438
435   If there is a value, move it in to DH
436   Call collide (line 849) to perform a check
437   Move value to video
438   Wrap up this iteration
439   Restore BX from line 421
440   Return DI from line 419
441   Change destination lines
442   Covert to offset (Line 864)
443   Return AX from line 418
444   Finished a line, decrement from BP
445   Jump back to line 417 if BP is still positive
446   BLANK
447   Return code may have changed in collide, so store it in AL
448   Clear AH
449   BLANK
450   Return from collision test
451   Restore the ES register from on line 393
452   Restore the DI register from on line 392
453   Restore the SI register from on line 391
454   Restore the previous stack frame
455   Return to caller
456   BLANK
457   COMMENT
458   COMMENT
459   COMMENT
460   COMMENT
461   COMMENT
462   COMMENT
463   COMMENT
464   COMMENT
465   COMMENT
466   COMMENT
467   BLANK
468   Plot the raster line to video ram
469   Clear DL
470   Put the first byte in DH
471   We may be in the middle of the byte (pixels 2-4)
472   Add possible carries from last iteration
473   Call the collision check on like 849
474   XOR the properly shifited and carried byte in to video ram
475   Save the carry
476   Increment the place in the graphic segment
477   Increment the place in video ram
478   Count down the bytes remaining
479   If bytes remain, jump back to like 468
480   BLANK
481   We're done, return to caller
482   BLANK
483   BLANK
484   BLANK
485   COMMENT
486   BLANK

TEST COLLISION POINT
487   Procedure swpntcol draws and tests a point for a collision
488   Store the previous frame
489   Create a new stack frame
490   Store the SI register
491   Store the DI register
492   Store the ES register
493   BLANK
494   Load argument 1 in to SI, the x coordinate
495   Load argument 2 in to SI, the y coordinate
496   Load argument 3 in to SI, the color/team
497   Call drawpntc to do the actual drawing
498   BLANK
499   Restore the ES register from line 492
500   Restore the DI register from line 491
501   Restore the SI register from line 490
502   Restore the previous stack frame
503   Return to caller
504   BLANK
505   BLANK
506   COMMENT
507   BLANK

DRAW A SYMBOL
508   Procedure drawsym. SI, DI, BP, and AX hold the object properties
509   Store the object pointer in BX
510   BLANK
511   Extraneous push? Object pointer now on stack twice
512   Move object color (old color) in to BX
513   Mask the first 2 bits in BX (color)
514   Move the mask byte in to AL (defined in 898, byte 0 = 0, byte 1 = 0xFF)
515   Move that mask value in to variable colmask
516   Restore object pointer
517   BLANK
518   Call setup on line 748 to prepare symbol. Flags represent symbol size
519   If the symbol is larger than a point, jump to line 533
520   Otherwise, it's a point so call drawpnt on line 601
521   We've finished the work so clean up on line 563
522   BLANK
523   COMMENT
524   COMMENT
525   COMMENT
526   COMMENT
527   COMMENT
528   COMMENT
529   COMMENT
530   COMMENT
531   COMMENT
532   BLANK
533   Procedure to draw a large symbol
534   Store AX, which is the length
535   Store DI, which is where we write to video
536   Clear CH, which holds our packed pixel rotation amount within a byte
537   Store the line wrap distance
538   Load the translation table address in to BX (fast XOR lookup)
539   Calls display segment on line 580 to use the above prepped registers
540   BLANK
541   Check if we've wrapped the size of our byte
542   If we're still processing normally (no wrap) then jump to line 548
543   We've wrapped, so AH is now a negative number. Store the wrap in AL
544   Clear AH
545   Add the wrap amount to SI for the new symbol address
546   Jump to line 554 to continue to the next line
547   BLANK
548   No wrap, we're still processing the line
549   Check if we've had a carry (CX was repurposed during dispds)
550   No carry, jump to line 554 to start a new line
551   There's a carry amount, store it in DH
552   Symbol has a different color from base, so jump to line 864 to adjust
553   XOR the adjusted color in DH to video
554   Start a new symbol line
555   Restore the line offset stored on line 537
556   Restore the old video location stored on line 536
557   Offset DI to the new line using BX
558   We've changed to the other raster line so swap adjrast and BX
559   Restore the old segment length stored on line 534
560   We're down one more line, so decrement the line counter
561   If the line counter is positive, we have more work. Back to line 533
562   BLANK
563   All done!
564   Restore BX double stored on line 511 and 509 (bug??)
565   Return to caller, usually from swdisp
566   BLANK
567   BLANK
568   BLANK
569   COMMENT
570   COMMENT
571   COMMENT
572   COMMENT
573   COMMENT
574   COMMENT
575   COMMENT
576   COMMENT
577   COMMENT
578   COMMENT
579   BLANK
580   Time to display the segment we've constructed
581   Clear out the carry space
582   Move the bytes to display in to DH (4 pixels in a byte)
583   Shift DX to the proper pixel
584   Clear everything but the target pixel
585   BLANK
586   Call adjcolor to detect and swap colors between the two teams
587   BLANK
588   Pixel is correct, write it to video
589   We've adjusted the carry, save it in CH for next pixel
590   Move to next source byte
591   Move to the next video ram byte
592   Decrement the length remaining
593   If AL is still positive, repeat from line 580
594   BLANK
595   Segment done, return
596   BLANK
597   BLANK
598   BLANK
599   COMMENT
600   BLANK

DRAW A PIXEL
601   Procedure to draw a point (no symbol or masks)
602   BLANK
603   COMMENT
604   BLANK
605   Adjust our target line to swap from game basis to screen basis 
606   Twos complement the now negative screen basis
607   BLANK
608   COMMENT
609   COMMENT
610   BLANK
611   Get the pixel column in to CX
612   Mask the first 2 bytes (are we at base byte offset or sub-byte?)
613   Invert the value, which becomes the shift amount after...
614   Twos-complement the inversion (a bit value of 2 became -1 and now 1)
615   Shift left (2 bits per pixel). Our column requires 2 double bit shifts
616   BLANK
617   COMMENT
618   BLANK
619   Move the start address in to AX
620   Halve row value since we only care about half the screen (one scanline)
621   Move the screen width in to DX
622   Screen width * row == end of row
623   End of row + Column number in SI = pixel offset, but we need byte offset.
      Remember that each byte is 4 packed pixels, so we aren't interested in the lowest
      two bits, just the byte we can use to address the upcoming change. Rather, we'll
      rotate the target byte to the pixels we're interested in
624   Increment DX with CF flag based on previous operation for carry
625   Shift DX right -- the least significant bit drops off in to the CF
626   Rotate AX right, this picks up the CF from DX from the last shift in to
      MSB. The LSB moves to CF, but that doesn't matter.
627   Do the shifting again for DX -- clobbers the LSB in CF from AX rotation
628   Rotate AX one last time and we should have the byte we're interested in
      
629   Store the byte we'll modify in SI.
630   BLANK
631   COMMENT
632   BLANK
633   Checks the first bit of the row number to see if we're on an odd line
634   If we're on an even line, then skip the next instruction (to line 636)
635   Odd line, so we need to move the source forward to the other raster segment
636   Jump label skipping raster 
637   Move the video ram base address in to AX (Temp)
638   Move video ram base address in to extra segment, ES
639   Move SI back to base
640   BLANK
641   COMMENT
642   COMMENT
643   BLANK
644   Store the color in to AX
645   Save only bits 7, 0, and 1 of current AX value
646   Test AL bit 7 for XOR flag
647   If we're doing an XOR color write, jump to line 659
648   BLANK
649   Otherwise this is an overlay so we compare src/dest, Set CH to 0x03
650   Shift the 0x03 in to position to mask the pixel of interest
651   Copy the current video value in to DH
652   Mask out the bits of interest
653   Do XOR of the current color (cyan becomes magenta and vice versa)
654   Rotate modified pixels back to original position
655   Set the remainder of the old byte around the new pixels
656   Move that byte back to its home in video memory
657   Return to caller
658   BLANK
659   Time for a full XOR, set every bit except 7
660   Shift AL left to the pixel of interest
661   XOR the byte
662   Return to caller
663   BLANK
664   BLANK
665   BLANK
666   COMMENT
667   BLANK

DRAW PIXEL FOR COLLISION TEST
668   Draw point with collision detection
669   BLANK
670   COMMENT
671   BLANK
672   Covert our pixel number from game basis in to screen basis (top/bottom)
673   Twos complement for the actual number (currently negative)
674   BLANK
675   COMMENT
676   COMMENT
677   BLANK
678   Store the column in to CX
679   Saves only the first 2 bits in CL (The packed pixel position)
680   We need to find the rotation value, subtract 3...
681   And invert to find the sub-byte pixel rotation value
682   Shift bits in to mask position
683   BLANK
684   COMMENT
685   BLANK
686   Save the target row in DX
687   Halve row value since we only care about half the screen (one scanline)
688   Size of row in DX
689   Multiply the row size by the row number to find row offset
690   Add the pixel offset to the row offset
691   Then add the carry flag from add in to DX
692   Shift DX right -- the least significant bit drops off in to the CF
693   Rotate AX right, this picks up the CF from DX from the last shift in to
      MSB. The LSB moves to CF, but that doesn't matter.
694   Do the shifting again for DX -- clobbers the LSB in CF from AX rotation
695   Rotate AX one last time and we should have the byte we're interested in
      
696   Store the target byte in to SI
697   BLANK
698   COMMENT
699   BLANK
700   Check the first bit to determine row value (hence memory segment)
701   If the even row, jump to line 703 and skip odd raster offset
702   It's odd, so add the raster offset to the source
703   Skip label
704   Move the base video display segment in to AX (temp)
705   Move the video memroy segment in to ES
706   Add the offset to the source
707   BLANK
708   COMMENT
709   BLANK
710   Set the initial mask in to CH (00000011)
711   Shift the bit mask in to proper position depending on pixel within byte
712   Mask the byte in memory with the mask to save the pixel in CH
713   BLANK
714   COMMENT
715   COMMENT
716   BLANK
717   Move the color bit in to AX
718   Mask bits 0, 1, and 7 
719   Check bit 7 for operation, color or XOR
720   It's XOR so jump to 727
721   BLANK
722   Otherwise, we're doing a pixel color copy
723   Rotate the pixels in AL by the shift amount
724   Set the target pixels
725   Jump to return on 735
726   BLANK
727   XOR write - set AL to all 1s, except bit 7
728   Rotate AL based on target pixel position
729   XOR the colors
730   BLANK
731   COMMENT
732   COMMENT
733   COMMENT
734   BLANK
735   Cleanup label
736   Move the XOR'd colors back to base byte position
737   Move the result in to AL
738   Clear AH
739   Return (color result in AL)
740   BLANK
741   BLANK
742   COMMENT
743   BLANK
744   COMMENT
745   COMMENT
746   COMMENT
747   BLANK

SET UP VARIABLES AND REGISTERS FOR DRAWING
748   Prepare for draw by setting the call environment
749   Check if the symbol is only 1 pixel tall
750   If it's more than 1 pixel, jump down to line 757
751   Check if the symbol width is more than 1 pixel
752   If it is more than 1, jump down to 757
753   If symbol is just a single pixel, then there's no prep work
754   BLANK
755   COMMENT
756   BLANK
757   Label for setup for pre-draw
758   Change position from game basis to screen basis (0 is top, not bottom)
759   Twos-complement the subtractions
760   BLANK
761   COMMENT
762   COMMENT
763   BLANK
764   Move the memory offset (pixel position) in to CX
765   Mask the sub-byte pixel position (first two bytes)
766   Shift CL left once to count the rotation required, used after return
767   BLANK
768   COMMENT
769   COMMENT
770   BLANK
771   Move the symbol width from object memory in to AX (pixels)
772   Convert from pixels to bytes...left shift once
773   ...and again since 1 byte = 4 pixels in CGA
774   BLANK
775   Move the pixel column in to DX
776   Convert from column to bytes with one shift...
777   and another shift...just like before
778   Subtract the entire screen width, now we're at negative byte difference
779   Twos-completement for the positive value of screen bytes remaining
780   BLANK
781   Preserve symbol width in AL
782   Subtract the screen bytes remaining (DL) from bytes required (AH)
783   If there is no concern about symbol space, jump to line 785
784   Otherwise, we need to track the remaining screen space as we draw
785   Time to consider vertical boundaries...
786   BLANK
787   COMMENT
788   COMMENT
789   BLANK
790   Store symbol dimensions on the stack
791   Pull the symbol height in to AX
792   Pull the screen ehight in DX
793   Subtract screen height from symbol position to get available space
794   Compare symbol height with availble space
795   If there is no space crunch, jump to line 797
796   Store the availble lines in DX
797   Label for next step -- vram location
798   Push height available on to the stack
799   BLANK
800   COMMENT
801   BLANK
802   Store the row start in AX
803   Halve row value since we only care about half the screen (one scanline)
804   Move the screen width in to DX (320)
805   Multiply DX by the row value to find the memory offset
806   Offset the pixel value in to the row
807   Add back the carry flag
808   We need the byte index, right shift DX, LSB to CF
809   Rotate AX to being CF from DX in to MSB
810   Shift DX right once more, again using CF to hold LSB
811   Final rotate of AX, brings in the CF from DX. AX is the byte index
812   BLANK
813   COMMENT
814   COMMENT
815   BLANK
816   Total line width in to DX
817   Odd line memory offset in to BX
818   Save DX in to adjrast
819   Subtract the offset from the screen width (now a negative value to change
      scanlines)
820   BLANK
821   COMMENT
822   COMMENT
823   BLANK
824   Checks if the symbol is on an off row
825   Not an odd row? no need to offset, so jump to line 828
826   Otherwise, add the offset to the first start byte
827   Swap the offset values for the eventual return
828   Label for final step
829   BLANK
830   COMMENT
831   BLANK
832   Saves the video ram segment in to SI...
833   Then moves it to the extra segment
834   Saves the symbol pointer in in to the source address
835   Saves the video ram in to destination address
836   Adds the offset in to video ram
837   Restore the previous stack frame
838   Restores the line count stored on line 798 in to 837
839   BLANK
840   Restores the segment size as stored on line 790 in to AX
841   Return to caller
842   BLANK
843   BLANK
844   COMMENT
845   COMMENT
846   COMMENT
847   COMMENT
848   BLANK

COLLISION TEST
849   Detect collisions label
850   Store AX
851   Move the desired byte value in to AL
852   Looks up the index in AL in to the translation table. Result back in AL
853   Compare the look up result with the actual memory value
854   Sets the comparison to the return value (0 = no collision)
855   Restore AX from line 850
856   Return to caller
857   BLANK
858   BLANK
859   COMMENT
860   COMMENT
861   COMMENT
862   COMMENT
863   BLANK

ADJUST COLOR FOR OTHER PLAYER/TEAM
864   Adjusts the color depending on object's team color (base is player)
865   Store AX
866   Store the inital byte in AL
867   Look up the byte in the translation table (result in AL)
868   Mask result with color mask (set from cmsktab on line 898)
869   Either nothing to mask here or it's wrong and all is masked (blue->red)
870   Restore AX from line 865
871   Return to caller
872   BLANK
873   COMMENT
874   COMMENT
875   COMMENT
876   COMMENT

TRANSLATION TABLE FOR BYTE VALUE TO PIXEL COLOR (COLLISION CHECK)
877   Defines translation table for collisions - bytes 0-15
878   Defines translation table for collisions - bytes 16-31
879   Defines translation table for collisions - bytes 32-47
880   Defines translation table for collisions - bytes 48-63
881   Defines translation table for collisions - bytes 64-79
882   Defines translation table for collisions - bytes 80-95
883   Defines translation table for collisions - bytes 96-111
884   Defines translation table for collisions - bytes 112-127
885   Defines translation table for collisions - bytes 128-143
886   Defines translation table for collisions - bytes 144-159
887   Defines translation table for collisions - bytes 160-175
888   Defines translation table for collisions - bytes 176-191
889   Defines translation table for collisions - bytes 192-207
890   Defines translation table for collisions - bytes 208-223
891   Defines translation table for collisions - bytes 224-239
892   Defines translation table for collisions - bytes 240-255
893   BLANK
894   COMMENT
895   COMMENT
896   COMMENT
897   BLANK

COLOR XOR MASK
898   The color mask table for each player color, only color 2 is different
899   BLANK
900   BLANK
901   BLANK
902   COMMENT
903   COMMENT
904   COMMENT
905   COMMENT
906   COMMENT
907   COMMENT
908   COMMENT
909   BLANK
910   BLANK
911   Defines get_type procedure to retrieve screen modes
912   Sets AH to 15, which prepares interrupt 10 to retrieve the screen mode
913   Invoke interrupt 10, which saves the screen mode in to AL
914   Clears AH so that only the screen mode remains in AX
915   Return from procedure
916   BLANK
917   BLANK
918   Defines set_type procedure to set screen modes
919   Store the previous frame
920   Create the new frame
921   Get argument 1 in to AL - the desired screen mode
922   Clear AH, which prepares the processor for interrupt 10 to set screen
923   Invokes interrupt 10, setting the screen mode to the value in AL
924   Restore the previous stack frame
925   Return from procedure
926   BLANK
927   BLANK
928   BLANK
929   COMMENT
930   COMMENT
931   COMMENT
932   COMMENT
933   COMMENT
934   COMMENT
935   COMMENT
936   BLANK
937   BLANK
938   Define setvdisp to set the active display to video RAM
939   Store the screen segment (0xB800) in dispseg
940   Start at the beginning of the segment
941   Set the odd raster lines to the odd raster offset 
942   Return to caller
943   BLANK
944   BLANK
945   Define setadisp to set the active display to the aux buffer
946   Set the current data segment in to dispset (this needs prior setup)
947   Set the offset 0x1000 bytes before the auxdisp buffer (offset to base)
948   Set the odd raster offset halfway in to the aux buffer
949   Return to caller
950   BLANK
951   BLANK
952   BLANK
953   BLANK
954   Begin the data area for this assembly unit
955   BLANK
956   Allocate an uninitialized word (2 bytes) for the restor line offset
957   Allocate an unitialized byte for the collision mask
958   Allocate an unitialized byte for the return come
959   Allocate a byte SCR_WDTH times (so 320 or 640 if high res) ground data
960   Allocate a word for the display segment
961   Allocate a word for the offset in the segment
962   Allocate a word for the odd line offset
963   BLANK
964   Import the left side of the screen position
965   Import the right side of the screen position
966   Import the shifting amount
967   Import the aux buffer
968   Import the ground height map
969   Import the display init flag
970   Import the forced redraw flag
971   Import the top of the object list
972   Import the top of the deleted list
973   Import the splattered ox draw flag
974   Import the splattered ox state flag
975   BLANK
976   End of assmbly
977   EOF