Xây dựng hải ly bận rộn nhất trong mã máy x86 trong 32 byte trở xuống


8

Nhiệm vụ của bạn là viết một chương trình bằng ngôn ngữ máy x86 (bất kỳ phiên bản nào bạn thích) sẽ chạy qua càng nhiều hướng dẫn càng tốt và sau đó dừng lại, sử dụng tối đa 32 byte mã và bắt đầu bằng các thanh ghi zeroed out.

Bạn có thể giả sử bất cứ điều gì về bộ nhớ truy cập ngẫu nhiên mà bạn thích, miễn là nó ở định dạng có thể sử dụng được cho máy x86.


3
Có những cỗ máy hải ly bận rộn khá nhỏ mà hành vi tạm dừng không xác định ( en.wikipedia.org/wiki/Busy_beaver#Known_values ). Điều gì xảy ra khi ai đó thực hiện một trong số họ?
asmeurer

Có một vài chi tiết có thể được điền vào đây. Chúng ta có một ngăn xếp được thiết lập cho chúng ta? Nó lớn như thế nào Chương trình có miễn phí truy cập bộ nhớ không? Nếu vậy, bao nhiêu của nó? Chúng tôi có quyền truy cập đọc-ghi vào tất cả 4 GB không? Nếu vậy, chúng tôi có thể chọn nơi chương trình của chúng tôi được đặt?
hộp bánh mì

Bạn có thể giả sử bất cứ điều gì về bộ nhớ mà bạn muốn, tôi đoán, miễn là các thanh ghi thực tế đều bị loại bỏ khi bắt đầu thực thi mã. (Điều này có gây ra sự cố không? Tôi không có kinh nghiệm khủng khiếp với ngôn ngữ máy.)
Joe Z.

Chúng ta có thể cho rằng bộ xử lý ở chế độ thực và / hoặc chế độ được bảo vệ không? Bởi vì nếu chúng ta có thể sử dụng chế độ được bảo vệ, điều đó sẽ mở ra một loạt các câu hỏi khác về Bảng mô tả toàn cầu và bảng phân trang, v.v. Với những vấn đề đó, có lẽ tốt hơn là chỉ thực thi chế độ thực, có nghĩa là có ít bộ nhớ hơn . Trừ khi chúng ta có thể giả định chế độ không thực, mà tôi nhận ra bây giờ tôi đang ngầm giả định rằng tôi đang ở trong. Nhưng trong mọi trường hợp, điều này có lẽ nên được gọi ra một cách rõ ràng trong mô tả vấn đề.
hộp bánh mì

Câu trả lời:


8

2 524224 hướng dẫn

Chương trình của tôi:

_start:
        mov     bl, _end
        stc
iloop:  adc     [bx], al
        inc     bx
        jnz     iloop
        jnc     _start
        a32
        loop    _start
        hlt
_end:

(Lưu ý kỹ thuật: phần này được viết cho nasm. a32Cú pháp của nasm cho byte tiền tố kích thước địa chỉ thay thế. Đối với masm, bạn sẽ thay thế a32bằng defb 0x67.)

Để rõ ràng, đây là đầu ra danh sách:

 1                  _start:
 2 0000 B310                mov     bl, _end
 3 0002 F9                  stc
 4 0003 1007        iloop:  adc     [bx], al
 5 0005 43                  inc     bx
 6 0006 75F9                jnz     iloop
 7 0008 73F4                jnc     _start
 8 000A 67                  a32
 9 000B E2F1                loop    _start
10 000D F4                  hlt
11                  _end:

Chương trình giả định rằng bộ xử lý ở chế độ thực và chương trình nằm ở dưới cùng của phân đoạn bộ nhớ 64k, nếu không được khởi tạo thành all-bit-zero. Thiết kế của nó rất đơn giản: coi bộ nhớ là một số nguyên không dấu khổng lồ duy nhất và tăng nó thông qua tất cả các giá trị có thể, cho đến khi nó quay trở lại tất cả các số không. Lặp lại 2 lần này 32 lần. Rồi dừng lại.

Vòng lặp trong cùng (dòng 4‒6) chịu trách nhiệm tăng số nguyên khổng lồ. Mỗi lần lặp lại thêm 0 hoặc 1 vào một byte đơn, tùy thuộc vào việc có thực hiện được byte trước đó hay không. Lưu ý rằng mỗi byte trong số nguyên khổng lồ được truy cập, cho dù nó có thay đổi hay không, vì vậy vòng lặp này luôn lặp lại 2 16 - 14 lần.

Nhân tiện, trong trường hợp bạn đang tự hỏi, mã này minh họa lý do tại sao x86 inc/ dechướng dẫn không ảnh hưởng đến cờ mang: chỉ để đơn giản hóa kiểu mẫu mang nhiều byte này. (Mẫu này xuất hiện thường xuyên hơn vào thời của bộ vi xử lý 8 bit, khi tập lệnh 8080 ban đầu được xác định.)

Dòng 7 làm cho quá trình tăng dần lặp lại cho đến khi một chuỗi được thực hiện từ byte cuối cùng, chỉ ra rằng số nguyên khổng lồ đã được đặt lại thành all-bits-zero. Điều này mất một thời gian dài.

Các dòng 8‒9 đánh dấu vòng lặp ngoài cùng và khiến quá trình này lặp lại 2 32 lần, cho đến khi thanh ecxghi cuộn quanh không. Điều này có hiệu quả tương đương với việc thêm 32 bit khác vào số nguyên khổng lồ.

Có thể thêm một vòng lặp bên ngoài khác và thực hiện lại bằng cách sử dụng (giả sử) thanh edxghi, sau đó có thể sử dụng esiedicho nhiều lần lặp lại hơn nữa. Tuy nhiên, nó không đáng để làm. Các hướng dẫn để tăng và vòng lặp yêu cầu bốn byte. Bốn byte đó được lấy đi từ số nguyên khổng lồ. Vì vậy, chúng tôi mất 32 bit trên bộ đếm RAM, chỉ để thêm 32 bit thông qua một thanh ghi: cuối cùng nó là một rửa. Lý do duy nhất ecxlà một ngoại lệ là nó có một looplệnh chuyên biệt chỉ phù hợp với ba byte. Do đó, chương trình giao dịch 24 bit cho 32, mức tăng nhỏ nhưng vẫn dương 8 bit.

Không quá khó để tính trực tiếp số lượng lệnh mà chương trình thực hiện trước khi dừng. Tuy nhiên, có một cách đơn giản hơn nhiều để ước tính con số này. Các chương trình Sửa tất cả các bộ nhớ của nó, trừ đi 14 byte có chứa các chương trình, và các bxecxthanh ghi. Điều này thêm tối đa 2 16 - 14 + 2 + 4 = 65528 byte, với tổng số 524224 bit. Phím tắt liên quan đến việc nhận ra rằng, trong quá trình chạy, mọi mẫu có thể có của 524224 bit xuất hiện chính xác một lần. Đối với RAM và thanh ecxghi, điều này rất dễ thấy, vì chương trình tăng dần qua từng giá trị. Dành chobxđiều này ít rõ ràng hơn vì nó được thay đổi cùng lúc với giá trị trong bộ nhớ đang được cập nhật. Tuy nhiên, người ta có thể chỉ ra rằng, với cấu trúc của chương trình, nếu một mẫu bit hoàn chỉnh thực sự xuất hiện hai lần, thì chương trình sẽ phải nằm trong một vòng lặp vô hạn. Vì đây không phải là trường hợp, mỗi mẫu bit cuối cùng chỉ được truy cập một lần. (Bằng chứng hoàn toàn được để lại như một bài tập cho người đọc, một cách tự nhiên.)

Vì mọi mẫu bit có thể xuất hiện trong quá trình của chương trình, chương trình phải thực hiện ít nhất 2 524224 hướng dẫn, tương đương với 1,4 × 10 157807 . (Số acutal cao hơn một chút, vì các lệnh nhảy, nhưng sự khác biệt ở cường độ này là không đáng kể.)

Rõ ràng, điều này có thể được cải thiện đáng kể bằng cách sử dụng hơn 64k RAM. Tôi đang giữ phiên bản tiếp theo của mã cho đến khi tôi biết chính xác có thể truy cập bao nhiêu RAM.


Không phải là 17 hướng dẫn (34 byte) sao? Hay tôi đang thiếu một cái gì đó?
Joe Z.

@JoeZ. inc là 1 byte (nếu bạn không lắp ráp nó ở chế độ 16 bit)
sao chép

Oh. Vì vậy, chương trình này thực sự sẽ dài 25 byte?
Joe Z.

Có, đó là 25 byte
sao chép

Xin lỗi, tôi nên đã rõ ràng hơn. Tôi đã thêm đầu ra danh sách để tránh sự mơ hồ.
hộp bánh mì

4

~ 2 ^ (2 ^ 67) hướng dẫn

(tại cú pháp & t)

start:
    movq    $0x1a,%rax
    stc
loop:
    adcb    %cl,(%rax)
    incq    %rax
    jnz     loop
    jnc     start
    hlt

Đã tháo rời, 26 byte:

start:
0000000000000000    movq    $0x0000001a,%rax
0000000000000007    stc
loop:
0000000000000008    adcb    %cl,(%rax)
000000000000000a    incq    %rax
000000000000000d    jne loop
0000000000000013    jae start
0000000000000019    hlt

Tương tự như giải pháp của Breadbox, nhưng không có lý do gì để dừng lại ở 16 bit địa chỉ. Mã của tôi sử dụng x86-64 và giả sử chúng tôi có 2 ^ 64 byte bộ nhớ và chúng tôi sử dụng tất cả trừ bộ nhớ giữ mã như một bộ đếm khổng lồ.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.