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˙