Mã máy x86, 34 byte
51
31 D2
AD
F7 D0
25 C0 C0 C0 00
75 01
42
E2 F3
C1 E2 03
DB 04 24
52
DB 04 24
DE F1
DB 1C 24
58
5A
C3
Các byte mã này xác định một hàm lấy đầu vào bitmap và trả về một giá trị nguyên cho biết oktas của nó. Như trong C , các mảng (như bitmap) được biểu diễn dưới dạng con trỏ đến phần tử đầu tiên và kích thước / chiều dài. Do đó, hàm này có hai tham số: tổng số pixel trong bitmap (cột × cột) và một con trỏ tới chính bitmap.
Mã này sử dụng quy ước gọi dựa trên thanh ghi tùy chỉnh, trong đó con trỏ bitmap được truyền trong thanh ESI
ghi và kích thước bitmap được truyền trong thanh ECX
ghi. Kết quả (oktas) là, thông thường, được trả lại EAX
.
Như đã nêu ở trên, đầu vào được lấy dưới dạng bitmap. Cụ thể, định dạng 32 bpp được sử dụng, ở định dạng cuối nhỏ, nhưng kênh alpha (byte thứ tự cao nhất) bị bỏ qua. Điều này đơn giản hóa rất nhiều thứ, cho phép chúng ta chỉ cần lặp lại qua từng pixel và kiểm tra giá trị màu RGB 32 bit của nó. Một tối ưu hóa thông minh cũng được sử dụng ở đây. Thay vì cô lập từng thành phần màu và kiểm tra xem nó có> = 192 hay không, chúng ta chỉ che dấu toàn bộ giá trị 32 bit bằng 0xC0C0C0 và kiểm tra xem kết quả có> = 0xC0C0C0 hay không. Điều này sẽ đánh giá đúng với tất cả các màu "đám mây" và sai cho tất cả các màu "bầu trời" (không phải đám mây). Chà, tôi nghĩ nó thật thông minh! :-) Nó chắc chắn tiết kiệm một số lượng lớn byte.
Do đó, để kiểm tra mã này, bạn sẽ cần phải chuyển đổi hình ảnh đầu vào thành bitmap 32 bpp. Bạn không thể sử dụng Windows Paint cho việc này vì nó hỗ trợ tối đa 24 bit cho mỗi pixel. Tuy nhiên, có một số giải pháp phần mềm khác có thể làm điều đó, chẳng hạn như Adobe Photoshop. Tôi đã sử dụng công cụ miễn phí này để chuyển đổi PNG thành BMP 32 bpp trên Windows, nghĩa là bạn chỉ cần chuyển đổi từ JPEG sang PNG (điều mà Paint có thể làm).
Các giả định khác mà tôi đặt ra là rất hợp lý:
- Bitmap được giả sử là có kích thước lớn hơn 0 ( nghĩa là , nó được giả sử có chứa ít nhất một pixel). Điều này là hợp lý bởi vì, khi bầu trời của chúng là null, chúng ta có vấn đề lớn hơn khí tượng học.
- Cờ hướng (
DF
) được giả sử là rõ ràng để chúng ta sẽ lặp lại chính xác thông qua bitmap bằng cách sử dụng LODSD
hướng dẫn. Đây là giả định tương tự được thực hiện bởi hầu hết các quy ước gọi x86, vì vậy nó có vẻ công bằng. Nếu bạn không thích nó, hãy thêm 1 byte vào số đếm choCLD
hướng dẫn.
- Chế độ làm tròn cho FPU x87 được giả sử là được đặt thành làm tròn đến gần nhất-chẵn. Điều này đảm bảo rằng chúng tôi có được hành vi chính xác khi chúng tôi chuyển đổi số lượng oktas từ tạm thời dấu phẩy động sang kết quả số nguyên cuối cùng, như được xác minh bởi trường hợp thử nghiệm # 4. Giả định này là hợp lý bởi vì đây là trạng thái mặc định cho FPU và nó được yêu cầu duy trì ngay cả trong mã C (trong đó cắt ngắn là hành vi làm tròn mặc định, buộc các trình biên dịch muốn tuân thủ tiêu chuẩn để tạo mã không hiệu quả làm thay đổi làm tròn chế độ, thực hiện chuyển đổi, và sau đó thay đổi chế độ làm tròn trở lại).
Ma thuật lắp ráp bất khả xâm phạm:
; int ComputeOktas(void* bmpBits /* ESI */,
; uint32_t bmpSize /* ECX */);
push ecx ; save size on stack
xor edx, edx ; EDX = 0 (cloudy pixel counter)
CheckPixels:
lodsd ; EAX = DS:[ESI]; ESI += 4
not eax
and eax, 0x00C0C0C0
jnz NotCloudy
inc edx
NotCloudy:
loop CheckPixels ; ECX -= 1; loop if ECX > 0
shl edx, 3 ; counter *= 8
fild DWORD PTR [esp] ; load original size from stack
push edx
fild DWORD PTR [esp] ; load counter from stack
fdivrp st(1), st(0) ; ST(0) = counter*8 / size
fistp DWORD PTR [esp] ; convert to integer, rounding to nearest even
pop eax ; load result
pop edx
ret
Chắc chắn bạn đã không làm cho nó xuống hết mức và vẫn đang tự hỏi làm thế nào mã hoạt động? :-)
Chà, nó khá đơn giản. Chúng tôi chỉ lặp lại thông qua bitmap một giá trị 32 bit tại một thời điểm, kiểm tra xem liệu giá trị RGB pixel đó là "mây" hay "không mây". Nếu trời nhiều mây, chúng tôi sẽ tăng bộ đếm trước không. Cuối cùng, chúng tôi tính toán: pixel pixel ⁄ tổng pixel × 8
(tương đương với: pixel mây ⁄ tổng pixel 0.125).
Tôi không thể bao gồm một liên kết TIO cho việc này vì nhu cầu về hình ảnh đầu vào. Tuy nhiên, tôi có thể cung cấp cho bạn khai thác mà tôi đã sử dụng để kiểm tra điều này trên Windows:
#include <stdio.h>
#include <assert.h>
#include <Windows.h>
int main()
{
// Load bitmap as a DIB section under Windows, ensuring device-neutrality
// and providing us direct access to its bits.
HBITMAP hBitmap = (HBITMAP)LoadImage(NULL,
TEXT("C:\\...\\test1.bmp"),
IMAGE_BITMAP,
0, 0,
LR_LOADFROMFILE | LR_CREATEDIBSECTION);
assert(hBitmap != NULL);
// Get the bitmap's bits and attributes.
DIBSECTION dib;
GetObject(hBitmap, sizeof(dib), &dib);
assert(dib.dsBm.bmBitsPixel == 32);
uint32_t cx = dib.dsBm.bmWidth;
uint32_t cy = abs(dib.dsBm.bmHeight);
uint32_t sz = cx * cy;
assert(sz > 0);
int oktas = ComputeOktas(sz, dib.dsBm.bmBits);
printf("%d\n", oktas);
return 0;
}
Hãy cẩn thận với điều này, mặc dù! Như đã định nghĩa ở trên, ComputeOktas
sử dụng quy ước gọi tùy chỉnh mà trình biên dịch C sẽ không tôn trọng. Bạn cần thêm mã ở đầu quy trình ngôn ngữ lắp ráp để tải các giá trị từ ngăn xếp vào các thanh ghi dự kiến, ví dụ :
mov ecx, DWORD PTR [bmpSize]
mov esi, DWORD PTR [bmpBits]