รหัสเครื่อง x86 (IA-32), 126 ไบต์
hexdump:
60 8b f9 57 33 c0 f2 ae 5e 2b fe 4f 87 fa 8d 1c
12 8b c3 48 f6 e3 c6 04 07 00 48 c6 04 07 20 75
f9 8b ea 4d 53 8d 04 2a 50 53 8b c5 f6 e3 8d 44
68 01 50 53 2b c2 8b c8 50 4b 53 55 53 03 c5 50
f7 d3 53 50 53 95 f6 e2 6b c0 04 50 43 53 51 6a
01 4a 52 6a 01 50 6a ff 51 b0 0a 6a 0b 8b dc 59
8b 6c cb fc 88 04 2f 03 2c cb 89 6c cb fc 83 f9
0a 75 01 ac e2 ea 4a 79 e0 83 c4 58 61 c3
นี่เป็นเวลาค่อนข้างนานดังนั้นเพื่ออธิบายฉันจะให้รหัส C ก่อน:
void doit(const char* s, char out[])
{
int n = strlen(s);
int w = 2 * n;
int h = w - 1;
int m = n - 1;
memset(out, ' ', h * w);
out[h * w] = 0;
int offset1 = n + m;
int offset2 = w * m + 2 * m + 1; // 2 * n * n - 1
int offset3 = offset2 - n; // 2 * n * n - n - 1
int offset4 = 4 * n * m; // 4 * n * n - 4 * n
int offsets[] = {
offset3, -1,
offset4, 1,
m, 1,
offset3, 1 - w,
offset4, -w,
offset2 - 1, -w,
offset2 - 1, w - 1,
m, w - 1,
offset3, w,
offset2, w,
offset1, w,
};
do
{
char c = *s++;
for (int i = 0; i < 11; ++i)
{
if (i == 9)
c = '\n';
int offset = offsets[i * 2];
assert(offset > 0 && offset < w * h);
out[offset] = c;
offsets[i * 2] += offsets[i * 2 + 1];
}
} while (--n);
}
ที่นี่ n
คือความยาวของสตริงอินพุต
ขนาดของพื้นที่ส่งออกคือ2n
(ความกว้าง) โดย2n-1
(ความสูง) ก่อนอื่นมันเติมทุกอย่างด้วยช่องว่าง จากนั้นมันจะเดินทางไปตามเส้นตรง 11 เส้นในพื้นที่เอาท์พุทและเติมด้วยข้อความ:
- 2 บรรทัดเต็มไปด้วยไบต์สิ้นสุดของบรรทัด (= 10)
- 9 บรรทัดจะเต็มไปด้วยไบต์ที่ต่อเนื่องของสายป้อน
แต่ละบรรทัดจะถูกแทนด้วยตัวเลขสองตัวเริ่มต้นการชดเชยและก้าวย่าง ฉันยัดพวกเขาทั้งสองเข้าไปในแถวลำดับoffsets
ย์เพื่อให้เข้าถึง "ง่าย"
ส่วนที่น่าสนใจคือการเติมอาเรย์ มีความสำคัญเล็กน้อยสำหรับลำดับของรายการในอาร์เรย์ ฉันพยายามจัดเรียงใหม่เพื่อลดจำนวนข้อขัดแย้งในการลงทะเบียน นอกจากนี้สูตรสมการกำลังสองมีอิสระในการเลือกวิธีการคำนวณ ฉันพยายามลดจำนวนการลบออก (เนื่องจากการเพิ่มเติมสามารถนำไปใช้โดยความยืดหยุ่นLEA
คำสั่งที่ )
แหล่งประกอบ:
pushad;
; // Calculate the length of the input string
mov edi, ecx;
push edi;
xor eax, eax;
repne scasb;
pop esi; // esi = input string
sub edi, esi;
dec edi;
; // Calculate the size of the output area
xchg edi, edx; // edx = n
// edi = output string
lea ebx, [edx + edx]; // ebx = w
mov eax, ebx;
dec eax; // eax = h
mul bl; // eax = w * h
; // Fill the output string with spaces and zero terminate it
mov byte ptr [edi + eax], 0;
myfill:
dec eax;
mov byte ptr [edi + eax], ' ';
jnz myfill;
mov ebp, edx;
dec ebp; // ebp = m
; // Fill the array of offsets
push ebx; // w
lea eax, [edx + ebp];
push eax; // offset1
push ebx; // w
mov eax, ebp;
mul bl;
lea eax, [eax + 2 * ebp + 1];
push eax; // offset2
push ebx; // w
sub eax, edx;
mov ecx, eax; // ecx = offset3
push eax; // offset3
dec ebx;
push ebx; // w - 1
push ebp; // m
push ebx; // w - 1
add eax, ebp;
push eax; // offset2 - 1
not ebx;
push ebx; // -w
push eax; // offset2 - 1
push ebx; // -w
xchg eax, ebp; // eax = m
mul dl;
imul eax, eax, 4;
push eax; // offset4
inc ebx;
push ebx; // 1 - w
push ecx; // offset3
push 1;
dec edx; // edx = n - 1
push edx;
push 1;
push eax;
push -1;
push ecx;
; // Use the array of offsets to write stuff to output
myout:
mov al, '\n';
push 11;
mov ebx, esp;
pop ecx;
myloop:
mov ebp, [ebx + ecx * 8 - 4];
mov [edi + ebp], al;
add ebp, [ebx + ecx * 8];
mov [ebx + ecx * 8 - 4], ebp;
cmp ecx, 10;
jne skip_read;
lodsb;
skip_read:
loop myloop;
dec edx;
jns myout;
add esp, 11 * 8;
popad;
ret;
ฉันใช้การคูณไบต์ที่นี่จำกัดความยาวของสตริงอินพุตที่ 127 ซึ่งจะช่วยหลีกเลี่ยงการลงทะเบียนซ้ำซ้อนedx
- ผลิตภัณฑ์จะถูกคำนวณax
แทน
ความผิดพลาดเล็กน้อย: เมื่อเติมอาร์เรย์ความยาวของสตริงจะลดลง 1 ดังนั้นฉันจึงปรับเงื่อนไขการออกจากลูป:
jns myout
มันนับเป็น -1