home *** CD-ROM | disk | FTP | other *** search
/ Windows 95 Shareware (Platinum Edition) / QUEPLAT95.ISO / files / programm / cwsort / cwsort.asm next >
Encoding:
Assembly Source File  |  1995-08-12  |  40.9 KB  |  1,778 lines

  1. COMMENT ^
  2.  
  3. CWSORT.ASM
  4.  
  5. 32-bit assembly language sort utility using CauseWay 386 DOS extender
  6.  
  7. Version:    1.0
  8.  
  9. Usage:        CWSORT <input file> <output file> <options>
  10.  
  11.             <options> may be placed anywhere in the command line and are
  12.             preceded by a '/'.  They are not case sensitive.
  13.             
  14.             Supported options are:
  15.             /+#####    begin at offset from start of sort string, ##### is the
  16.                     offset value, relative 1, maximum of 65535
  17.             /D        remove duplicate fields
  18.             /L#####    fixed length field sort, ##### is the field length,
  19.                     maximum of 65535
  20.             /Q        run in quiet mode
  21.             /R        reverse sort order
  22.             /S<file name>    sort file according to sort order specified in
  23.                             <file name>.
  24.  
  25. Comments:    This code is not optimized except in the bottleneck areas where it
  26.             makes a difference.  Even there, every last cycle hasn't been
  27.             wrung out, although optimization is pretty good.  Be aware that
  28.             many optimizations are written towards Pentium usage and some
  29.             code constructs may be less optimal with a 386 and, to a lesser
  30.             extent, a 486.  In particular, string instructions tend to be
  31.             much less important on a Pentium.
  32.             
  33.             A minimum of higher level directives were used to avoid
  34.             compatibility problems with different assembler versions.
  35.             
  36.             The CWSORT.ASM source code is released to the public domain.
  37.             Copyright restrictions remain in force for the CauseWay DOS
  38.             extender contained in CWSORT.EXE.  However, redistribution of
  39.             CauseWay-extended executable files created by registered owners
  40.             of CauseWay (as Michael Devore is) without royalties or
  41.             licensing fees or restrictions is explicitly allowed by the
  42.             CauseWay software license agreement.
  43.  
  44. Written by:    Michael Devore
  45.  
  46. Authorized contact information for CWSORT and CauseWay questions:
  47. CompuServe:    71540,62
  48. Internet:    71540.62@compuserve.com
  49. Fax:        1-708-717-6373 (N.B. 708 area code is scheduled to change in 1996)
  50. Mail:        Michael Devore
  51.             Devore Software & Consulting
  52.             PO Box 4283
  53.             Naperville, IL  60567-4283
  54.             USA
  55.  
  56. Begin Date:        08/95
  57. Last changed:    08/95
  58.  
  59. END COMMENT ^
  60.  
  61. ;******************************************************************************
  62. ;******************************************************************************
  63.  
  64. .386                ; placed before model directive and segment declarations
  65.                     ; makes all segments 32-bit
  66. .MODEL SMALL
  67. .STACK 400h
  68.  
  69. ; handy equates
  70. BELL    EQU        7
  71. CR        EQU        13
  72. LF        EQU        10
  73. OFF        EQU        0
  74. ON        EQU        1
  75. STDOUT    EQU        1
  76.  
  77. BADEXITCODE = 129
  78.  
  79. .DATA
  80.  
  81. BufferSelector    DW    0    ; selector of input file memory buffer
  82. InputFileHandle    DW    0
  83. LineCount    DD    0    ; count of text lines
  84. OutputFileHandle    DW    0
  85. OutputSelector    DW    0    ; selector of output file memory buffer
  86. PointerArraySize    DD    0    ; current size of dword pointer array
  87. PointerSelector    DW    0    ; selector of pointers to variable length fields
  88. SortOrderFileHandle    DW    0
  89.  
  90. ALIGN 4
  91. FixedLengthSize    DD    0    ; size of fixed length fields
  92. ALIGN 4
  93. SortOffsetValue    DD    0    ; size of sorting offset
  94.  
  95. ExitCode        DB    0
  96. FileCount        DB    0
  97. IsDuplicateOption    DB    0    ; nonzero if remove duplicates option
  98. IsFixedLengthOption    DB    0    ; nonzero if /L##### option
  99. IsSortOffsetOption    DB    0    ; nonzero if /+##### option
  100. IsQuietOption    DB    0    ; nonzero if /Q quiet option specified
  101. IsReverseOption    DB    0    ; nonzero if /R reverse option
  102. IsSortOrderOption    DB    0    ; nonzero if /S<filename> option
  103. ParsingFileNameFlag    DB    0    ; nonzero if parsing file name
  104.  
  105.                 DW    0    ; file name length
  106. InputFileName    DB    81 DUP (0)    ; input file name
  107.                 DW    0    ; file name length
  108. OutputFileName    DB    81 DUP (0)    ; output file name
  109.                 DW    0    ; file name length
  110. SortOrderFileName    DB    81 DUP (0)    ; sort order file name
  111.  
  112. ; build the default sort order table in ASCII order
  113. ALIGN 4
  114. SortOrderTable    LABEL    BYTE
  115. COUNTER    =    0
  116. REPT 256
  117.     DB    COUNTER
  118.     COUNTER=COUNTER+1
  119. ENDM
  120.  
  121. ; address for different sort logic parameters
  122. ALIGN 4
  123. SetOffsetCounterAddress    DD    OFFSET NoOffsetCounterLogic
  124. SortOffsetLogicAddress    DD    OFFSET NoSortOffsetLogic
  125. FixedCounterAddress    DD    OFFSET NoFixedCounterLogic
  126. FixedMoveAddress    DD    OFFSET NoFixedMoveLogic
  127. FixedJumpAddress    DD    OFFSET NoFixedJumpLogic
  128. SortLookupAddress    DD    OFFSET NoSortLookupLogic
  129.  
  130. .DATA?
  131.  
  132. ScratchBuffer    DB    256 DUP (?)
  133.  
  134. InputFileSize    DD    ?
  135.  
  136. ; variables used in shell sort
  137. iValue    DD    ?
  138. vValue    DD    ?
  139. SortOffsetCounter    DD    ?
  140. FixedLengthCounter    DD    ?
  141.  
  142. .CONST
  143.  
  144. CRLFText        DB    CR,LF
  145.  
  146.                 DW    BadOptionTextStop-BadOptionText
  147. BadOptionText    DB    'Invalid option specified: /'
  148. BadOptionTextStop    =    $
  149.  
  150.                 DW    BadSortFileOpenTextStop-BadSortFileOpenText
  151. BadSortFileOpenText        DB    'Cannot open sort order file:  '
  152. BadSortFileOpenTextStop    =    $
  153.  
  154.                 DW    BadSortFileReadTextStop-BadSortFileReadText
  155. BadSortFileReadText        DB    'Error reading sort order file: '
  156. BadSortFileReadTextStop    =    $
  157.  
  158.                 DW    BadInputOpenTextStop-BadInputOpenText
  159. BadInputOpenText    DB    'Cannot open input file: '
  160. BadInputOpenTextStop    =    $
  161.  
  162. BadInputReadText    DB    'Failure to read input file: '
  163.  
  164.                 DW    BadOutputOpenTextStop-BadOutputOpenText
  165. BadOutputOpenText    DB    'Cannot create output file: '
  166. BadOutputOpenTextStop    =    $
  167.  
  168.                 DW    LocatingLinesTextStop-LocatingLinesText
  169. LocatingLinesText    DB    'Locating text lines....'
  170. LocatingLinesTextStop    =    $
  171.  
  172.                 DW    MemoryTextStop-MemoryText
  173. MemoryText        DB    'Not enough available memory.'
  174. MemoryTextStop    =    $
  175.  
  176.                 DW    NoInputTextStop-NoInputText
  177. NoInputText        DB    'No input or output file specified.'
  178. NoInputTextStop    =    $
  179.  
  180.                 DW    NoOutputTextStop-NoOutputText
  181. NoOutputText    DB    'No output file specified.'
  182. NoOutputTextStop    =    $
  183.  
  184.                 DW    ReadingFileTextStop-ReadingFileText
  185. ReadingFileText    DB    'Reading file: '
  186. ReadingFileTextStop    =    $
  187.  
  188.                 DW    SortingFileTextStop-SortingFileText
  189. SortingFileText    DB    'Sorting file....'
  190. SortingFileTextStop    =    $
  191.  
  192.                 DW    WritingFileTextStop-WritingFileText
  193. WritingFileText    DB    'Writing file: '
  194. WritingFileTextStop    =    $
  195.  
  196.                 DW    CreditTextStop-CreditText
  197. CreditText    =    $
  198.     DB    "CWSORT 1.0, Written by Michael Devore, released to public domain.",CR,LF
  199.     DB    CR,LF
  200.     DB    "Usage: CWSORT <input file> <output file> <options>",CR,LF
  201.     DB    CR,LF
  202.     DB    "   <options> may be placed anywhere in the command line and are",CR,LF
  203.     DB    "   preceded by a '/'.  They are not case sensitive.",CR,LF
  204.     DB    CR,LF
  205.     DB    "   Supported options are:",CR,LF
  206.     DB    CR,LF
  207.     DB    "   /+##### begin at offset from start of sort string, ##### is the",CR,LF
  208.     DB    "           offset value, relative 1, maximum of 65535",CR,LF
  209.     DB    "   /D      remove duplicate fields",CR,LF
  210.     DB    "   /L##### fixed length field sort, ##### is the field length,",CR,LF
  211.     DB    "           maximum of 65535",CR,LF
  212.     DB    "   /Q      run in quiet mode",CR,LF
  213.     DB    "   /R      reverse sort order",CR,LF
  214.     DB    "   /S<file name>   sort file according to sort order specified in",CR,LF
  215.     DB    "                   <file name>.",CR,LF
  216. CreditTextStop    =    $
  217.  
  218. .CODE
  219.  
  220. ;******
  221. ; MAIN
  222. ;******
  223. ; main entry point of application
  224.  
  225. main    PROC
  226.     call    Init
  227.     call    GetCommandLine
  228.     jc    mexit
  229.     call    PrepareFiles
  230.     jc    mexit
  231.     call    AllocateInputBuffer
  232.     mov    edx,OFFSET DGROUP:ReadingFileText
  233.     call    DisplayString
  234.     mov    edx,OFFSET DGROUP:InputFileName
  235.     call    DisplayStringCRLF
  236.     call    ReadFileIntoMemory
  237.     jc    mexit
  238.     mov    edx,OFFSET DGROUP:LocatingLinesText
  239.     call    DisplayStringCRLF
  240.     call    SetTextPointers
  241.     mov    edx,OFFSET DGROUP:SortingFileText
  242.     call    DisplayStringCRLF
  243.     call    SortInfo
  244.     call    CheckDuplicateLines
  245.     call    BuildOutputBuffer
  246.     mov    edx,OFFSET DGROUP:WritingFileText
  247.     call    DisplayString
  248.     mov    edx,OFFSET DGROUP:OutputFileName
  249.     call    DisplayStringCRLF
  250.     call    WriteFileToDisk
  251.     jc    mexit
  252.  
  253. mexit:
  254.     call    Terminate    ; no return
  255. main    ENDP
  256.  
  257. ;******
  258. ; INIT
  259. ;******
  260. ; initialization code
  261.  
  262. Init    PROC    NEAR
  263.  
  264. ; point ds and gs to DGROUP as default
  265.     mov    eax,DGROUP
  266.     mov    ds,eax        ; yes, this is stupid, but MOV DS,AX with MASM forces a
  267.                     ; superfluous 66h prefix byte and it's harmless as far
  268.                     ; as TASM is concerned
  269.     mov    gs,eax
  270.     ret
  271. Init    ENDP
  272.  
  273. ;****************
  274. ; GETCOMMANDLINE
  275. ;****************
  276. ; get command line parameters
  277. ; no intelligent option parsing, but not enough options to make it so
  278. ; upon entry es -> protected mode PSP (application entry condition)
  279. ; return carry flag set if bad option or null command line
  280. ;   carry flag reset otherwise
  281.  
  282. GetCommandLine    PROC    NEAR
  283.     mov    esi,81h        ; es:esi -> command line preceded by length
  284.     movzx    ecx,BYTE PTR es:[esi-1]    ; get count of bytes in command line
  285.     cmp    ecx,1
  286.     jbe    nocl
  287.  
  288. clloop:
  289.     or    ecx,ecx        ; no more parameter chars
  290.     je    gcldone
  291.     dec    ecx
  292.     mov    al,es:[esi]    ; get parameter char
  293.     inc    esi
  294.     cmp    al,' '        ; see if whitespace
  295.     jbe    nextparam    ; yes, move to next parameter
  296.     cmp    al,'/'        ; see if option
  297.     je    isoption    ; yes
  298.  
  299. ; see if file name being processed
  300.     cmp    ParsingFileNameFlag,ON
  301.     je    savechar
  302.  
  303. ; start of input or output file name
  304.     mov    edi,OFFSET DGROUP:InputFileName
  305.     inc    FileCount
  306.     mov    ParsingFileNameFlag,ON
  307.     cmp    FileCount,2    ; determine if on input or output file name
  308.     jb    savestart
  309.     ja    clloop        ; both parsed, ignore this extraneous text
  310.  
  311. ; acquiring output file name
  312.     mov    edi,OFFSET DGROUP:OutputFileName
  313.  
  314. savestart:
  315.     mov    ebx,edi        ; save pointer to start of file name for length word
  316.  
  317. ; store file name
  318. savechar:
  319.     mov    ds:[edi],al    ; save file name char
  320.     inc    edi
  321.     inc    WORD PTR [ebx-2]
  322.     jmp    clloop    ; loop for another char
  323.  
  324. ; moving to next parameter
  325. nextparam:
  326.     mov    ParsingFileNameFlag,OFF    ; turn off flag of processing file name
  327.     jmp    clloop
  328.  
  329. gcldone:
  330.     clc                ; flag successful command line processing
  331.     ret
  332.  
  333. ; no command line, display help screen
  334. nocl:
  335.     call DisplayHelp
  336.  
  337. gclfail:
  338.     stc                ; flag failure
  339.     ret
  340.  
  341. ; process option
  342. isoption:
  343.     or    ecx,ecx        ; no more chars
  344.     je    gcldone
  345.     dec    ecx
  346.     mov    al,es:[esi]    ; get option char
  347.     inc    esi
  348.  
  349.     cmp    al,'Q'
  350.     jne    iso2
  351.  
  352. isoq:
  353.     mov    IsQuietOption,ON
  354.     jmp    clloop
  355.  
  356. iso2:
  357.     cmp    al,'q'
  358.     je    isoq
  359.     cmp    al,'R'
  360.     jne    iso3
  361.  
  362. isor:
  363.     mov    IsReverseOption,ON
  364.     jmp    clloop
  365.  
  366. iso3:
  367.     cmp    al,'r'
  368.     je    isor
  369.     cmp    al,'D'
  370.     jne    iso4
  371.  
  372. isod:
  373.     mov    IsDuplicateOption,ON
  374.     jmp    clloop
  375.  
  376. iso4:
  377.     cmp    al,'d'
  378.     je    isod
  379.     cmp    al,'L'
  380.     jne    iso5
  381.  
  382. ; set fixed length fields option and get field length
  383. isol:
  384.     mov    IsFixedLengthOption,ON
  385.     call    GetWordValue
  386.     mov    WORD PTR FixedLengthSize,ax
  387.     jmp    clloop
  388.  
  389. iso5:
  390.     cmp    al,'l'
  391.     je    isol
  392.     cmp    al,'+'
  393.     jne    iso6
  394.  
  395. ; set sorting offset option and get offset value    
  396.     mov    IsSortOffsetOption,ON
  397.     call    GetWordValue
  398.     mov    WORD PTR SortOffsetValue,ax
  399.     jmp    clloop
  400.  
  401. iso6:
  402.     cmp    al,'S'
  403.     jne    iso7
  404.  
  405. ; set sort order option and get sort order file name
  406. isos:
  407.     mov    IsSortOrderOption,ON
  408.     mov    edi,OFFSET DGROUP:SortOrderFileName
  409.  
  410. soloop:
  411.     or    ecx,ecx        ; no more chars
  412.     je    gcldone
  413.     dec    ecx
  414.     mov    al,es:[esi]    ; get file name char
  415.     inc    esi
  416.     cmp    al,' '
  417.     jbe    clloop
  418.     mov    ds:[edi],al
  419.     inc    edi
  420.     inc    WORD PTR SortOrderFileName-2    ; bump file name length
  421.     jmp    soloop
  422.  
  423. iso7:
  424.     cmp    al,'s'
  425.     je    isos
  426.  
  427. ; invalid option
  428.     mov    ScratchBuffer+2,al
  429.     mov    WORD PTR ScratchBuffer,1
  430.     mov    edx,OFFSET DGROUP:BadOptionText
  431.     call    DisplayString
  432.     mov    edx,OFFSET DGROUP:ScratchBuffer+2
  433.     call    DisplayStringCRLF
  434.     mov    ExitCode,BADEXITCODE
  435. BADEXITCODE    = BADEXITCODE+1
  436.  
  437.     jmp    gclfail
  438.  
  439. GetCommandLine    ENDP
  440.  
  441. ;**************
  442. ; GETWORDVALUE
  443. ;**************
  444. ; get an option word value, 1-65535
  445. ; upon entry ecx == remaining count of chars in command line, updated,
  446. ;  es:esi -> command line chars
  447. ; return value in eax, ignore overflow
  448. ; Yes, there are all sorts of tricks to shave code and cycles for these
  449. ;  type of routines dreamed up from countless contests, but I never remember
  450. ;  them when the time comes.  Prime rule of optimization:  unless
  451. ;  doing it for practice or entertainment, cycle-shaving where it isn't
  452. ;  necessary or noticed is a waste of time.
  453.  
  454. GetWordValue    PROC    NEAR
  455.     xor    eax,eax        ; init return value
  456.     mov    edi,10        ; use as constant
  457.  
  458. gwvloop:
  459.     or    ecx,ecx        ; no more chars
  460.     je    gwvdone
  461.     dec    ecx
  462.     mov    bl,es:[esi]    ; get option value
  463.     inc    esi
  464.     cmp    bl,'0'        ; must be numeric
  465.     jb    gwvdone
  466.     cmp    bl,'9'
  467.     ja    gwvdone
  468.     mul    edi            ; shift previous value by 1 digit place (*10)
  469.     and    eax,0ffffffh    ; eliminate possibility of dword overflow problems
  470.     and    bl,0fh        ; strip ASCII value
  471.     movzx    ebx,bl    ; zero high bytes
  472.     add    eax,ebx
  473.     jmp    gwvloop
  474.  
  475. gwvdone:
  476.     cmp    eax,65535    ; check for word overflow
  477.     jb    gwvdone2    ; no problem
  478.     mov    eax,65535
  479.  
  480. gwvdone2:
  481.     or    eax,eax        ; see if nonzero value specified
  482.     jne    gwvret        ; yes
  483.     inc    eax            ; make minimum value of 1
  484.  
  485. gwvret:
  486.     ret
  487. GetWordValue    ENDP
  488.  
  489. ;*************
  490. ; DISPLAYHELP
  491. ;*************
  492. ; display help screen
  493.  
  494. DisplayHelp    PROC    NEAR
  495.     mov    edx,OFFSET DGROUP:CreditText
  496.     call    DisplayString
  497.     ret
  498. DisplayHelp    ENDP
  499.  
  500. ;**************
  501. ; PREPAREFILES
  502. ;**************
  503. ;
  504. ; prepare input and output files, sort order file if necessary
  505. ; check file existence and open
  506. ; return carry flag set if failure,
  507. ;   carry flag reset if success
  508.  
  509. PrepareFiles    PROC    NEAR
  510.     cmp    BYTE PTR InputFileName,0    ; see if any input file specified
  511.     jne    pf2            ; yes
  512.  
  513. ; no input or output file
  514.     mov    edx,OFFSET DGROUP:NoInputText
  515.     call    DisplayStringCRLF
  516.     mov    ExitCode,BADEXITCODE
  517. BADEXITCODE    = BADEXITCODE+1
  518.  
  519.     jmp    pffail
  520.  
  521. pf2:
  522.     cmp    BYTE PTR OutputFileName,0    ; see if output file specified
  523.     jne    pf3            ; yes
  524.  
  525. ; no output file
  526.     mov    edx,OFFSET DGROUP:NoOutputText
  527.     call    DisplayStringCRLF
  528.     mov    ExitCode,BADEXITCODE
  529. BADEXITCODE    = BADEXITCODE+1
  530.  
  531.     jmp    pffail
  532.  
  533. pf3:
  534.     mov    edx,OFFSET DGROUP:InputFileName
  535.     mov    al,40h        ; read-only, deny none access
  536.     call    OpenFile    ; open input file, ax == handle
  537.     jc    pfbadinp
  538.     mov    InputFileHandle,ax
  539.  
  540. ; seek to end of input file to get file size
  541.     mov    bx,ax
  542.     xor    cx,cx
  543.     mov    dx,cx
  544.     mov    ax,4202h    ; move file pointer relative end of file
  545.     int    21h
  546.     mov    WORD PTR InputFileSize,ax    ; save the input file size
  547.     mov    WORD PTR InputFileSize+2,dx
  548.  
  549. ; rewind file
  550.     xor    cx,cx
  551.     mov    dx,cx
  552.     mov    ax,4200h    ; move file pointer relative beginning of file
  553.     int    21h
  554.  
  555.     mov    edx,OFFSET DGROUP:OutputFileName
  556.     mov    al,40h        ; read-only, deny none access
  557.     call    CreateFile    ; create output file, ax == handle
  558.     jc    pfbadout
  559.     mov    OutputFileHandle,ax
  560.  
  561. ; open and process sort order file, if any
  562.     cmp    IsSortOrderOption,ON
  563.     jne    pfret
  564.     call    ProcessSortOrderFile
  565.     jc    pffail        ; error processing sort order file
  566.  
  567. pfret:
  568.     clc                ; flag success
  569.     ret
  570.  
  571. ; error opening input file
  572. pfbadinp:
  573.     mov    ExitCode,BADEXITCODE
  574. BADEXITCODE    = BADEXITCODE+1
  575.     mov    edx,OFFSET DGROUP:BadInputOpenText
  576.  
  577. pfbadshared:
  578.     call    DisplayString
  579.     mov    edx,OFFSET DGROUP:InputFileName
  580.     call    DisplayStringCRLF
  581.  
  582. pffail:
  583.     stc                ; flag failure
  584.     ret
  585.  
  586. ; error creating output file
  587. pfbadout:
  588.     mov    ExitCode,BADEXITCODE
  589. BADEXITCODE    = BADEXITCODE+1
  590.     mov    edx,OFFSET DGROUP:BadOutputOpenText
  591.     jmp    pfbadshared
  592.  
  593. PrepareFiles    ENDP
  594.  
  595. ;**********************
  596. ; PROCESSSORTORDERFILE
  597. ;**********************
  598. ; open and process the sort order file
  599. ; return carry flag set if fail, reset on success
  600.  
  601. ProcessSortOrderFile    PROC    NEAR
  602.     mov    edx,OFFSET DGROUP:SortOrderFileName
  603.     mov    al,40h        ; read-only, deny none access
  604.     call    OpenFile    ; open input file, ax == handle
  605.     jc    psobadopen
  606.     mov    SortOrderFileHandle,ax    ; currently superfluous, but good idea to keep
  607.     mov    bx,ax        ; file handle to bx for reads
  608.  
  609. ; read the sort order file and process
  610. psoreadloop:
  611.     mov    cx,256
  612.     mov    edx,OFFSET DGROUP:ScratchBuffer
  613.     call    ReadFile
  614.     jc    psobadread
  615.     or    ax,ax        ; see if any bytes read
  616.     je    psosuccess    ; not this time
  617.  
  618. psomodloop:
  619.     movzx    edi,BYTE PTR ds:[edx]    ; ebx -> position to modify within table
  620.     mov    cl,ds:[edx+1]    ; get modification value
  621.     mov    ds:[SortOrderTable+edi],cl    ; update sort order value
  622.     add    edx,2        ; move to next position
  623.     sub    ax,2        ; drop count of entries read (word/entry)
  624.     ja    psomodloop    ; not at end entry or odd byte
  625.     jmp psoreadloop    ; loop for another round
  626.  
  627. psosuccess:
  628.     call    CloseFile
  629.     clc                ; flag success
  630.     ret
  631.  
  632. psofail:
  633.     stc                ; flag failure
  634.     ret
  635.  
  636. ; error creating output file
  637. psobadopen:
  638.     mov    ExitCode,BADEXITCODE
  639. BADEXITCODE    = BADEXITCODE+1
  640.     mov    edx,OFFSET DGROUP:BadSortFileOpenText
  641.  
  642. psobadshared:
  643.     call    DisplayString
  644.     mov    edx,OFFSET DGROUP:SortOrderFileName
  645.     call    DisplayStringCRLF
  646.     jmp    psofail
  647.  
  648. ; error reading sort input file
  649. psobadread:
  650.     mov    ExitCode,BADEXITCODE
  651. BADEXITCODE    = BADEXITCODE+1
  652.     mov    edx,OFFSET DGROUP:BadSortFileReadText
  653.     jmp    psobadshared
  654.  
  655. ProcessSortOrderFile    ENDP
  656.  
  657. ;**********
  658. ; OPENFILE
  659. ;**********
  660. ; open file
  661. ; upon entry edx -> filename, al holds open flags
  662. ; return carry set if error, otherwise ax == file handle
  663.  
  664. OpenFile    PROC    NEAR
  665.     mov    ah,3dh        ; open file
  666.     int    21h
  667.     ret
  668. OpenFile    ENDP
  669.  
  670. ;************
  671. ; CREATEFILE
  672. ;************
  673.  
  674. ; create normal file, truncate if exists
  675. ; upon entry edx -> file name
  676. ; return carry set if error, otherwise ax == file handle
  677.  
  678. CreateFile    PROC
  679.     xor    cx,cx            ; normal file attribute
  680.     mov    ah,3ch            ; create or truncate file
  681.     int    21h
  682.     ret
  683. CreateFile    ENDP
  684.  
  685. ;*********************
  686. ; ALLOCATEINPUTBUFFER
  687. ;*********************
  688. ; allocate memory block buffer for input file
  689.  
  690. AllocateInputBuffer    PROC    NEAR
  691.     mov    eax,InputFileSize
  692.     add    eax,2            ; in case need to add final CR/LF to file
  693.     add    eax,9            ; allow slop at end of file for text scanning
  694.     call    GetMemory    ; attempt to allocate sufficient memory to hold file
  695.     mov    BufferSelector,ax    ; save selector
  696.     ret
  697. AllocateInputBuffer    ENDP
  698.  
  699. ;***************
  700. ; DISPLAYSTRING
  701. ;***************
  702. ; display text string
  703. ; upon entry edx -> text to display, word length prepended
  704.  
  705. DisplayString    PROC    NEAR
  706.     cmp    IsQuietOption,ON
  707.     je    dsret        ; no display with quiet option
  708.     mov    cx,WORD PTR ds:[edx-2]
  709.     mov    bx,STDOUT
  710.     mov    ah,40h
  711.     int    21h
  712.  
  713. dsret:
  714.     ret
  715. DisplayString    ENDP
  716.  
  717. ;*******************
  718. ; DISPLAYSTRINGCRLF
  719. ;*******************
  720. ; display text string with CR/LF terminator
  721. ; upon entry edx -> text to display, word length prepended
  722.  
  723. DisplayStringCRLF    PROC    NEAR
  724.     cmp    IsQuietOption,ON
  725.     je    dscrret        ; no display with quiet option
  726.     call    DisplayString
  727.     mov    edx,OFFSET DGROUP:CRLFText
  728.     mov    cx,2
  729.     mov    bx,STDOUT
  730.     mov    ah,40h
  731.     int    21h
  732.  
  733. dscrret:
  734.     ret
  735. DisplayStringCRLF    ENDP
  736.  
  737. ;********************
  738. ; READFILEINTOMEMORY
  739. ;********************
  740. ; read entire input file into memory, if possible
  741. ; return carry flag set if failure,
  742. ;   carry flag reset if success
  743.  
  744. ReadFileIntoMemory    PROC    NEAR
  745.     push    ds        ; save critical register
  746.     xor    edx,edx        ; zero init offset in input buffer
  747.     mov    bx,InputFileHandle
  748.     mov    esi,InputFileSize    ; get bytes to read
  749.     mov    ds,BufferSelector    ; ds:edx -> input file read buffer
  750.  
  751. rffileloop:
  752.     mov    ecx,esi        ; get bytes to read
  753.     cmp    ecx,0fff0h    ; see if past maximum
  754.     jbe    rfread        ; no
  755.     mov    cx,0fff0h    ; set bytes to read to maximum (ecx high word known zero)
  756.  
  757. rfread:
  758.     call    ReadFile
  759.     jc    rfret        ; error during read
  760.     movzx    eax,ax    ; get bytes read this pass
  761.     add    edx,eax        ; update buffer read position
  762.     sub    esi,eax        ; update bytes left to read
  763.     jne    rffileloop    ; more to read
  764.  
  765. ; check for final CR/LF, add if necessary and not fixed length sorting
  766.     cmp    gs:IsFixedLengthOption,ON    ; see if fixed length sorting
  767.     jne    rfvar        ; no
  768.  
  769. ; pad the input sort buffer with spaces for any partial line at
  770. ;  end of file for less than fixed length
  771.     xor    edx,edx
  772.     mov    eax,gs:InputFileSize
  773.     mov    ecx,gs:FixedLengthSize
  774.     div    ecx            ; value known to fit in eax
  775.     or    edx,edx        ; see if any remainder
  776.     je    rf2            ; no
  777.     sub    ecx,edx        ; compute amount of partial line padding
  778.  
  779. ; resize memory allocation for input buffer increase
  780.     push    ecx        ; keep pad size
  781.     mov    eax,gs:InputFileSize
  782.     add    ecx,eax        ; compute original file size+pad size
  783.     add    ecx,9        ; allow slop space at end of file for text scanning
  784.     mov    ax,gs:BufferSelector
  785.     call    ResizeMemory
  786.     pop    ecx            ; restore pad size
  787.  
  788. ; pad the partial line with spaces
  789. ; one-time store isn't worth special Pentium optimizations, just use stos
  790.     mov    edi,gs:InputFileSize
  791.     add    gs:InputFileSize,ecx    ; bump defacto file size for partial padding
  792.     mov    eax,20202020h
  793.     push    ds
  794.     pop    es            ; es -> input buffer
  795.     push    ecx
  796.     shr    ecx,2
  797.     rep    stosd
  798.     pop    ecx
  799.     and    ecx,3
  800.     rep    stosb
  801.  
  802. rf2:
  803.     mov    edx,gs:InputFileSize    ; edx -> end of file
  804.     jmp    scanpad
  805.  
  806. rfvar:
  807.     cmp    WORD PTR ds:[edx-2],CR+(256*LF)
  808.     je    scanpad
  809.     mov    WORD PTR ds:[edx],CR+(256*LF)
  810.     add    edx,2
  811.     add    gs:InputFileSize,2    ; bump length of file by two for CR/LF pair
  812.  
  813. ; pad the input sort buffer with spaces for end of line scanning
  814. ; much easier and faster than constantly doing a special end of file check
  815. ; note that this pad will not be sorted into the output file, it's just
  816. ;  a bumper for reads past end of file
  817. scanpad:
  818.     mov eax,20202020h
  819.     mov    ds:[edx],eax
  820.     mov    ds:[edx+4],eax
  821.     mov    ds:[edx+8],al
  822.  
  823. rfret:
  824.     pop    ds            ; restore critical register
  825.     ret
  826. ReadFileIntoMemory    ENDP
  827.  
  828. ;*****************
  829. ; SETTEXTPOINTERS
  830. ;*****************
  831. ; allocate array of dword pointers to text lines
  832. ; keep pointer to each new line
  833. ; grow the pointer array allocation as necessary
  834.  
  835. SetTextPointers    PROC    NEAR
  836.     cmp    IsFixedLengthOption,OFF    ; see if sorting fixed length files
  837.     je    varline        ; no
  838.     xor    edx,edx        ; compute line pointer entry count
  839.     mov    eax,InputFileSize
  840.     mov    ecx,FixedLengthSize
  841.     div    ecx            ; value known to fit in eax, partial already padded to full
  842.  
  843. stpalloc:
  844.     mov    LineCount,eax
  845.     inc    eax            ; make relative 1
  846.     shl    eax,2        ; allocate dword entry per line
  847.     mov    PointerArraySize,eax
  848.     call    GetMemory
  849.     mov    PointerSelector,ax
  850.  
  851. ; setup line pointer entry values, each a multiple of fixed length size
  852.     mov    es,ax        ; es -> array storage
  853.     mov    edi,4        ; edi -> line pointer array, don't use 0'th element
  854.     xor    eax,eax        ; eax -> current line
  855.     mov    ecx,LineCount
  856.  
  857. stpstoreloop:
  858.     mov    es:[edi],eax    ; update line pointer entry
  859.     add    edi,4        ; move to next entry
  860.     add    eax,FixedLengthSize    ; point to next line
  861.     dec    ecx
  862.     jne    stpstoreloop    ; more lines
  863.     jmp    stpdone
  864.  
  865. varline:
  866.     mov    eax,65536
  867.     mov    PointerArraySize,eax
  868.     call    GetMemory    ; make initial space for pointer array
  869.     mov    PointerSelector,ax
  870.     mov    es,ax        ; es -> array storage
  871.     mov    ds,BufferSelector    ; ds -> input buffer for performance
  872.     mov    esi,4        ; init pointer array element
  873.                     ; don't use [0]'th array element, not used in sort
  874.     xor    edi,edi        ; buffer pointer
  875.     mov    edx,edi        ; buffer pointer adjustment
  876.  
  877. ; es -> array
  878. ; ds -> input buffer
  879. ; gs -> DGROUP
  880. addline:
  881.     add    edi,edx        ; compute adjusted start of line
  882.     cmp    edi,gs:InputFileSize    ; check if at or past end of file
  883.     jae    stpdone        ; no more lines
  884.     inc    gs:LineCount    ; bump count of lines
  885.  
  886. ; check if pointer array needs expanding
  887.     cmp    esi,gs:PointerArraySize
  888.     jae    growarray    ; next array element is past pointer array boundary
  889.  
  890. storearray:
  891.     mov    es:[esi],edi
  892.     add    esi,4        ; move to next array element
  893.     mov    edx,2        ; preset adjustment for alignment check in case of match
  894.     mov    ecx,gs:InputFileSize    ; get absolute upper boundary
  895.     dec    ecx            ; adjust for second byte
  896.     mov    eax,ecx
  897.     sub    eax,9        ; get main loop upper boundary
  898.     jnc    alignchk
  899.     xor    eax,eax        ; upper boundary below zero, set to zero
  900.  
  901. ; realign EDI to dword boundary if necessary
  902. alignchk:
  903.     cmp    edi,ecx        ; see if at absolute upper boundary
  904.     jae    stpdone        ; yes
  905.     cmp    edi,eax        ; see if at main loop upper boundary
  906.     jae    slowchk        ; yes, check remaining bytes via slow compare
  907.     test    edi,3    ; see if at dword alignment
  908.     je    scanloop    ; already aligned
  909.  
  910. slowchk:
  911.     cmp    WORD PTR ds:[edi],CR+(256*LF)    ; see if at CR/LF pair
  912.     je    addline        ; yes
  913.     inc    edi
  914.     jmp    alignchk
  915.  
  916. ; scan for CR/LF pairs in the buffer.
  917. ; match checks can be made in advance of previous locations IF they are
  918. ;  within one byte of the previous location, otherwise previous locations
  919. ;  may match first.
  920. ; this testing order looks somewhat strange, but improves Pentium
  921. ;  performance by pairing instructions for the UV pipelines with a minimum
  922. ;  of stalls.
  923. scanloop:
  924.     mov    eax,ds:[edi]
  925.     mov    ebx,ds:[edi+4]
  926.  
  927.     cmp    ax,CR+(256*LF)
  928.     je    match1
  929.     shr    eax,16
  930.     mov    ecx,ds:[edi+1]
  931.     mov    edx,ds:[edi+5]
  932.  
  933.     cmp    ax,CR+(256*LF)
  934.     je    match3
  935.     cmp    cx,CR+(256*LF)
  936.     je    match2
  937.     shr    ecx,16
  938.  
  939.     cmp    bx,CR+(256*LF)
  940.     je    match5
  941.     shr    ebx,16
  942.     cmp    cx,CR+(256*LF)
  943.     je    match4
  944.     cmp    dx,CR+(256*LF)
  945.     je    match6
  946.     shr    edx,16
  947.  
  948.     cmp    bx,CR+(256*LF)
  949.     je    match7
  950.     cmp    dx,CR+(256*LF)
  951.     je    match8
  952.  
  953.     add    edi,8
  954.     jmp    scanloop
  955.  
  956. ; matches on CR/LF pair
  957. ; location determines adjustment to next line
  958. match1:
  959.     mov    edx,2
  960.     jmp    addline 
  961.  
  962. match2:
  963.     mov    edx,3
  964.     jmp    addline 
  965.  
  966. match3:
  967.     mov    edx,4
  968.     jmp    addline 
  969.  
  970. match4:
  971.     mov    edx,5
  972.     jmp    addline 
  973.  
  974. match5:
  975.     mov    edx,6
  976.     jmp    addline 
  977.  
  978. match6:
  979.     mov    edx,7
  980.     jmp    addline 
  981.  
  982. match7:
  983.     mov    edx,8
  984.     jmp    addline 
  985.  
  986. match8:
  987.     mov    edx,9
  988.     jmp    addline 
  989.  
  990. stpdone:
  991.     push    gs        ; restore ds -> DGROUP
  992.     pop    ds
  993.     ret
  994.  
  995. ; grow the pointer array by 64K
  996. growarray:
  997.     mov    ax,es        ; input buffer selector to grow
  998.     mov    ecx,gs:PointerArraySize
  999.     add    ecx,65536
  1000.     mov    gs:PointerArraySize,ecx
  1001.     call    ResizeMemory
  1002.     jmp    storearray
  1003.  
  1004. SetTextPointers    ENDP
  1005.  
  1006. ;**********
  1007. ; SORTINFO
  1008. ;**********
  1009. ; sort information, using either an optimized simple sort if no sort parameters
  1010. ;  are specified or a slower, parameter-checking sort.  The performance
  1011. ;  decrease compared to the simple sort is quite significant, so don't
  1012. ;  use the fixed length, offset, or sort order settings if possible.
  1013.  
  1014. SortInfo    PROC    NEAR
  1015.     mov    al,IsFixedLengthOption
  1016.     or    al,IsSortOffsetOption
  1017.     or    al,IsSortOrderOption    ; see if any special sort parameters in use
  1018.     jne    sspspec        ; use special sort
  1019.  
  1020.     call    SimpleShellSort
  1021.     ret
  1022.  
  1023. sspspec:
  1024.     call    ParameterShellSort
  1025.     ret
  1026. SortInfo    ENDP
  1027.  
  1028. ;*****************
  1029. ; SIMPLESHELLSORT
  1030. ;*****************
  1031. ; sort the information in the input file, defaults to simple and fast
  1032. ; the sort is a shell sort and follows this C code:
  1033. COMMENT ^
  1034. shellsort(int a[],int N)
  1035. {
  1036.     int i,j,h,v;
  1037.  
  1038.     for(h=1;h<=N/9;h=3*h+1)
  1039.         ;
  1040.     for(;h>0;h/=3){
  1041.         for(i=h+1;i<=N;i++){
  1042.             v=a[i];
  1043.             j=i;
  1044.             while(j>h && a[j-h]>v){
  1045.                 a[j]=a[j-h];
  1046.                 j-=h;
  1047.             }
  1048.             a[j]=v;
  1049.         }
  1050.     }
  1051. }
  1052. END COMMENT ^
  1053.  
  1054. SimpleShellSort    PROC    NEAR
  1055.     mov    eax,LineCount
  1056.     cmp    eax,1
  1057.     jbe    siret        ; 1 line is sorted by definition
  1058.  
  1059.     mov    fs,PointerSelector
  1060.     mov    ds,BufferSelector
  1061.  
  1062. ; sort the symbol names
  1063. ; fs -> pointer array
  1064. ; ds -> input buffer
  1065. ; gs -> DGROUP
  1066.     xor    ebx,ebx
  1067.     mov    edx,ebx
  1068.     mov    bl,9
  1069.     div    ebx
  1070.     mov    ecx,eax            ; ecx == quotient, N/9
  1071.  
  1072. ; for (h=1;h<=N/9;h=3*h+1);
  1073.     mov    eax,ebx            ; eax==9
  1074.     mov    al,1            ; eax==1, h
  1075.     mov    bl,3            ; ebx==3
  1076.     xor    edx,edx            ; zero for multiply loop
  1077.  
  1078. sethloop:
  1079.     cmp    eax,ecx            ; h<=N/9
  1080.     ja    sort2
  1081.     mul    ebx                ; 3*h, assume 32-bit result (pretty safe bet)
  1082.     inc    eax                ; 3*h+1
  1083.     jmp    sethloop
  1084.  
  1085. ; ebx will play role of j, edx will play role of h
  1086. sort2:
  1087.     mov    edx,eax            ; edx == h
  1088.  
  1089. ; for (;h>0;...
  1090. hloop:
  1091.     or    edx,edx            ; h>0
  1092.     je    sortend
  1093.  
  1094. ; for(i=h+1...
  1095.     mov    eax,edx
  1096.     inc    eax
  1097.     mov    gs:iValue,eax
  1098.  
  1099. ; for(...;i<=N;...){
  1100. iloop:
  1101.     mov    eax,gs:iValue
  1102.     cmp    eax,gs:LineCount
  1103.     ja    nexth
  1104.  
  1105.     mov    ecx,fs:[4*eax]
  1106.     mov    gs:vValue,ecx    ; v=a[i]
  1107.     mov    ebx,eax            ; j=i
  1108.  
  1109. ; while(j>h && a[j-h]>v){
  1110. whileloop:
  1111.     cmp    ebx,edx            ; j>h
  1112.     jbe    whilefail
  1113.  
  1114.     mov    eax,ebx
  1115.     sub    eax,edx            ; eax==j-h
  1116.     xor    ecx,ecx            ; zero high bytes of register for following repe
  1117.  
  1118.     mov    esi,fs:[4*eax]    ; ds:esi -> a[j-h]
  1119.     mov    edi,gs:vValue    ; ds:edi==v
  1120.  
  1121. comploop:
  1122.     mov    ax,ds:[esi]
  1123.     mov    cx,ds:[edi]
  1124.     cmp    ax,CR+(256*LF)
  1125.     je    whilefail    ; first <= second, a[j-h]<v
  1126.     cmp    cx,CR+(256*LF)    ; check if first >= second
  1127.     je    dochange    ; first > second, modify a[j]
  1128.     cmp    al,cl
  1129.     jb    whilefail    ; first < second, a[j-h]<v
  1130.     jne    dochange    ; first > second, modify a[j]
  1131.     inc    esi
  1132.     inc    edi
  1133.     jmp    comploop
  1134.  
  1135. dochange:
  1136.     mov    eax,ebx
  1137.     sub    eax,edx            ; eax==j-h
  1138.     mov    eax,fs:[4*eax]    ; eax==a[j-h]
  1139.     mov    fs:[4*ebx],eax    ; a[j]=a[j-h]
  1140.     sub    ebx,edx            ; j-=h
  1141.     jmp    whileloop
  1142.  
  1143. whilefail:
  1144.     mov    eax,gs:vValue
  1145.     mov    fs:[4*ebx],eax    ; a[j]=v
  1146.  
  1147. ; for(...;i++){
  1148.     inc    gs:iValue
  1149.     jmp    iloop
  1150.  
  1151. ; for (...;h/=3){
  1152. nexth:
  1153.     mov    eax,edx
  1154.     xor    edx,edx
  1155.     mov    ecx,edx
  1156.     mov    cl,3
  1157.     div    ecx
  1158.     mov    edx,eax
  1159.     jmp    hloop
  1160.  
  1161. sortend:
  1162.     mov    ax,DGROUP
  1163.     mov    ds,ax
  1164.  
  1165. siret:
  1166.     ret
  1167. SimpleShellSort    ENDP
  1168.  
  1169. ;********************
  1170. ; PARAMETERSHELLSORT
  1171. ;********************
  1172. ; sort the information in the input file according to specified parameters
  1173. ; this is also a shell sort, but slower than the simple sort version.
  1174. ; the heart of the sort routine has replaceable parts depending on the option
  1175. ;  parameter which has been specified.
  1176.  
  1177. ParameterShellSort    PROC    NEAR
  1178.     mov    eax,LineCount
  1179.     cmp    eax,1
  1180.     jbe    paret        ; 1 line is sorted by definition
  1181.  
  1182.     cmp    IsSortOffsetOption,OFF    ; check if sort offset
  1183.     je    pss2        ; no
  1184.  
  1185. ; change default logic in sort
  1186.     mov    SetOffsetCounterAddress,OFFSET OffsetCounterLogic
  1187.     mov    SortOffsetLogicAddress,OFFSET SortOffsetLogic    ; add sort offset logic
  1188.  
  1189. pss2:
  1190.     cmp    IsFixedLengthOption,OFF    ; see if fixed length sort option
  1191.     je    pss3        ; no
  1192.  
  1193. ; change default logic in sort
  1194.     mov    FixedCounterAddress,OFFSET FixedCounterLogic
  1195.     mov    FixedMoveAddress,OFFSET FixedMoveLogic
  1196.     mov    FIxedJumpAddress,OFFSET FixedJumpLogic
  1197.  
  1198. pss3:
  1199.     cmp    IsSortOrderOption,OFF    ; see if sort order option
  1200.     je    pss4        ; no
  1201.  
  1202. ; change default logic in sort
  1203.     mov    SortLookupAddress,OFFSET SortLookupLogic
  1204.  
  1205. pss4:
  1206.     mov    fs,PointerSelector
  1207.     mov    ds,BufferSelector
  1208.  
  1209. ; sort the symbol names
  1210. ; fs -> pointer array
  1211. ; ds -> input buffer
  1212. ; gs -> DGROUP
  1213.     xor    ebx,ebx
  1214.     mov    edx,ebx
  1215.     mov    bl,9
  1216.     div    ebx
  1217.     mov    ecx,eax            ; ecx == quotient, N/9
  1218.  
  1219. ; for (h=1;h<=N/9;h=3*h+1);
  1220.     mov    eax,ebx            ; eax==9
  1221.     mov    al,1            ; eax==1, h
  1222.     mov    bl,3            ; ebx==3
  1223.     xor    edx,edx            ; zero for multiply loop
  1224.  
  1225. pasetpahloop:
  1226.     cmp    eax,ecx            ; h<=N/9
  1227.     ja    pasort2
  1228.     mul    ebx                ; 3*h, assume 32-bit result (pretty safe bet)
  1229.     inc    eax                ; 3*h+1
  1230.     jmp    pasetpahloop
  1231.  
  1232. ; ebx will play role of j, edx will play role of h
  1233. pasort2:
  1234.     mov    edx,eax            ; edx == h
  1235.  
  1236. ; for (;h>0;...
  1237. pahloop:
  1238.     or    edx,edx            ; h>0
  1239.     je    pasortend
  1240.  
  1241. ; for(i=h+1...
  1242.     mov    eax,edx
  1243.     inc    eax
  1244.     mov    gs:iValue,eax
  1245.  
  1246. ; for(...;i<=N;...){
  1247. pailoop:
  1248.     mov    eax,gs:iValue
  1249.     cmp    eax,gs:LineCount
  1250.     ja    panexth
  1251.  
  1252.     mov    ecx,fs:[4*eax]
  1253.     mov    gs:vValue,ecx    ; v=a[i]
  1254.     mov    ebx,eax            ; j=i
  1255.  
  1256. ; while(j>h && a[j-h]>v){
  1257. pawhileloop:
  1258.     cmp    ebx,edx            ; j>h
  1259.     jbe    pawhilefail
  1260.  
  1261.     mov    eax,ebx
  1262.     sub    eax,edx            ; eax==j-h
  1263.     xor    ecx,ecx            ; zero high bytes of register for following repe
  1264.  
  1265.     mov    esi,fs:[4*eax]    ; ds:esi -> a[j-h]
  1266.     mov    edi,gs:vValue    ; es:edi==v
  1267.  
  1268.     jmp    DWORD PTR gs:[SetOffsetCounterAddress]
  1269.  
  1270. pastsetoff:
  1271.  
  1272.     jmp    DWORD PTR gs:[FixedCounterAddress]
  1273.  
  1274. pacomploop:
  1275.  
  1276.     jmp    DWORD PTR gs:[FixedMoveAddress]
  1277.  
  1278. pastmove:
  1279.     inc    esi
  1280.     inc    edi
  1281.  
  1282.     jmp    DWORD PTR gs:[SortOffsetLogicAddress]
  1283.  
  1284. pastoffset:
  1285.     jmp    DWORD PTR gs:[SortLookupAddress]
  1286.  
  1287. padocomp:
  1288.     cmp    al,cl
  1289.     jb    pawhilefail    ; first < second, a[j-h]<v
  1290.     jne    padochange    ; first > second, modify a[j]
  1291.  
  1292.     jmp    DWORD PTR gs:[FixedJumpAddress]
  1293.  
  1294. padochange:
  1295.     mov    eax,ebx
  1296.     sub    eax,edx            ; eax==j-h
  1297.     mov    eax,fs:[4*eax]    ; eax==a[j-h]
  1298.     mov    fs:[4*ebx],eax    ; a[j]=a[j-h]
  1299.     sub    ebx,edx            ; j-=h
  1300.     jmp    pawhileloop
  1301.  
  1302. pawhilefail:
  1303.     mov    eax,gs:vValue
  1304.     mov    fs:[4*ebx],eax    ; a[j]=v
  1305.  
  1306. ; for(...;i++){
  1307.     inc    gs:iValue
  1308.     jmp    pailoop
  1309.  
  1310. ; for (...;h/=3){
  1311. panexth:
  1312.     mov    eax,edx
  1313.     xor    edx,edx
  1314.     mov    ecx,edx
  1315.     mov    cl,3
  1316.     div    ecx
  1317.     mov    edx,eax
  1318.     jmp    pahloop
  1319.  
  1320. pasortend:
  1321.     mov    ax,DGROUP
  1322.     mov    ds,ax
  1323.  
  1324. paret:
  1325.     ret
  1326.  
  1327. ; variable logic routines for different sort parameters follows
  1328.  
  1329. ; no counter to set
  1330. ; has to be constructed as label near to shut up ML 6 griping
  1331. NoOffsetCounterLogic    LABEL    NEAR
  1332.     jmp    pastsetoff    ; simple processing return
  1333.  
  1334. ; set offset counter for sort offset processing
  1335. OffsetCounterLogic:
  1336.     mov    eax,gs:SortOffsetValue
  1337.     mov    gs:SortOffsetCounter,eax
  1338.     jmp    pastsetoff
  1339.  
  1340. ; no sort offset logic
  1341. NoSortOffsetLogic    LABEL    NEAR
  1342.     jmp    pastoffset
  1343.  
  1344. ; sort offset logic
  1345. SortOffsetLogic:
  1346.     dec    gs:SortOffsetCounter    ; drop counter of sort offset
  1347.     jg    pacomploop    ; still positive nonzero
  1348.     jmp    pastoffset    ; sort offset has been decremented down, continue with sort
  1349.  
  1350. ; no fixed counter logic
  1351. NoFixedCounterLogic    LABEL    NEAR
  1352.     jmp    pacomploop
  1353.  
  1354. ; fixed length sort counter logic
  1355. FixedCounterLogic:
  1356.     mov    eax,gs:FixedLengthSize
  1357.     mov    gs:FixedLengthCounter,eax
  1358.     jmp    pacomploop
  1359.  
  1360. ; no fixed move logic
  1361. NoFixedMoveLogic    LABEL    NEAR
  1362.     mov    ax,ds:[esi]
  1363.     mov    cx,ds:[edi]
  1364.     cmp    ax,CR+(256*LF)
  1365.     je    pawhilefail    ; first <= second, a[j-h]<v
  1366.     cmp    cx,CR+(256*LF)    ; check if first >= second
  1367.     je    padochange    ; first > second, modify a[j]
  1368.     jmp    pastmove
  1369.  
  1370. ; fixed length sort move logic
  1371. FixedMoveLogic:
  1372.     mov    al,ds:[esi]
  1373.     mov    cl,ds:[edi]
  1374.     jmp    pastmove
  1375.  
  1376. ; no fixed jump logic
  1377. NoFixedJumpLogic    LABEL    NEAR
  1378.     jmp    pacomploop
  1379.  
  1380. ; fixed length sort jump logic
  1381. FixedJumpLogic:
  1382.     dec    gs:FixedLengthCounter    ; drop count of bytes to check
  1383.     jne    pacomploop    ; more bytes to check
  1384.     jmp    pawhilefail    ; equal length of field
  1385.  
  1386. ; no sort order lookup table logic
  1387. NoSortLookupLogic    LABEL    NEAR
  1388.     jmp    padocomp
  1389.  
  1390. ; sort order lookup table logic
  1391. SortLookupLogic:
  1392.     movzx    eax,al    ; get offsets in sort order table
  1393.     movzx    ecx,cl
  1394.     mov    al,gs:[SortOrderTable+eax]    ; get new lookup compare values
  1395.     mov    cl,gs:[SortOrderTable+ecx]
  1396.     jmp    padocomp
  1397.  
  1398. ParameterShellSort    ENDP
  1399.  
  1400. ;*********************
  1401. ; CHECKDUPLICATELINES
  1402. ;*********************
  1403. ; check for and remove duplicate lines if the no duplicates option specified
  1404.  
  1405. CheckDuplicateLines    PROC    NEAR
  1406.     push    ds        ; save critical register
  1407.     cmp    IsDuplicateOption,OFF
  1408.     je    cdlret        ; duplicate removal not turned on
  1409.  
  1410.     mov    ebx,8
  1411.     mov    fs,PointerSelector    ; fs:ebx -> pointer array, second entry
  1412.     mov    ecx,LineCount
  1413.     dec    ecx
  1414.     je    cdlret        ; only one line
  1415.  
  1416.     mov    ds,BufferSelector    ; ds -> input buffer
  1417.  
  1418. cdllineloop:
  1419.     mov    esi,fs:[ebx]    ; ds:esi -> current line to check against previous
  1420.     mov    edi,fs:[ebx-4]    ; es:edi -> previous line
  1421.  
  1422.     cmp    gs:IsFixedLengthOption,OFF    ; see if fixed length option specified
  1423.     je    cdlvarloop        ; no
  1424.  
  1425. ; fixed length
  1426.     mov    edx,gs:FixedLengthSize
  1427.  
  1428. cdlfixloop:
  1429.     mov    al,ds:[esi]        ; get current line char
  1430.     cmp    al,ds:[edi]        ; check against previous line's
  1431.     jne    cdlnext            ; no match on lines
  1432.     inc    esi                ; move to next position
  1433.     inc    edi
  1434.     dec    edx                ; drop count of chars to check
  1435.     jne    cdlfixloop        ; more to check
  1436.  
  1437. ; current line matched previous
  1438. ; update input file size with removed character count
  1439. cdlmatch:
  1440.     sub    esi,fs:[ebx]
  1441.     sub    gs:InputFileSize,esi
  1442.     mov    DWORD PTR fs:[ebx-4],-1    ; mark previous line for removal
  1443.     jmp    cdlnext
  1444.  
  1445. ; variable length fields
  1446. cdlvarloop:
  1447.     mov    ax,ds:[esi]        ; get current line char+1
  1448.     mov    dx,ds:[edi]
  1449.     cmp    al,dl            ; check first line char against previous line's
  1450.     jne    cdlnext            ; no match on lines
  1451.     inc    esi                ; move to next position
  1452.     inc    edi
  1453.     cmp    ax,CR+(256*LF)    ; see if at end of first line
  1454.     je    cdlfirstend        ; yes
  1455.     cmp    dx,CR+(256*LF)    ; see if at end of second line
  1456.     jne    cdlvarloop        ; no
  1457.     jmp    cdlnext            ; lines don't match, different lengths
  1458.  
  1459. ; at end of first line
  1460. cdlfirstend:
  1461.     cmp    ax,dx            ; see if match on EOL
  1462.     jne    cdlnext            ; no
  1463.     inc    esi                ; update offset so can properly update InputFileSize
  1464.     jmp    cdlmatch        ; yes
  1465.  
  1466. cdlnext:
  1467.     add    ebx,4            ; move to next line entry
  1468.     dec    ecx                ; drop count of lines to check
  1469.     jne    cdllineloop
  1470.  
  1471. cdlret:
  1472.     pop    ds            ; restore critical register
  1473.     ret
  1474. CheckDuplicateLines    ENDP
  1475.  
  1476. ;*******************
  1477. ; BUILDOUTPUTBUFFER
  1478. ;*******************
  1479. ; build output buffer from sorted pointers to input buffer
  1480.  
  1481. BuildOutputBuffer    PROC    NEAR
  1482.     push    ds        ; save critical register
  1483.     mov    eax,InputFileSize
  1484.     call    GetMemory    ; attempt to allocate sufficient memory to hold file
  1485.     mov    OutputSelector,ax    ; save selector
  1486.     xor    edi,edi
  1487.     mov    ebx,4
  1488.     mov    fs,PointerSelector    ; fs:ebx -> pointer array
  1489.     mov    es,ax        ; es:edi -> output buffer
  1490.     mov    ecx,LineCount
  1491.     mov    ebp,4        ; increment for next entry
  1492.     cmp    IsReverseOption,ON    ; see if reversing normal order
  1493.     jne    bo2            ; no
  1494.     mov    ebp,ecx
  1495.     dec    ebp            ; make relative zero
  1496.     shl    ebp,2        ; dword entry/line
  1497.     add    ebx,ebp        ; ebx -> last entry
  1498.     mov    ebp,-4        ; increment for next entry
  1499.  
  1500. bo2:
  1501.     mov    ds,BufferSelector    ; ds -> input buffer
  1502.  
  1503. lineloop:
  1504.     mov    esi,fs:[ebx]    ; ds:esi -> current line to transfer
  1505.     add    ebx,ebp        ; move to next pointer in array
  1506.     cmp    esi,-1        ; see if marked for removal
  1507.     je    nextline    ; yes
  1508.  
  1509.     cmp    gs:IsFixedLengthOption,OFF    ; see if fixed length sort
  1510.     je    transloop    ; no
  1511.  
  1512. ; fixed length value transfer, just blast across the character count
  1513.     push    ecx        ; save critical register
  1514.     mov    ecx,gs:FixedLengthSize
  1515.     push    ecx
  1516.     shr    ecx,2
  1517.     rep    movsd
  1518.     pop    ecx
  1519.     and    ecx,3
  1520.     rep    movsb
  1521.     pop    ecx            ; restore critical register
  1522.     jmp    nextline
  1523.  
  1524. transloop:
  1525.     mov    ax,ds:[esi]
  1526.     add    esi,2        ; move to next word in input buffer
  1527.     cmp    al,CR        ; check if possible end of line
  1528.     je    chkend1
  1529.     cmp    ah,CR
  1530.     je    chkend2
  1531.  
  1532. dotrans:
  1533.     mov    es:[edi],ax    ; no end of line, transfer characters
  1534.     add    edi,2        ; move to next word in output buffer
  1535.     jmp    transloop
  1536.  
  1537. chkend1:
  1538.     cmp    ah,LF        ; see if CR/LF pair
  1539.     jne    dotrans        ; no
  1540.     mov    es:[edi],ax
  1541.     add    edi,2
  1542.     jmp    nextline
  1543.  
  1544. chkend2:
  1545.     cmp    BYTE PTR ds:[esi],LF    ; see if CR/LF pair
  1546.     jne    dotrans        ; no
  1547.     mov    es:[edi],ax
  1548.     mov    BYTE PTR es:[edi+2],LF
  1549.     add    edi,3
  1550.  
  1551. nextline:
  1552.     dec    ecx
  1553.     jne    lineloop
  1554.  
  1555. ; release the input buffer and pointer buffer to free up memory and
  1556. ; possible disk space
  1557.     pop    ds            ; restore critical register
  1558.     mov    ax,BufferSelector
  1559.     call    ReleaseMemory
  1560.     mov    BufferSelector,0
  1561.     mov    ax,PointerSelector
  1562.     call    ReleaseMemory
  1563.     mov    PointerSelector,0
  1564.     ret
  1565. BuildOutputBuffer    ENDP
  1566.  
  1567. ;**********
  1568. ; READFILE
  1569. ;**********
  1570. ; read file
  1571. ; upon entry bx == file handle, cx == bytes to read,
  1572. ;   ds:edx -> read buffer
  1573. ; return carry set if error, otherwise ax == bytes read
  1574.  
  1575. ReadFile    PROC    NEAR
  1576.     mov    ah,3fh        ; read from file
  1577.     int    21h
  1578.     ret
  1579. ReadFile    ENDP
  1580.  
  1581. ;********************
  1582. ; WRITEFILETODISK
  1583. ;********************
  1584. ; write sorted input file to disk
  1585. ; return carry flag set if failure,
  1586. ;   carry flag reset if success
  1587.  
  1588. WriteFileToDisk    PROC    NEAR
  1589.     push    ds        ; save critical register
  1590.  
  1591.     mov    bx,OutputFileHandle
  1592.     mov    esi,InputFileSize
  1593.     mov    ds,OutputSelector    ; ds -> output file write buffer
  1594.     xor    edx,edx
  1595.  
  1596. wffileloop:
  1597.     mov    ecx,esi
  1598.     cmp    ecx,0fff0h    ; see if past maximum
  1599.     jbe    wfwrite        ; no
  1600.     mov    cx,0fff0h    ; set bytes to write to maximum (ecx high word known zero)
  1601.  
  1602. wfwrite:
  1603.     call    WriteFile
  1604.     jc    wfret        ; error during write
  1605.     movzx    eax,ax    ; get bytes written this pass
  1606.     add    edx,eax        ; update buffer write position
  1607.     sub    esi,eax        ; update bytes left to write
  1608.     jne    wffileloop    ; more to write
  1609.  
  1610. wfret:
  1611.     pop    ds            ; restore critical register
  1612.     ret
  1613. WriteFileToDisk    ENDP
  1614.  
  1615. ;***********
  1616. ; WRITEFILE
  1617. ;***********
  1618. ; write file
  1619. ; upon entry bx == file handle, cx == bytes to write,
  1620. ;   ds:edx -> write buffer
  1621. ; return carry set if error, otherwise ax == bytes written
  1622.  
  1623. WriteFile    PROC    NEAR
  1624.     mov    ah,40h        ; write to file
  1625.     int    21h
  1626.     ret
  1627. WriteFile    ENDP
  1628.  
  1629. ;***********
  1630. ; CLOSEFILE
  1631. ;***********
  1632. ; close  file
  1633. ; upon entry bx == file handle
  1634. ; return carry set if error, otherwise reset
  1635.  
  1636. CloseFile    PROC    NEAR
  1637.     mov    ah,3eh        ; close file
  1638.     int    21h
  1639.     ret
  1640. CloseFile    ENDP
  1641.  
  1642. ;***********
  1643. ; TERMINATE
  1644. ;***********
  1645. ; perform termination functions
  1646.  
  1647. Terminate    PROC    NEAR
  1648.     call    Cleanup
  1649.     call    ExitApplication    ; no return
  1650. Terminate    ENDP
  1651.  
  1652. ;*********
  1653. ; CLEANUP
  1654. ;*********
  1655. ; perform cleanup operations prior to exiting
  1656.  
  1657. CleanUp    PROC    NEAR
  1658.  
  1659. ; free allocated memory, although not strictly necessary since normal
  1660. ;   DOS termination will accomplish the task as well.
  1661.     mov    ax,BufferSelector
  1662.     cmp    ax,0        ; see if selector is allocated
  1663.     je    cu2            ; nope
  1664.     call    ReleaseMemory
  1665.  
  1666. cu2:
  1667.     mov    ax,PointerSelector
  1668.     cmp    ax,0        ; see if selector is allocated
  1669.     je    cu3            ; nope
  1670.     call    ReleaseMemory
  1671.  
  1672. cu3:
  1673.     mov    ax,OutputSelector
  1674.     cmp    ax,0        ; see if selector is allocated
  1675.     je    cu4            ; nope
  1676.     call    ReleaseMemory
  1677.  
  1678. cu4:
  1679.     ret
  1680. CleanUp    ENDP
  1681.  
  1682. ;*****************
  1683. ; EXITAPPLICATION
  1684. ;*****************
  1685. ; exit the application
  1686.  
  1687. ExitApplication    PROC    NEAR
  1688.     mov    al,ExitCode    ; get code to return to calling program
  1689.     mov    ah,4ch        ; exit with return code
  1690.     int    21h
  1691. ExitApplication    ENDP
  1692.  
  1693. ;*************
  1694. ; OUTOFMEMORY
  1695. ;*************
  1696. ; out of memory, give feedback and terminate
  1697.  
  1698. OutOfMemory    PROC    NEAR
  1699.     mov    eax,DGROUP
  1700.     mov    ds,eax        ; ensure ds -> cwsort data
  1701.     mov    edx,OFFSET DGROUP:MemoryText
  1702.     call    DisplayStringCRLF
  1703.     mov    ExitCode,8
  1704.     call    Terminate    ; no return
  1705. OutOfMemory    ENDP
  1706.  
  1707. ;
  1708. ;***** CauseWay specific functions which use the CauseWay API *****
  1709. ;
  1710.  
  1711. ;***************
  1712. ; RELEASEMEMORY
  1713. ;***************
  1714. ; release previously allocated memory block
  1715. ; upon entry ax == selector of block to release
  1716. ; return carry flag set if error
  1717.  
  1718. ReleaseMemory    PROC    NEAR
  1719.     push    ebx        ; save used registers
  1720.     mov    bx,ax        ; bx == selector of block to release
  1721.     mov    ax,0ff0fh    ; CauseWay RelMem function
  1722.     int    31h
  1723.     pop    ebx            ; restored used registers
  1724.     ret
  1725. ReleaseMemory    ENDP
  1726.  
  1727. ;***********
  1728. ; GETMEMORY
  1729. ;***********
  1730. ; allocate memory block, automatically use virtual memory if
  1731. ;   available and required (allocation exceeds free physical memory)
  1732. ; upon entry eax == size of block to allocate in bytes
  1733. ; return ax == selector of allocated block
  1734.  
  1735. GetMemory    PROC    NEAR
  1736.     push    ebx        ; save used registers
  1737.     push    ecx
  1738.     mov    ecx,eax        ; ecx == size of block to allocate
  1739.     mov    ax,0ff0ch    ; CauseWay GetMem32 function
  1740.     int    31h
  1741.     jc    gmerror
  1742.     mov    ax,bx        ; ax == selector of allocated block
  1743.     pop    ecx            ; restored used registers
  1744.     pop    ebx
  1745.     ret
  1746.  
  1747. ; error allocating, out of memory
  1748. gmerror:
  1749.     call    OutOfMemory    ; no return
  1750.  
  1751. GetMemory    ENDP
  1752.  
  1753. ;***************
  1754. ;* RESIZEMEMORY
  1755. ;***************
  1756.  
  1757. ; resize allocated memory block
  1758. ; upon entry ax == selector, ecx == new size of block in bytes
  1759.  
  1760. ResizeMemory    PROC    NEAR
  1761.     push    ecx        ; save used registers
  1762.     push    ebx
  1763.     mov    bx,ax        ; selector value to bx
  1764.     mov    ax,0ff0eh    ; CauseWay ResMem32 function
  1765.     int    31h
  1766.     jc    rmerror
  1767.     pop    ebx            ; restore used registers
  1768.     pop    ecx
  1769.     ret
  1770.  
  1771. ; error resizing memory
  1772. rmerror:
  1773.     call    OutOfMemory    ; no return
  1774.  
  1775. ResizeMemory    ENDP
  1776.  
  1777. END    main
  1778.