Decoded: Sopwith (1984) by David L. Clark Source file: BMBLIB.C Beginner friendly, line-by-line code walkthrough by MaiZure BMBLIB.C manages command line argument reading and registers. Code quality is lower than most files in this archive. It was clearly written by multiple period over different times with different expectations Original code: http://davidlclark.com/page/sopwith-source-code Original code with line numbers http://www.maizure.org/projects/decoded-sopwith/BMBLIB_linenum.txt 1 COMMENT 2 COMMENT 3 BLANK 4 COMMENT 5 COMMENT 6 COMMENT 7 COMMENT 8 COMMENT 9 COMMENT 10 COMMENT 11 COMMENT 12 COMMENT 13 COMMENT 14 BLANK 15 COMMENT 16 COMMENT 17 COMMENT 18 COMMENT 19 BLANK 20 BLANK 21 Include OS header (dos.h) 22 Include main game header (dos.h) 23 Include C standard library header (string.h) 24 BLANK 25 BLANK 26 Set the system global to DOS 27 BLANK 28 BLANK 29 BLANK 30 BLANK 31 BLANK 32 COMMENT 33 COMMENT 34 COMMENT 35 BLANK 36 BLANK 37 BLANK 38 BLANK GET SOPWITH LOADTIME ARGUMENTS FROM THE CONSOLE This is a classic example of a programmer "gettin' stuff done" hoping that it will work like magic (it does) and that no one will ever look at this code again. Variadic function argument processing 101 in 1984! If this seems complicated, part of the reason is that this function can do a lot more work than is was finally used for in the final build of Sopwith. 39 Declares getflags with 4 arguments 40 Argument 1 is the standard argument count from the command line 41 Argument 2 is the standard argument input vector from the command line 42 Argument 3 is a string that specifies the format of argument 4 43 Argument 4 is a vector of pointers for each command line flag in Sopwith 44 BLOCK START - getflags, processes custom command line arguments 45 Declares pointer to the input arguments and a pointer to an end point 46 Declares three iterators 47 Declares a character pointer to be used within the input string 48 Declares pointer to the command line switch we're interested in 49 Declares pointer to be used within the input string 50 Declares a variable pointer that scans at the argument level 51 Declares a pointers to our variable pointer (yes this is really a ***) 52 Declares a pointer to the character within the format 53 Declare initial return status to 0 (FAIL) 54 Declares a status flag 55 BLANK 56 Points our local argument pointer to the input argument vector 57 Points our iterator to the argument counter 58 Skip the program's name on the command line (always arg0) 59 BLANK 60 BLOCK START - While arguments remain... 61 Point to the first character of the argument 62 If it's not a switch, then we're done..break to next line on line 137 63 BLOCK START - EOL check, check 2nd character for another '-' 64 Increment to the next 65 Reduce the count 66 End loop on EOL 67 BLOCK END - EOL Check 68 BLANK 69 Label for handling a single switch 70 Set the flag variable to the switch reference 71 Point a variable to the beginning of the variadic fields vector 72 BLANK 73 BLOCK START - Loop on the format string 74 Reset offset iterator to 0 75 Check the length of the switch (should always be 1 in this build) 76 BLANK 77 COMMENT 78 BLANK 79 If there is a match or a special character then...(no specials now) 80 We found our desired position in the format string, so break loop 81 BLANK 82 Increment format a string based on determined size 83 Plus one more byte to next start 84 BLOCK END - Loop on the format string 85 BLANK 86 BLOCK START - Unexpected format, no input or end of format string 87 BLOCK START - Find the offset (position) of the end of string 88 Go to the last character... 89 and overwrites end of character with \0 90 BLOCK START - No final character, then we're at the end 91 Set return status to 1 (SUCCESS) 92 Jump to the end of the procedure 93 BLOCK END - No final character 94 BLOCK END - Find offset 95 Something really went off the rails...end process 96 BLOCK END - Unexpected format 97 BLANK 98 Set the switch to the matching format pointer offset 99 Increment the argument pointer as well 100 The pointer's pointer! 101 Still need to resolve the switch 102 BLOCK START - Check for more, Check if we're looking beyond the argument and between switches (we should be if all is well) 103 BLOCK START - Argument count check, if there is more... 104 Advance through the argument list 105 Count down 106 We should now be pointing at next 107 BLOCK END - Argument count check 108 BLOCK END - Check for more 109 BLANK 110 SWITCH - Check switch value (most are deprecated in this build) 111 Case # -- append (not used) 112 Reset the offset to 0 113 BLOCK START - More to read 114 Convert the string value to a long integer (end check) 115 The offset is the difference between beginning and end 116 Set arg pointer to the end (next arg) 117 BLOCK END - More to read 118 BLOCK START - No more to append 119 If we're already looking at the next argument... 120 Move back 121 Add back to count 122 End if 123 Somehow there's a flag without a valid input 124 BLOCK END - No more to append 125 End case for # 126 BLANK 127 Case for *: unused in this version 128 End case for * 129 BLANK 130 Case & - this is the only one used. Asset the flag in memory 131 End case for & 132 END SWITCH 133 BLANK 134 If there is more expected and the pointer is valid go back to line 69 135 Next argument 136 BLOCK END - While arguments remain... 137 BLANK 138 Goto label for end of arugment processing 139 Reset input vector pointer to end (or failure point) of arguments 140 Set argument count to 0 (Attempts to make this reentrant?) 141 Return status, success or fail 142 BLOCK END - getflags, processes custom command line arguments 143 BLANK 144 BLANK INTERFACE FUNCTIONS BETWEEN DOS/STRING AND SOPWITH CODE The following half-dozen functions translate Sopwith code in to respective OS and library functions. These use ANSI C formats so I suspect they were written later for portability. 145 Declares helper function with 2 arguments, a string and a position 146 BLOCK START - index, returns valid chars at position in a string or NULL 147 Declare a local char 148 BLANK 149 Returns the character or NULL 150 BLOCK END - index 151 BLANK 152 BLANK 153 Defines inportb that takes an unsigned int port number 154 BLOCK START - inportb, reads a byte from a port 155 Returns the byte value read from the port as an (intended) 2-byte int 156 BLOCK END - inportb 157 BLANK 158 BLANK 159 Defines movblock with 5 arguments: source offset and segment.. 160 ..Destination offset and segment... 161 ..and the size to move 162 BLOCK START - movblock, moves a block of memory across segments (FAR) 163 Call movedata with the input arguments 164 BLOCK END - movblock 165 BLANK 166 BLANK 167 BLANK 168 Defines movmem with 3 arguments, *source, *destination, and size to move 169 BLOCK START - movmem, moves a block of memory from src to dest (NEAR) 170 Calls memmove to perform the move 171 BLOCK END - movmem 172 BLANK 173 BLANK 174 BLANK 175 Defines outportb with two arguments, a target port and a data byte 176 BLOCK START - outportb, writes input data to the input port 177 Returns success or failure of writing input data to the input port 178 BLOCK END - outportb 179 BLANK 180 BLANK 181 Defines setmem with 3 arguments: mem pointer, size, and value 182 BLOCK START - setmem, sets a block of memory to the input value 183 Calls memset with the same arguments to set memory to the value 184 BLOCK END - setmem 185 BLANK 186 BLANK WRAPPER FOR INVOKING DOS API INTERRUPTS IN C 187 Defines sysint with 3 arguments: int number, input and output reg values 188 BLOCK START - sysint, performs x86/DOS API interrupts 189 Declares a REGS union from DOS.H. Format varies by implementation, but this generally includes DI, SI, BP, FLAGS, BX, CX, DX, and AX 190 Declares a SREGS structure for the six segment registers 191 Declares a return value 192 BLANK 193 Sets the register state for AX based on input value 194 Sets the register state for BX based on input value 195 Sets the register state for CX based on input value 196 Sets the register state for DX based on input value 197 Sets the register state for DS based on input value 198 Invokes the input interrupt number and register values 199 Reads the output register values for AX 200 Reads the output register values for BX 201 Reads the output register values for CX 202 Reads the output register values for DX 203 Reads the output register values for DS 204 Returns the interrupt result (usually AX but depends on int number) 205 BLOCK END - sysint 206 BLANK 207 BLANK WRAPPER FOR INVOKING SOFTWARE INTERRUPTS IN C 208 Defines sysint21 with 2 arguments: input register values and output 209 BLOCK START systint21, invokes DOS software interrupt 210 Declares a REGS union from DOS.H. Format varies, but this generally includes DI, SI, BP, FLAGS, BX, CX, DX, and AX 211 Declares a SREGS structure for the six segment registers 212 Declares a return value variable that usually matches AX 213 BLANK 214 Sets the register state for AX based on input value 215 Sets the register state for BX based on input value 216 Sets the register state for CX based on input value 217 Sets the register state for DX based on input value 218 Sets the register state for DS based on input value 219 Invokes interrupt 21 with features based on AH value in AX 220 Reads the output register values for AX 221 Reads the output register values for BX 222 Reads the output register values for CX 223 Reads the output register values for DX 224 Reads the output register values for DS 225 Returns the interrupt result (usually matches AX depending on call) 226 BLOCK END - sysint21 227 EOF˙