diff options
Diffstat (limited to 'v2.0/source/SORT.ASM')
| -rw-r--r-- | v2.0/source/SORT.ASM | 420 |
1 files changed, 420 insertions, 0 deletions
diff --git a/v2.0/source/SORT.ASM b/v2.0/source/SORT.ASM new file mode 100644 index 0000000..a4f39e8 --- /dev/null +++ b/v2.0/source/SORT.ASM | |||
| @@ -0,0 +1,420 @@ | |||
| 1 | TITLE SORT FILTER FOR MS-DOS | ||
| 2 | ; | ||
| 3 | ; Sort /R /+n | ||
| 4 | ; /R -> reverse sort | ||
| 5 | ; /+n -> sort on column n | ||
| 6 | ; | ||
| 7 | ; Written by: Chris Peters | ||
| 8 | ; | ||
| 9 | ; Modification History: | ||
| 10 | ; 3-18-83 MZ Fix CR-LF at end of buffer | ||
| 11 | ; Fix small file sorting | ||
| 12 | ; Fix CR-LF line termination bug | ||
| 13 | ; Comment the Damn source | ||
| 14 | ; | ||
| 15 | FALSE EQU 0 | ||
| 16 | TRUE EQU NOT FALSE | ||
| 17 | |||
| 18 | ;NOTE: "internat" must be false if KANJI version | ||
| 19 | internat equ true | ||
| 20 | ;NOTE: see above | ||
| 21 | |||
| 22 | .xlist | ||
| 23 | .xcref | ||
| 24 | INCLUDE DOSSYM.ASM | ||
| 25 | .cref | ||
| 26 | .list | ||
| 27 | |||
| 28 | sys MACRO name ; system call macro | ||
| 29 | MOV AH,name | ||
| 30 | INT 21h | ||
| 31 | ENDM | ||
| 32 | save MACRO reglist ; push those registers | ||
| 33 | IRP reg,<reglist> | ||
| 34 | PUSH reg | ||
| 35 | ENDM | ||
| 36 | ENDM | ||
| 37 | restore MACRO reglist ; pop those registers | ||
| 38 | IRP reg,<reglist> | ||
| 39 | POP reg | ||
| 40 | ENDM | ||
| 41 | ENDM | ||
| 42 | |||
| 43 | MAXREC EQU 256 ; MAXIMUM NUL RECORD SIZE | ||
| 44 | |||
| 45 | SPACE EQU 0 ; Offset zero in the allocated block | ||
| 46 | BUFFER EQU MAXREC ; Offset MAXREC in the allocated block | ||
| 47 | |||
| 48 | SUBTTL Segments used in load order | ||
| 49 | |||
| 50 | |||
| 51 | CODE SEGMENT | ||
| 52 | CODE ENDS | ||
| 53 | |||
| 54 | CONST SEGMENT PUBLIC BYTE | ||
| 55 | CONST ENDS | ||
| 56 | |||
| 57 | CSTACK SEGMENT STACK | ||
| 58 | DB 128 DUP (0) ; initial stack to be clear | ||
| 59 | CSTACK ENDS | ||
| 60 | |||
| 61 | DG GROUP CODE,CONST,CSTACK | ||
| 62 | |||
| 63 | CODE SEGMENT | ||
| 64 | ASSUME CS:DG,DS:NOTHING,ES:NOTHING,SS:CSTACK | ||
| 65 | |||
| 66 | COLUMN DW 0 ; COLUMN TO USE FOR KEY + 1 | ||
| 67 | SWITCH DB '/' | ||
| 68 | |||
| 69 | SORT: | ||
| 70 | ; | ||
| 71 | ; check for proper version number of system | ||
| 72 | ; | ||
| 73 | sys GET_VERSION | ||
| 74 | XCHG AH,AL ; Turn it around to AH.AL | ||
| 75 | CMP AX,200H ; Version 2.00 only | ||
| 76 | JAE OKDOS ; Success | ||
| 77 | MOV DX,OFFSET DG:BADVER ; Get error message | ||
| 78 | PUSH CS ; Get DS addressability | ||
| 79 | POP DS | ||
| 80 | sys STD_CON_STRING_OUTPUT ; Send to STDOUT | ||
| 81 | PUSH ES ; long segment | ||
| 82 | PUSH COLUMN ; offset zero | ||
| 83 | LONG_RET PROC FAR | ||
| 84 | RET ; long return to OS | ||
| 85 | LONG_RET ENDP | ||
| 86 | ; | ||
| 87 | ; get proper switch character | ||
| 88 | ; | ||
| 89 | OKDOS: | ||
| 90 | MOV AL,0 ; Get current switch character | ||
| 91 | sys CHAR_OPER | ||
| 92 | MOV SWITCH,DL | ||
| 93 | ; | ||
| 94 | ; parse command line | ||
| 95 | ; | ||
| 96 | MOV SI,80H ; pointer to command line | ||
| 97 | CLD ; go left to right | ||
| 98 | XOR CX,CX | ||
| 99 | LODSB | ||
| 100 | MOV CL,AL ; CX = length of command line | ||
| 101 | SWITCH_LOOP: | ||
| 102 | CALL GET_CHAR ; get a character | ||
| 103 | CMP AL,SWITCH ; beginning of switch? | ||
| 104 | JNZ SWITCH_LOOP ; No, get next character | ||
| 105 | CALL GET_CHAR ; get 1st char of switch | ||
| 106 | CMP AL,'+' ; Column to sort? | ||
| 107 | JZ SWITCH_NUMBER ; Yes, parse a number | ||
| 108 | OR AL,20h ; convert to lower case | ||
| 109 | CMP AL,'r' ; Reverse sort? | ||
| 110 | JNZ SWITCH_LOOP ; No, get next switch | ||
| 111 | MOV CS:CODE_PATCH,72h ; sleaze JAE into JB | ||
| 112 | JMP SWITCH_LOOP ; get next switch | ||
| 113 | SWITCH_NUMBER: | ||
| 114 | MOV COLUMN,0 ; start off at 0 | ||
| 115 | SWITCH_NEXT_NUMBER: | ||
| 116 | CALL GET_CHAR ; get supposed digit | ||
| 117 | SUB AL,'0' ; convert to number | ||
| 118 | JB SWITCH_LOOP ; less than '0' | ||
| 119 | CMP AL,9 ; is it a valid digit? | ||
| 120 | JA SWITCH_LOOP ; nope, get next switch | ||
| 121 | CBW ; make it a full word | ||
| 122 | MOV BX,AX ; save byte away | ||
| 123 | MOV AX,10 ; decimal number system | ||
| 124 | MUL COLUMN ; take previous result | ||
| 125 | ADD AX,BX ; add in low order digit | ||
| 126 | MOV COLUMN,AX ; save away value | ||
| 127 | JMP SWITCH_NEXT_NUMBER ; get next character | ||
| 128 | GET_CHAR: | ||
| 129 | JCXZ END_GET ; End of line | ||
| 130 | DEC CX ; dec char count | ||
| 131 | LODSB ; get the character | ||
| 132 | RET ; return | ||
| 133 | END_GET: | ||
| 134 | POP AX ; nuke return on stack | ||
| 135 | ; | ||
| 136 | ; set up column for proper sort offset | ||
| 137 | ; | ||
| 138 | END_SWITCH: | ||
| 139 | ADD COLUMN,2 | ||
| 140 | CMP COLUMN,2 | ||
| 141 | JZ GOT_COL | ||
| 142 | DEC COLUMN | ||
| 143 | |||
| 144 | ; | ||
| 145 | ; Get sorting area, no more than 64K | ||
| 146 | ; | ||
| 147 | GOT_COL: | ||
| 148 | MOV BX,1000H ; 64K worth of paragraphs | ||
| 149 | GET_MEM: | ||
| 150 | sys ALLOC ; allocate them from somewhere | ||
| 151 | JNC GOT_MEM ; if error, BX has amount free, try to get it | ||
| 152 | OR BX,BX ; but, is BX = 0? | ||
| 153 | JNZ GET_MEM ; nope, try to allocate it | ||
| 154 | JMP SIZERR ; complain | ||
| 155 | |||
| 156 | GOT_MEM: | ||
| 157 | MOV DS,AX ; Point DS to buffer | ||
| 158 | MOV ES,AX ; and point ES to buffer | ||
| 159 | MOV CL,4 ; 2^4 bytes per paragraph | ||
| 160 | SHL BX,CL ; Find out how many bytes we have | ||
| 161 | |||
| 162 | ; | ||
| 163 | ; clear out temporary record area | ||
| 164 | ; | ||
| 165 | MOV CX,MAXREC/2 ; Size of temporary buffer (words) | ||
| 166 | MOV AX,' ' ; Character to fill with | ||
| 167 | MOV DI,SPACE ; Beginning of temp buffer | ||
| 168 | REP STOSW ; Blam. | ||
| 169 | ; | ||
| 170 | ; read in file from standard input | ||
| 171 | ; | ||
| 172 | MOV DX,BUFFER + 2 ; DX = place to begin reading | ||
| 173 | MOV CX,BX ; CX is the max number to read | ||
| 174 | SUB CX,MAXREC + 2 ; remember offset of temp buffer | ||
| 175 | SORTL: | ||
| 176 | XOR BX,BX ; Standard input | ||
| 177 | sys READ ; Read it in | ||
| 178 | ADD DX,AX ; Bump pointer by count read | ||
| 179 | SUB CX,AX ; subtract from remaining the count read | ||
| 180 | JZ SIZERR ; if buffer is full then error | ||
| 181 | OR AX,AX ; no chars read -> end of file | ||
| 182 | JNZ SORTL ; there were chars read. go read again | ||
| 183 | JMP SHORT SIZOK ; trim last ^Z terminated record | ||
| 184 | SIZERR: | ||
| 185 | MOV SI,OFFSET DG:ERRMSG ; not enough memory error | ||
| 186 | ERROR_EXIT: | ||
| 187 | PUSH CS ; DS addressability | ||
| 188 | POP DS | ||
| 189 | LODSW ; get length | ||
| 190 | MOV CX,AX ; put into appropriate register | ||
| 191 | MOV DX,SI ; get output destination | ||
| 192 | MOV BX,2 ; output to standard error | ||
| 193 | sys WRITE ; and write it out | ||
| 194 | MOV AL,1 ; return an error code | ||
| 195 | sys EXIT | ||
| 196 | |||
| 197 | ; | ||
| 198 | ; Look for a ^Z. Terminate buffer at 1st ^Z. | ||
| 199 | ; | ||
| 200 | SIZOK: | ||
| 201 | MOV BX,DX ; save end pointer | ||
| 202 | MOV CX,DX ; get pointer to end of text | ||
| 203 | SUB CX,BUFFER+2 ; dif in pointers is count | ||
| 204 | MOV AL,1AH ; char is ^Z | ||
| 205 | MOV DI,BUFFER+2 ; point to beginning of text | ||
| 206 | REPNZ SCASB ; find one | ||
| 207 | JNZ NoBack ; nope, try to find CRLF | ||
| 208 | DEC BX ; pretend that we didn't see ^Z | ||
| 209 | NoBack: | ||
| 210 | SUB BX,CX ; sub from endpointer the number left | ||
| 211 | SUB BX,2 ; Hope for a CR LF at end | ||
| 212 | CMP WORD PTR [BX],0A0Dh ; Was there one there? | ||
| 213 | JZ GOTEND ; yep, here is the end | ||
| 214 | ADD BX,2 ; nope, bump back to SCASB spot | ||
| 215 | CMP BYTE PTR [BX],AL ; Was there ^Z there? | ||
| 216 | JZ GOTEND ; yep, chop it | ||
| 217 | INC BX ; Nope, skip last char | ||
| 218 | GOTEND: | ||
| 219 | MOV BP,BX ; BP = filesize-2(CRLF)+temp buffer+2 | ||
| 220 | MOV WORD PTR DS:[BP],0 ; 0 at end of the file | ||
| 221 | ; | ||
| 222 | ; We now turn the entire buffer into a linked list of chains by | ||
| 223 | ; replacing CRLFs with the length of the following line (with 2 for CRLF) | ||
| 224 | ; | ||
| 225 | MOV BX,BUFFER ; pointer to line head (length) | ||
| 226 | MOV DI,BUFFER+2 ; pointer to line text | ||
| 227 | REPLACE_LOOP: | ||
| 228 | MOV AL,13 ; char to look for is CR | ||
| 229 | MOV CX,BP ; count = end pointer | ||
| 230 | SUB CX,DI ; chop off start point to get length | ||
| 231 | INC CX ; add 1??? | ||
| 232 | REPLACE_SCAN: | ||
| 233 | REPNZ SCASB ; look for CR | ||
| 234 | JNZ REPLACE_SKIP ; count exhausted | ||
| 235 | CMP BYTE PTR [DI],10 ; LF there? | ||
| 236 | JNZ REPLACE_SCAN ; nope, continue scanning | ||
| 237 | REPLACE_SKIP: | ||
| 238 | MOV AX,DI ; AX to point after CR | ||
| 239 | DEC AX ; AX to point to CR | ||
| 240 | save <AX> ; save pointer | ||
| 241 | SUB AX,BX ; AX is length of line found | ||
| 242 | MOV [BX],AX ; stuff it in previous link | ||
| 243 | restore <BX> ; get pointer to next | ||
| 244 | INC DI ; skip LF??? | ||
| 245 | JCXZ END_REPLACE_LOOP ; no more to scan -> go sort | ||
| 246 | JMP REPLACE_LOOP ; look for next | ||
| 247 | |||
| 248 | END_REPLACE_LOOP: | ||
| 249 | MOV WORD PTR [BX],0 ; terminate file with nul | ||
| 250 | LEA BP,[BX+2] ; remember the null line at end | ||
| 251 | MOV DI,BUFFER ; DI is start of unsorted section | ||
| 252 | |||
| 253 | ; | ||
| 254 | ; begin sort. Outer loop steps over all unsorted lines | ||
| 255 | ; | ||
| 256 | OUTER_SORT_LOOP: | ||
| 257 | MOV BX,DI ; BX is start of unsorted section | ||
| 258 | MOV SI,BX ; SI is scanning place link | ||
| 259 | CMP WORD PTR [BX],0 ; are we at the end of the buffer? | ||
| 260 | JNZ INNER_SORT_LOOP ; No, do inner process | ||
| 261 | JMP END_OUTER_SORT_LOOP ; yes, go dump out | ||
| 262 | |||
| 263 | ; | ||
| 264 | ; BX points to best guy found so far. We scan through the sorted section | ||
| 265 | ; to find an appropriate insertion point | ||
| 266 | ; | ||
| 267 | INNER_SORT_LOOP: | ||
| 268 | ADD SI,[SI] ; link to next fellow | ||
| 269 | MOV AX,[SI] ; get length of comparison guy | ||
| 270 | OR AX,AX ; test for end of buffer | ||
| 271 | JZ END_INNER_SORT_LOOP ; if zero then figure out insertion | ||
| 272 | save <SI,DI> ; save SI,DI | ||
| 273 | MOV DI,BX ; DI = pointer to tester link | ||
| 274 | SUB AX,COLUMN ; adjust length for column | ||
| 275 | JA AXOK ; more chars in tester than column? | ||
| 276 | MOV SI,SPACE ; point SI to blank area | ||
| 277 | MOV AX,MAXREC ; make AX be max length | ||
| 278 | AXOK: | ||
| 279 | MOV DX,[DI] ; get length of best guy | ||
| 280 | SUB DX,COLUMN ; adjust length for column | ||
| 281 | JA DXOK ; there are more chars after column | ||
| 282 | MOV DI,SPACE ; point air to a space | ||
| 283 | MOV DX,MAXREC ; really big record | ||
| 284 | DXOK: | ||
| 285 | MOV CX,AX ; AX is shortest record | ||
| 286 | CMP AX,DX ; perhaps DX is shorter | ||
| 287 | JB SMALL ; nope, leace CX alone | ||
| 288 | MOV CX,DX ; DX is shorter, put length in CX | ||
| 289 | SMALL: | ||
| 290 | ADD DI,COLUMN ; offset into record | ||
| 291 | ADD SI,COLUMN ; offset into other record | ||
| 292 | if not internat | ||
| 293 | REPZ CMPSB ; compare every one | ||
| 294 | endif | ||
| 295 | if internat | ||
| 296 | push bx | ||
| 297 | push ax | ||
| 298 | mov bx,offset dg:table | ||
| 299 | tloop: lodsb | ||
| 300 | xlat byte ptr cs:[bx] | ||
| 301 | mov ah,al | ||
| 302 | mov al,es:[di] | ||
| 303 | inc di | ||
| 304 | xlat byte ptr cs:[bx] | ||
| 305 | cmp ah,al | ||
| 306 | loopz tloop | ||
| 307 | pop ax | ||
| 308 | pop bx | ||
| 309 | endif | ||
| 310 | restore <DI,SI> ; get head pointers back | ||
| 311 | JNZ TESTED_NOT_EQUAL ; didn't exhaust counter, conditions set | ||
| 312 | CMP AX,DX ; check string lengths | ||
| 313 | TESTED_NOT_EQUAL: | ||
| 314 | ; | ||
| 315 | ; note! jae is patched to a jbe if file is to be sorted in reverse! | ||
| 316 | ; | ||
| 317 | CODE_PATCH LABEL BYTE | ||
| 318 | JAE INNER_SORT_LOOP ; if this one wasn't better then go again | ||
| 319 | MOV BX,SI ; it was better, save header | ||
| 320 | JMP INNER_SORT_LOOP ; and scan again | ||
| 321 | |||
| 322 | END_INNER_SORT_LOOP: | ||
| 323 | MOV SI,BX ; SI is now the best person | ||
| 324 | CMP SI,DI ; check best for current | ||
| 325 | JZ END_INSERT ; best equals current, all done | ||
| 326 | |||
| 327 | ; | ||
| 328 | ; SI points to best line found so far | ||
| 329 | ; DI points to a place to insert this line | ||
| 330 | ; DI is guaranteed to be < SI | ||
| 331 | ; make room for line at destination | ||
| 332 | ; | ||
| 333 | MOV DX,[SI] ; get length of line | ||
| 334 | save <SI,DI> ; save positions of people | ||
| 335 | STD ; go right to left | ||
| 336 | MOV CX,BP ; get end of file pointer | ||
| 337 | SUB CX,DI ; get length from destination to end | ||
| 338 | MOV SI,BP ; start from end | ||
| 339 | DEC SI ; SI points to end of file | ||
| 340 | MOV DI,SI ; destination is end of file | ||
| 341 | ADD DI,DX ; DI points to new end of file | ||
| 342 | REP MOVSB ; blam. Move every one up | ||
| 343 | CLD ; back left to right | ||
| 344 | restore <DI,SI> ; get old source and destination | ||
| 345 | ; | ||
| 346 | ; MOVE NEW LINE INTO PLACE | ||
| 347 | ; | ||
| 348 | save <DI> ; save destination | ||
| 349 | ADD SI,DX ; adjust for previous movement | ||
| 350 | save <SI> ; save this value | ||
| 351 | MOV CX,DX ; get number to move | ||
| 352 | REP MOVSB ; blam. move the new line in | ||
| 353 | restore <SI,DI> ; get back destination and new source | ||
| 354 | ; | ||
| 355 | ; DELETE LINE FROM OLD PLACE | ||
| 356 | ; | ||
| 357 | save <DI> ; save destination | ||
| 358 | MOV CX,BP ; pointer to end | ||
| 359 | ADD CX,DX ; remember bump | ||
| 360 | SUB CX,SI ; get count of bytes to move | ||
| 361 | INC CX ; turn it into a word | ||
| 362 | SHR CX,1 ; or a count of words | ||
| 363 | MOV DI,SI ; new destination of move | ||
| 364 | ADD SI,DX ; offset of block | ||
| 365 | REP MOVSW ; blam, squeeze out the space | ||
| 366 | restore <DI> ; get back original destination | ||
| 367 | MOV WORD PTR DS:[BP-2],0 ; remake the end of file mark | ||
| 368 | |||
| 369 | END_INSERT: | ||
| 370 | ADD DI,[DI] ; link to next guy | ||
| 371 | JMP OUTER_SORT_LOOP ; and continue | ||
| 372 | ; | ||
| 373 | ; PUT BACK IN THE CR-LF | ||
| 374 | ; | ||
| 375 | END_OUTER_SORT_LOOP: | ||
| 376 | MOV DI,BUFFER ; start at beginning (where else) | ||
| 377 | MOV CX,[DI] ; count of butes | ||
| 378 | |||
| 379 | INSERT_LOOP: | ||
| 380 | ADD DI,CX ; point to next length | ||
| 381 | MOV CX,[DI] ; get length | ||
| 382 | MOV WORD PTR [DI],0A0DH ; replace length with CRLF | ||
| 383 | CMP CX,0 ; check for end of file | ||
| 384 | JNZ INSERT_LOOP ; nope, try again | ||
| 385 | |||
| 386 | WRITE_FILE: | ||
| 387 | MOV DX,BUFFER+2 ; get starting point | ||
| 388 | MOV CX,BP ; pointer to end of buffer | ||
| 389 | SUB CX,DX ; dif in pointers is number of bytes | ||
| 390 | MOV BX,1 ; to standard output | ||
| 391 | sys WRITE ; write 'em out | ||
| 392 | JC BADWRT ; some bizarre error -> flag it | ||
| 393 | CMP AX,CX ; did we write what was expected? | ||
| 394 | JZ WRTOK ; yes, say bye bye | ||
| 395 | BADWRT: | ||
| 396 | MOV SI,OFFSET dg:ERRMSG2 ; strange write error | ||
| 397 | JMP ERROR_EXIT ; bye bye | ||
| 398 | WRTOK: | ||
| 399 | XOR AL,AL ; perfect return (by convention) | ||
| 400 | sys EXIT ; bye! | ||
| 401 | |||
| 402 | CODE ENDS | ||
| 403 | |||
| 404 | CONST SEGMENT PUBLIC BYTE | ||
| 405 | EXTRN BADVER:BYTE,ERRMSG:BYTE,ERRMSG2:BYTE | ||
| 406 | if internat | ||
| 407 | extrn table:byte | ||
| 408 | endif | ||
| 409 | CONST ENDS | ||
| 410 | |||
| 411 | SUBTTL Initialized Data | ||
| 412 | PAGE | ||
| 413 | CSTACK SEGMENT STACK | ||
| 414 | DB 96 dup (0) | ||
| 415 | CSTACK ENDS | ||
| 416 | |||
| 417 | END SORT | ||
| 418 | |||
| 419 | |||
| 420 | \ No newline at end of file | ||