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

SWUTIL.ASM provides various utility functions in 16-bit x86 assembly for DOS.
Functions include flushing keyboard, memory copying, set cursor positions, and
keyboard/joystick i/o, and a few others. 

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

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


NOTE ON ASSEMBLY IN SOPWITH
The original intended assembler for Sopwith code is Microsoft Macro Assembler
(MASM), probably version 2 or 3. This is from the days of segmentation so you'll
see segment:offset accesses to support near addresses. Know your DOS i/o ports
and interrupts (Ask Ralf Brown). The good news is that this old enough that
we can work with 20-bit addresses without considering DOS memory extenders (EMS/XMS,
etc).


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    COMMENT
26    COMMENT
27    COMMENT
28    BLANK
29    BLANK
30    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.
31    Includes macros provided with MASM
32    BLANK
33    Creates a local code segment, linker will combine upcoming code with other
      code
34    BLANK
35    BLANK
36    Imports @AB variable. @AB == 4
37    Imports game header definitions in assembly (see SW.HA)
38    BLANK

PUBLIC FUNCTION DECLARATIONS
These functions are callable from within C functions after linking
39    Function swbreak - new interrupt handler for CTRL-BREAK (interrupt 0x1B)
40    Function swgetc - get character from the keyboard buffer (BIOS or int)
41    Function swgetjoy - get input from joystick
42    Function swputc - outputs a character to the screen (interrupt 0x10)
43    Function swflush - clears the keyboard buffer
44    Function swkeyint - new interrupt handler for the keyboard
45    Function swposcur - set console cursor position
46    Function swcolour - set current console draw color
47    Function swshfprt - new interrupt handler for print screen (int 0x05)
48    Function swprint - wrapper for print screen interrupt
49    Function swsetblk - sets a memory block to input value
50    Function swtick - new interrupt handler for timer interrupt (Int 0x1C)
51    Function movexy - Updates XY position of object in memory
52    Function setdxdy - Updates XY movtion vectors of object in memory
53    Function soundmul - Performs a 16-bit unsigned multiplication chain
54    Function sounddiv - Performs a 16-bit unsigned division chain
55    Function dsseg - return data segment address
56    Function csseg - returns code segment address
57    BLANK

EXTERNAL FUNCTIONS
58    Imports the C tick routine (SWDISP.C)
59    Imports the ctrl-break C function (SWINIT.C)
60    Imports the history function (SWHIST.ASM)
61    Imports the swsound function (SWSOUND.C)
62    Imports the sound function (SWSOUND.C)
63    BLANK
64    BLANK
65    BLANK
66    COMMENT
67    BLANK

CTRL-BREAK INTERRUPT HANDLER (CUSTOM)
68    Define swbreak
69    Save the data segment register (we'll manually reference this shortly)
70    Save the base register
71    Save the base pointer
72    BLANK
73    Move stack pointer to the base pointer - create a new stack frame
74    Move the saved data segment in to the base register
75    Move the old data segment in to the current data segment (new context)
76    BLANK
77    Set the (now near) control-break flag in the application. Now Sopwith
      can see that control-break was pushed within the game's running context by checking
      this flag. This was the reason for the override
78    BLANK
79    Restore saved base pointer
80    Restore saved base register
81    Restore saved data segment (probably not necessary -- good practice)
82    BLANK
83    Undo both frames that were constructed in this routine
84    Return from interrupt (pop flags in addition to IP and CS)
85    BLANK
86    BLANK
87    COMMENT
88    BLANK

KEYBOARD INTERRUPT HANDLER (CUSTOM)
89    Declare the keyboard interrupt function
90    Enable interrupts (possibly disabled when entering this handler)
91    BLANK
92    Stores the current stack frame
93    Stores the current data segment (can't assume it points to Sopwith DS)
94    Creates a new stack frame
95    Dereference the saved base pointer
96    Ensure that the data segment and stack frame share segments (we know CS
      = DS already)
97    Now we can reference program variables properly, mark keyboard interrupt
98    BLANK
99    Check keyboard type flag
100   If check wasn't equal, then we're on an IBM keyboard, skip to line 106
101   We're not on an IBM keyboard, restore data segment to interrupt handler
102   Restore base pointer
103   Roll back stack
104   Return from interrupt
105   BLANK
106   Store all the registers we're about to use: start with AX
107   Store BX
108   Store CX
109   Store DX
110   Store ES
111   Store SI
112   Store DI
113   BLANK
114   Set the extra segment to the game segment (DS is still in game segment)
115   Clear the pause flag
116   BLANK
117   Check if the system flag matches DOS
118   If so, then we used the DOS port to get the scan code, jump to line 122
119   If not, we've already executed int 16 and the code is in AL. Move to BL
120   Go to line 131 to handle the code.
121   BLANK
122   Read value at port 0x60 in to AL
123   Store it in BL
124   Read the controller port at 0x61 in to AL
125   Store status in AH
126   Set bit 7 of AL
127   Output AL to the controller at 0x61 in order to reset interrupt state
128   Undo the bit 7 asserstion
129   Send previous status back to keyboard controller at 0x71 from AL
130   BLANK
131   Check the scan code result with the print screen scan code
132   If not print screen, jump to line 136
133   If print screen, invoke interrupt 0x05
134   Jump to 195
135   BLANK
136   Check of the scan code result against pause key code
137   If not paused then jump to line 161
138   Otherwise, asset pause variable
139   Check if this is DOS
140   If not DOS, jump to line 143 (no need to reset interrupt)
141   Set 0x20 in AL
142   Write 0x20 to port 0x20 to signal End of Interrupt to the 8259 PIC chip
143   Since we're now paused, check if sound is on.
144   If sound is off then jump to line 157
145   Clear AX to 0
146   Push AX on to the stack
147   Push AX on to the stack again (now we have two 0 arguments on the stack)
148   Call sound to NULL out the desired sound
149   Remove the 0's from the stack
150   Call swsound to execute the new (empty) sound
151   Set the sound flag to 0, for off
152   Check if game is paused..
153   If paused, go back to check on line 152. This is a busy-wait while pause.
      A subsequent keyboard interrupt will change the result of this check in another
      interrupt handler frame
154   Set the sound variable to on
155   Jump to line 161
156   BLANK
157   Sound isn't on so test for pause (same as line 152)
158   Jump back a line until we're not paused (more interruptable busy-wait)
159   End keyboard check by jumping line 202
160   BLANK
161   Check if the break key was pushed
162   If not, jump to line 166
163   If so, invoke interrupt 0x1B, which is another custom interrupt
164   BLANK
165   BLANK
166   We might have a game key, so pass the scantable length in to C
167   Put the scan code to check in AL
168   Remove break
169   Load the scantable address in to DI
170   Move scan table to the source (DI to SI)
171   Make sure we're scanning forward
172   Search for a match between AL and the scan table
173   If no match was found, then the key isn't used, jump to exit on line 195
174   BLANK
175   We've moved past the match by 1 so decrement DI
176   Then subtract the base table address to leave the offset in DI
177   Shift DI by 1 for offsetting into the masks
178   Load the mask table in to SI
179   Add DI to SI to find the address of the mask
180   Move the key mask in to AX
181   Disable interrupts
182   BLANK
183   Check if the key was just released (bit 7)
184   If so, jump to line 192
185   It wasn't released so check if it was already pressed from last time
186   Invert AX to prepare to unset bit
187   If we don't need to unset the next key (still down), jump to line 189
188   Logical AND the inverted keymask and the nextkey flags to unset
189   It was released, so reset bit in current key flags
190   Jump to the end
191   BLANK
192   The key was pressed so set it down
193   And set it next time
194   BLANK
195   Check if we're running under DOS (for IRQ acknowledge)
196   No DOS? Jump to 202
197   BLANK
198   Disable interrupts
199   Prepare to send 0x20 by putting it in AL
200   Output 0x20 to port 0x20 to acknowledge interrupts in PIC
201   BLANK
202   We're done, all keymasks set, Restore DI
203   Restore SI
204   Restore ES
205   Restore DX
206   Restore CX
207   Restore BX
208   Restore AX
209   Restore DS
210   Restore BP (old stack frame)
211   Remove all the stack work we've done
212   Return from interrupt
213   BLANK
214   COMMENT
215   BLANK
216   BLANK
217   BLANK
218   COMMENT
219   COMMENT
220   COMMENT
221   COMMENT
222   COMMENT
223   COMMENT
224   BLANK

GET CHARACTER FROM THE KEYBOARD (MAIN MENU FUNCTION)
225   Define function to get current character
226   Check if we're in play
227   If we're in play then jump to line 232
228   If in play then perform the BIOS keyboard routine at line 306
229   Then post that result to the history logger in SWHIST.ASM
230   End fucntion if not in play
231   BLANK
232   Check if an IBM keyboard
233   Not an IBM keyboard, jump to line 243
234   IBM keyboard so disable interrupts (race condition on curr/next/prev)
235   Save the nextkey state
236   Move the current state in to AX
237   Make the next state the same as the current state
238   Restore current keyboard state
239   Move it to previous state
240   Enable interrupts
241   Check joystick by jumping to line 172
242   BLANK
243   For non-IBM keyboards, store DI
244   Store SI
245   Call the BIOS read procedure
246   Check if there's input
247   If no input, we're finished, jump to utility key checks on line 260
248   BLANK
249   There's input, so make sure ES is DS by moving DS to SI
250   Then move SI to ES
251   Load the lower case key table in to DI
252   Call get character mask on line 286
253   If there is a match, handle it on line 260
254   Otherwise, load the upper case mask
255   Check that character mask
256   If there's a hit, process it on line 260
257   BLANK
258   No valid key was found, clear AX and fall through
259   BLANK
260   Push result on the stack
261   Check if break was pressed
262   Check if there was a break
263   If no, jump to line 266
264   There was a break so move the break mask in to AX
265   BLANK
266   We need the matching key mask so pop in to SI
267   OR the match and the possible break
268   Restore SI
269   Restore DI
270   BLANK
271   Check if we have a joystick
272   No joystick, we're done - goto line 281
273   There's a joystick, so save keyboard
274   Propogate next joystick to AX
275   Then to previous
276   Then move current result to AX
277   Push current to next
278   Restore the keyboard
279   Overlay keyboard and joystick to unify game code handling
280   BLANK
281   Log the input
282   Return to caller (game) with all input registers updated
283   BLANK
284   BLANK
285   BLANK
286   Define getcm to retrieve scan masks
287   Need to mirror DS and ES so move DS to SI
288   Then SI to ES
289   Set CX to the number of entries in the scan table
290   DI already contains the table address so save it to SI
291   Make sure we're scanning forward through memory
292   AX already contains the target code, so scan the whole mask now
293   If there was no hit, then we're done so jump to line 302
294   BLANK
295   We found something, but we're 1 past the hit so decrement SI
296   Subtract the base address to leave the offset
297   Shift DI left by 1 
298   Load the scan masks in to SI
299   Then skip the offset amount in to the masks
300   Move that key mask in to AX
301   Clear SI
302   Return to caller
303   BLANK
304   BLANK
305   BLANK
306   Define procedure to invoke BIOS keyboard read
307   Set AH to 0x01
308   Invoke interrupt 0x16 to check if keyboard buffer has an entry
309   FLAGS is non-zero if there is a character so jump to line 312
310   Otherwise, there is no character so clear out AX
311   Return to caller
312   BIOS check found a key so..
313   Set AH to 0x00
314   Invoke interrupt 0x16 to put the key code in to AL
315   Return to caller
316   BLANK
317   BLANK
318   BLANK
319   BLANK
320   COMMENT
321   COMMENT
322   COMMENT
323   BLANK

GET JOYSTICK INPUT
324   Set variable for the high value threshold
325   Set variable for the low value threshold
326   Set maximum variable
327   BLANK
328   Define swgetjoy to return joystick input during io checks
329   Check if we're using a joystick
330   If not, return from this function
331   BLANK
332   We're using a joystick so prepare to check player 1 joystick port
333   Call procedure to get joystick zero in to AX
334   Save result from player 1 joystick port
335   Prepare player 2 joystick port
336   Get input from player 2 joystick port in to AX
337   BLANK
338   Load joystick mask address in to BX but offset 4 words in
339   Check if we're low
340   If not, jump to line 343 to check high
341   If we're low, then move back 3 words in the mask
342   Jump to player 1 result check on line 346
343   Compare player 2 result with high 
344   If it is high, then check player 2 on line 350
345   Move forward 3 words in the mask
346   Compare player 2 to low
347   If we're greater than low then jump to 350
348   Move back 1 word in the mask address
349   Jump to mask button check
350   Check player 2 for high
351   If it's not high then jump to mask button check
352   Move foward 1 word in to mask
353   BLANK
354   Load the mask value directly in BX
355   BLANK
356   Move 0x201 in to DX (0x201 is the game port)
357   Read from the game port in to AL
358   Check if button 1 was pushed
359   If not check butt on 2 on line 361
360   If yes, then we have to shoot so assert K_SHOT on joy mask in BX
361   Check for button 2
362   If no, then check joystick changes on line 365
363   If yes, then we prepare for bombs by asseting K_BOMB on to mask in BX
364   BLANK
365   Move the joystick mask in to current joystick
366   Propogate to the next state (so we can track changes)
367   Move to AX
368   Invert AX so we can easily detect/make changes
369   Turn off bits previously on in AX
370   Invert again
371   Undo asserted bits for next check
372   BLANK
373   Return from joystick get
374   BLANK
375   BLANK
376   Define getjoy to read joystick state
377   Store BX
378   Store CX
379   Store DX
380   Disable interrupts (race on keyboard)
381   BLANK
382   Move joystick number in to CX (0 or 1 for player 1 or 2)
383   Move 1 in to CH
384   Shift CH by player number (so now we have 0010 or 0001 in CH)
385   BLANK
386   Set the game port address
387   Request joystick strobe in to AL
388   Check if there's a hit 
389   If yes
390   BLANK
391   Assert JOYMAX on AX
392   Jump to return on line 423
393   BLANK
394   Disable keyboard flag
395   Set AL to 00 (PIT mode to latch count)
396   Output to the PIT 8253 chip
397   Read byte 1 in to AL
398   Store it in BL
399   Read byte 2 in to AL
400   Store it in BH
401   BLANK
402   Strobe controller 1
403   BLANK
404   Read from last status
405   Check if it's the same
406   If not, redo until change, jump to line 404
407   BLANK
408   Set AL 00 to set read mode
409   Output to PIT
410   Read in byte 1 to AL
411   Move to AH
412   Read in byte 2 to AH
413   Swap AL and AH (we're already storing other result in BX)
414   BLANK
415   Check for keyboard interrupt...
416   If no interrupt, jump to 420
417   There's an interrupt so average results
418   Jump to end on line 423
419   BLANK
420   Get the time difference in to BX
421   Move it to AX for the final result
422   BLANK
423   We're done, enable interrupts
424   Restore DX
425   Restore CX
426   Restore BX
427   Return
428   BLANK
429   BLANK
430   COMMENT
431   COMMENT
432   COMMENT
433   COMMENT
434   BLANK

FLUSH KEYBOARD BUFFER
435   Declare swflush function to clear the keyboard buffer
436   Moves imported inplay variable to AX. AX is now 1 if game in progress
437   ANDs the ibm keyboard flag with AX. AX is 1 if ibm keyboard in play
438   Jump to end if AX == 1 because keys need processing not flushing
439   BLANK
440   Set the high nibble in AX to 1. This is a signal for interrupt 16
441   Invoke interrupt 16. With AH = 1, AX should be non-zero (and hence FLAGS
      non-zero) if there is a character available in the keyboard buffer
442   If character is available, jump to line 447. Otherwise, buffer is already
      empty so fall through to end procedure
443   BLANK
444   Clear AX register
445   Return from swflush
446   BLANK
447   Jump label for flushing keyboard buffer
448   Set AH to zero, another signal for interrupt 16.
449   Invokes interrupt 16 - AH is zero and character is already available so
      AH now contains that scancode. 
450   Jump back to empty buffer test on line 440 and repeat this cycle until
      keyboard buffer is empty.
451   BLANK
452   BLANK
453   BLANK
454   BLANK
455   COMMENT
456   COMMENT
457   COMMENT
458   COMMENT
459   BLANK

OUTPUTS A CHARACTER TO THE CONSOLE
460   Declare function swputc
461   Store old frame pointer
462   Create a new stack frame base
463   BLANK
464   Move argument 1 (a character) in to accumulator
465   Check if the character is a TAB
466   If it's NOT tab then skip to line 480, otherwise it is a tab....
467   Store register CX on the stack (about to clobber with int 0x10)
468   Store register DX on the stack (also about to clobber with int 0x10)
469   Move 3 in to AH as a signal for interrupt 0x10
470   Clear the BX register
471   Invoke interrupt 10, DH now holds cursor row and DL cursor column
472   Add 8 characters to cursor column
473   Mask top 5 bits of DX (rounds down to nearest 8)
474   Move 2 in to AH
475   Invoke int 10 with 2 in AH sets cursor position to DH = row, DL = column
476   Restore DX
477   Restore CX
478   Tab handled, jump to end of procedure on line 492
479   BLANK
480   Moves save text color in to BL (BL is foreground pixel color)
481   Clears BH, normally identifies the text page. We want 0
482   Sets AH to 0x0E, to write text
483   Invoke interrupt 10, writing char in AL to cursor pos, AL was set in line
      464
484   BLANK
485   Checks if we're in hi-res mode
486   If not hi-res, jump to end on line 492, cant print in hi-res
487   Check if character is a control character (not printable)
488   If control, skip to end on line 492
489   Set AH to 0x0E for write text and AL to 0x20 for blank space
490   Invoke interrupt 0x10 to write blank space
491   BLANK
492   Restore the original stack frame
493   Return from swputc
494   BLANK
495   BLANK
496   BLANK
497   COMMENT
498   COMMENT
499   BLANK

GET CONSOLE CURSOR POSITION
500   Define swposcur to move text cursor to x and y already on the stack
501   Save the current stack frame
502   Make a new stack frame
503   BLANK
504   Get the y argument in to DH
505   Get the x argument in to DL
506   Clear out the BH register
507   BLANK
508   Check if we're in high res mode
509   IF not, then jump down to line 511
510   If yes, then DH is twice as wide so shift left once
511   Prepare to set cursor position based on DX, move 0x02 in to AH
512   Invoke interrupt 0x10
513   BLANK
514   Restore previous stack frame
515   Return to caller
516   BLANK
517   BLANK
518   BLANK
519   BLANK
520 
521   BLANK

SET THE TEXT COLOR
522   Define procedure swcolor
523   Store the base pointer
524   Create a new stack frame
525   Move argument 1 in to AL (should be a color)
526   Move AL in to memory variable for color
527   Return previous frame
528   Return new color
529   BLANK
530   BLANK
531   COMMENT
532   BLANK

PRINT SCREEN INTERRUPT HANDLER (CUSTOM)
533   Define the swshfprt function to set up for print screen
534   Store DS
535   Store the current stack frame
536   Store BX
537   BLANK
538   Create a new stack frame
539   Pull the previous data context (this is an interrupt)
540   Reload DS from last frame
541   BLANK
542   Set print screen variable
543   Set BX to the address of the print instruction
544   Move that addres to the printip function pointer
545   Get the code segment of the print context
546   Move that to the printcs function pointer
547   BLANK
548   Restore BX
549   Return frame
550   Restore data segment
551   Smash stack
552   Return from interrupt
553   BLANK
554   COMMENT
555   BLANK
556   Define the print screen
557   Store the flags
558   Call the function pointer for print screen
559   Return to caller
560   BLANK
561   BLANK
562   COMMENT
563   BLANK
564   BLANK
565   BLANK

TIMER INTERRUPT HANDLER (CUSTOM)
566   Define the replacement tick handler from the clock interrupt
567   Store everything, starting with DS
568   Store ES
569   Store AX
570   Store BX
571   Store CX
572   Store DX
573   Store SI
574   Store DI
575   Store BP, current stack frame
576   BLANK
577   Make a new frame
578   Grab the 9th argument on the stack (DS)
579   Move that argument in to DS
580   ..and in to ES
581   BLANK
582   Check if we're paused..
583   If paused, then no tick, so jump to line 586
584   There is a tick so call the C function in SWTITLE.C
585   BLANK
586   Restore everything in reverse starting with the frame in BP
587   Restore DI
588   Restore SI
589   Restore DX
590   Restore CX
591   Restore BX
592   Restore AX
593   Restore ES
594   Restore DS
595   BLANK
596   Get rid of the call to this function
597   Return from interrupt
598   BLANK
599   BLANK
600   COMMENT
601   BLANK
602   BLANK
603   BLANK

BLOCK MEMORY SET
604   Define function for swsetblk
605   Save the current base pointer
606   Create a new stack frame
607   Save the previous extra segment
608   Move the target segment from argument 2 of swsetblk call in to AX
609   Move the new target segment in to ES
610   Set the read direction to zero (move forward)
611   Set the target write offset from argument 1 of swsetblk in to DI
612   Move the number of bytes from argument 3 of swsetblk in to CX (counter)
613   Move the desired memory value from argument 4 of swset in to AL
614   Repeatedly store byte value in AL to ES:DI until CX is 0 (rep repeats
      actions with CX--)
615   Restore the save segment
616   Restore the previous stack frame
617   Return set value to caller (this should probably return original CX)
618   BLANK
619   BLANK
620   COMMENT
621   BLANK
622   BLANK
623   BLANK

MOVE OBJECT XY POSITION
624   Set OB variable to 4 (object base offset relative to pointer)
625   Set X to OB+2, which is the ob_x property given an object pointer. Note
      that hardcoded offsets like these are a serious problem for portability...this
      scenario assumes that integers are 2 bytes. This was a good assumption at the
      time for DOS systems, but it's no longer true.
626   SET Y to OB+4, which is the ob_y property (See SW.HA and SEGMENT.H)
627   BLANK
628   BLANK
629   Declare and define movexy, moves game objects to a new xy
630   Save previous stack frame
631   Create new stack frame
632   Save the previous source memory index
633   Save the previous desintation memory index
634   BLANK
635   Moves the object pointer (movexy argument 1) in to DI
636   BLANK
637   Moves object's x position in to AX by offseting OB_X (2 bytes) in to the
      object base address
638   Moves the objects previous x position in to SI
639   Move's the objects delta x in to CX
640   Move the object's last delta x in to DX
641   BLANK
642   Adds the object's previous x to it's previous delta x
643   Adds with carry of the current x with current delta x
644   BLANK
645   Stores the new x position in to our local stack object's current x
646   Stores the new previous x in to the local object's last x
647   Moves argument 1 (pointer to object x position), in to the base register
648   Moves the new x position in to the source object's current x memory location
      
649   BLANK
650   Moves object's y position in to AX by offseting OB_Y (4 bytes) in to the
      object base address
651   Moves the objects previous y position in to SI
652   Move's the objects delta y in to CX
653   Move the object's last delta y in to DX
654   BLANK
655   Adds the object's previous y to it's previous delta y
656   Adds with carry of the current y with current delta y
657   BLANK
658   Stores the new y position in to our local stack object's current y
659   Stores the new previous y in to the local object's last y
660   Moves argument 1 (pointer to object x position), in to the base register
661   Moves the new y position in to the source object's current y memory location
662   BLANK
663   Restores the original DI register
664   Restores the original SI register
665   Restores the original stack frame
666   Returns to caller
667   BLANK
668   BLANK
669   BLANK
670   BLANK

MOVE OBJECT XY VECTOR
671   Set OB variable to 4 (object base offset relative to pointer)
672   Set X to OB+2, which is the ob_x property given an object pointer. Note
      non-portable assumption that integers are 2 bytes
673   SET Y to OB+4, which is the ob_y property (See SW.HA and SEGMENT.H)
674   BLANK
675   BLANK
676   Defines setdxdy function to update object's xy vectors
677   Saves the caller's stack frame
678   Creates a new stack frame
679   BLANK
680   Moves the object's pointer in to DI (passed as argument 1)
681   BLANK
682   Move input delta x to CX (ex if dx=5, CX = 0000 0000 0000 0101)
683   Moves the CH in to AL, AL = 0000 0000
684   Sign extends AX, AX = 0000 0000 0000 0000
685   Save AX in to ob_dx (ob_dx = 0000 0000 0000 0000)
686   Move CL (0000 0101) in to AH. (AX = 0000 0101 0000 0000 = 1,280) 
687   Clear AL (AX = 0000 0101 0000 0000 = 1,280 = original dx * 256)
688   Save AX in to ob_ldx
689   BLANK
690   Move input delta y in to CX
691   Move CH in to AL
692   Sign extend AX
693   Save AX in to ob_dy
694   Move CL in to AH
695   Clear AL
696   Save last dy. We now have dy*256 for use in movexy
697   BLANK
698   Restore previous stack frame
699   Return to caller, object has been updated
700   BLANK
701   BLANK
702   COMMENT
703   COMMENT
704   COMMENT
705   COMMENT
706   BLANK

LONG MULTIPLY (SOUND CALCULATION)
707   Define soundmul to multiple several arguments
708   Save the current frame
709   Create a new stack frame
710   BLANK
711   Grab the multiplicand from the stack
712   Multiply it by the multiplier on the stack
713   Store high order result from DX in to BX
714   BLANK
715   Multiply intermediate result in AX by the next multiplier
716   BLANK
717   Move the original top 16 bits in to AX
718   Move the second result in to BX
719   Remultiplier the second multiplier by the first result
720   BLANK
721   Add BX to AX, answer now lies in AX 
722   BLANK
723   Restore the stack frame
724   Return
725   BLANK
726   BLANK

LONG DIVIDE (SOUND FUNCTION)
727   Define sounddiv to long divide
728   Save the current frame
729   Make a new frame
730   BLANK
731   Get the lower dividend
732   Get the the upper dividend
733   Divide by the divisor, answer is in AX
734   BLANK
735   Restore stack frame
736   Return answer
737   BLANK
738   BLANK
739   COMMENT
740   COMMENT
741   COMMENT
742   COMMENT
743   COMMENT
744   COMMENT
745   COMMENT
746   BLANK
747   BLANK

DSSEG
748   Defines the public function dsseg. Returns the current address of the
      data segment register
749   Set the AX (accumulator) to the value in the DS (data segment) register
750   Return AX to caller
751   BLANK

CSSEG
752   Defines the public function csseg. Returns the current address in the
      code segment register
753   Set the AX (accumulator) to the value in the CS (code segment) register
754   Return AX to caller
755   BLANK
756   BLANK
757   BLANK
758   COMMENT
759   BLANK
760   Creates a local data segment, linker will combine upcoming data in to
      final exe
761   BLANK
762   Makes the ctrlbflag (ctrl-break) flag publicly accessible
763   BLANK
764   Imports the joystick boolean from SWMAIN.C (word size is 2 bytes)
765   Imports the ibmkeybd boolean from SWMAIN.C 
766   Imports the inplay boolean from SWMAIN.C
767   Imports the printflg boolean from from SWMAIN.C
768   Imports the soundflg boolean from SWMAIN.C
769   Imports the hires flag from SWMAIN.C
770   BLANK
771   Stores the ctrlbflag as 2 bytes initialized to 0
772   Stores the text color flag as 1 byte initialized to 3
773   Stores the previous keyboard scancode as 2 bytes initialized to 0
774   Stores the current keyboard scancode as 2 bytes initialized to 0
775   Stores the next keyboard scancode as 2 bytes initialized to 0
776   Stores the previous joystick i/o read as 2 bytes initialized to 0
777   Stores the current joystick i/o read as 2 bytes initialized to 0
778   Stores the next joystick i/o read as 2 bytes initialized to 0
779   Stores the keyboard interrupt flag as 2 bytes initialized to 0
780   Stores the paused flag as 2 bytes initialized to 0
781   BLANK
782   Store 12 bytes of the scan table (These match ST-keyboards, set 1)
783   Stores five 2-byte scanmasks (0x1, 0x2, 0x4, etc)
784   Stores five more 2-byte scanmasks
785   Stores the final two 2-byte scanmasks
786   Stores a lookup table of the 12 lower case symbols matching keys
787   Stores a lookup table of the 12 upper case symbols matching keys
788   Stores five 2-byte joystick input masks
789   Store the final three 2-byte joystick input
790   BLANK
791   Stores the print screen code offset
792   Stores the print screen code segment number
793   BLANK
794   Stores the operating system type flag as a word
795   BLANK
796   Ends assembly code unit
797   EOF