Files
Cesarus85--mjolnir-vr/source/cseries/sort.c
T
Jonas VolmanandClaude Opus 5 3328df1847 Match cseries sort object
Byte-exact, all four functions, whole object.  Source was a pure skeleton with
no code; status MISSING work now NonMatching -> Matching.

An iterative quicksort for 2-byte and 4-byte elements with a 30-deep bounds
stack and a selection-sort cutoff at 8.  The two code_* symbols are the
selection sorts and take hi in EAX under VC7's custom convention for statics.
The comparator takes values, not pointers, and the zero-extension in the target
pins the 2-byte element type to word rather than short.

Five source-shape findings, each read off the disassembly:

- the swap loads both elements then stores to *best first, which is
  temp = *best; *best = *hi; *hi = temp - the mirror of the natural form.  That
  alone made both selection sorts exact.
- element_count and size are unsigned: the target uses jb, ja and shr where
  signed types give jl, jg and sar.
- the source writes lo+1 < j, not the algebraically identical lo < j-1, which
  reverses the lea and cmp operands.
- the larger-partition test compares raw byte differences.  On word pointers
  every element-count form makes MSVC emit two sars or cancel them and mask,
  costing 16 bytes; only an explicit byte-basis comparison gives the plain dec.
  j-1-lo >= hi-i, j-lo > hi-i and the byte form are integer-identical and only
  the last matches.
- depth = 0 is assigned before hi; the reverse order swaps the two stores.

Both qsort bodies went exact from the same five fixes, which is good evidence
the shapes are right rather than coincidental.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-13 04:33:37 -07:00

314 lines
4.0 KiB
C

/*
SORT.C
symbols in this file:
00080360 0060:
_code_00080360 (0000)
000803C0 0050:
_code_000803c0 (0000)
00080410 0150:
_qsort_2byte (0000)
00080560 0140:
_qsort_4byte (0000)
*/
/* ---------- headers */
#include "cseries.h"
/* ---------- constants */
enum
{
QSORT_STACK_SIZE = 30,
QSORT_CUTOFF = 8
};
/* ---------- macros */
/* ---------- structures */
/* ---------- prototypes */
static void code_00080360(
word *hi,
word *lo,
boolean (*compare)(word, word));
static void code_000803c0(
long *hi,
long *lo,
boolean (*compare)(long, long));
void qsort_2byte(
word *elements,
unsigned long element_count,
boolean (*compare)(word, word));
void qsort_4byte(
long *elements,
unsigned long element_count,
boolean (*compare)(long, long));
/* ---------- globals */
/* ---------- public code */
void qsort_2byte(
word *elements,
unsigned long element_count,
boolean (*compare)(word, word))
{
word *lo_stack[QSORT_STACK_SIZE];
word *hi_stack[QSORT_STACK_SIZE];
word *lo, *hi, *i, *j, *mid;
long depth;
unsigned long size;
word temp;
if (element_count<2)
return;
lo = elements;
depth = 0;
hi = elements+element_count-1;
for (;;)
{
size = hi-lo+1;
if (size<=QSORT_CUTOFF)
{
code_00080360(hi, lo, compare);
}
else
{
mid = lo+(size>>1);
temp = *mid;
*mid = *lo;
*lo = temp;
i = lo;
j = hi+1;
for (;;)
{
do
{
i++;
}
while (i<=hi && !compare(*i, *lo));
do
{
j--;
}
while (j>lo && compare(*j, *lo));
if (j<i)
break;
temp = *i;
*i = *j;
*j = temp;
}
temp = *lo;
*lo = *j;
*j = temp;
if ((char *)j-(char *)lo-1>=(char *)hi-(char *)i)
{
if (lo+1<j)
{
lo_stack[depth] = lo;
hi_stack[depth] = j-1;
depth++;
}
if (i<hi)
{
lo = i;
continue;
}
}
else
{
if (i<hi)
{
lo_stack[depth] = i;
hi_stack[depth] = hi;
depth++;
}
if (lo+1<j)
{
hi = j-1;
continue;
}
}
}
if (--depth<0)
break;
lo = lo_stack[depth];
hi = hi_stack[depth];
}
return;
}
void qsort_4byte(
long *elements,
unsigned long element_count,
boolean (*compare)(long, long))
{
long *lo_stack[QSORT_STACK_SIZE];
long *hi_stack[QSORT_STACK_SIZE];
long *lo, *hi, *i, *j, *mid;
long depth;
unsigned long size;
long temp;
if (element_count<2)
return;
lo = elements;
depth = 0;
hi = elements+element_count-1;
for (;;)
{
size = hi-lo+1;
if (size<=QSORT_CUTOFF)
{
code_000803c0(hi, lo, compare);
}
else
{
mid = lo+(size>>1);
temp = *mid;
*mid = *lo;
*lo = temp;
i = lo;
j = hi+1;
for (;;)
{
do
{
i++;
}
while (i<=hi && !compare(*i, *lo));
do
{
j--;
}
while (j>lo && compare(*j, *lo));
if (j<i)
break;
temp = *i;
*i = *j;
*j = temp;
}
temp = *lo;
*lo = *j;
*j = temp;
if ((char *)j-(char *)lo-1>=(char *)hi-(char *)i)
{
if (lo+1<j)
{
lo_stack[depth] = lo;
hi_stack[depth] = j-1;
depth++;
}
if (i<hi)
{
lo = i;
continue;
}
}
else
{
if (i<hi)
{
lo_stack[depth] = i;
hi_stack[depth] = hi;
depth++;
}
if (lo+1<j)
{
hi = j-1;
continue;
}
}
}
if (--depth<0)
break;
lo = lo_stack[depth];
hi = hi_stack[depth];
}
return;
}
/* ---------- private code */
static void code_00080360(
word *hi,
word *lo,
boolean (*compare)(word, word))
{
word *best, *scan;
word temp;
while (hi>lo)
{
best = lo;
for (scan = lo+1; scan<=hi; scan++)
{
if (compare(*scan, *best))
best = scan;
}
temp = *best;
*best = *hi;
*hi = temp;
hi--;
}
return;
}
static void code_000803c0(
long *hi,
long *lo,
boolean (*compare)(long, long))
{
long *best, *scan;
long temp;
while (hi>lo)
{
best = lo;
for (scan = lo+1; scan<=hi; scan++)
{
if (compare(*scan, *best))
best = scan;
}
temp = *best;
*best = *hi;
*hi = temp;
hi--;
}
return;
}