2010-06-19 18 views
5

Mục tiêu: Tôi muốn viết một bộ hợp x86_64. Lưu ý: được đánh dấu là wiki cộng đồngLàm thế nào để viết một X86_64 _assembler_?

Thông tin cơ bản: Tôi quen thuộc với C. Tôi đã viết bản MIPS trước đây. Tôi đã viết một số x86 lắp ráp. Tuy nhiên, tôi muốn viết một bộ x86_64 - nó sẽ xuất ra mã máy mà tôi có thể nhảy đến và bắt đầu thực hiện (như trong một JIT).

Câu hỏi là: cách tốt nhất để tiếp cận điều này là gì? Tôi nhận ra vấn đề này trông khá lớn để giải quyết. Tôi muốn bắt đầu với một thiết lập cơ bản tối thiểu:

  • tải vào đăng ký
  • Arithmetric ops trên thanh ghi (chỉ số nguyên là tốt, không cần phải gây rối với FPU chưa)
  • Conditionals
  • nhảy

Chỉ một bộ cơ bản để làm cho Turing hoàn tất. Bất cứ ai làm điều này? Gợi ý/tài nguyên?

+1

Bạn đã cân nhắc sử dụng một trình kết hợp hiện có như ['gas'] (http://en.wikipedia.org/wiki/GNU_Assembler) hay [' nasm'] (http://nasm.us)? [Chương 11] (http://www.nasm.us/doc/nasmdo11.html) của tài liệu NASM mô tả các tính năng x86-64 của nó. –

+1

Để kết thúc trở lại, hãy lấy một bản sao của sổ tay AMD (nó trực tuyến miễn phí). Đối với giao diện người dùng, kiến ​​thức cơ bản về trình biên dịch sẽ là đủ, vì x86 (và amd64) lắp ráp đặc biệt dễ phân tích cú pháp. –

Trả lời

8

Một trình biên dịch, giống như bất kỳ trình biên dịch nào khác, được viết tốt nhất như là một trình phân tích từ vựng đưa vào bộ xử lý ngữ pháp ngôn ngữ.

Ngôn ngữ lắp ráp thường dễ dàng hơn các ngôn ngữ được biên dịch thông thường vì bạn không cần phải lo lắng về các cấu trúc vượt qua ranh giới đường và định dạng thường được sửa.

tôi đã viết một lắp ráp cho một (hư cấu) CPU khoảng hai năm trước cho các mục đích giáo dục và về cơ bản điều trị mỗi dòng như:

  • nhãn bắt buộc (ví dụ, :loop).
  • Hoạt động
  • (ví dụ: mov).
  • toán hạng (ví dụ: ax,$1).

Cách dễ nhất để làm điều đó là đảm bảo rằng mã thông báo có thể dễ dàng phân biệt được.

Đó là lý do tại sao tôi thực hiện quy tắc mà nhãn phải bắt đầu bằng : - nó đã làm cho việc phân tích dòng dễ dàng hơn rất nhiều. Quá trình xử lý một dòng là:

  • xoá nhận xét (đầu tiên ; ngoài chuỗi để kết thúc dòng).
  • nhãn trích xuất nếu có.
  • từ đầu tiên sau đó là hoạt động.
  • nghỉ ngơi là các toán hạng.

Bạn có thể dễ dàng nhấn mạnh rằng các toán hạng khác nhau cũng có các điểm đánh dấu đặc biệt để làm cho cuộc sống của bạn dễ dàng hơn. Tất cả điều này là giả sử bạn có quyền kiểm soát định dạng đầu vào. Nếu bạn được yêu cầu sử dụng định dạng Intel hoặc AT & T, điều này sẽ khó khăn hơn một chút.

Con đường tôi tiếp cận nó là rằng có một đơn giản chức năng cho mỗi hoạt động đó đã gọi (ví dụ, doJmp, doCall, doRet) và chức năng quyết định những gì được cho phép trong các toán hạng.Ví dụ: doCall chỉ cho phép một số hoặc nhãn, doRet không cho phép gì.

Ví dụ, đây là một đoạn mã từ encInstr chức năng:

private static MultiRet encInstr(
    boolean ignoreVars, 
    String opcode, 
    String operands) 
{ 
    if (opcode.length() == 0) return hlprNone(ignoreVars); 
    if (opcode.equals("defb")) return hlprByte(ignoreVars,operands); 
    if (opcode.equals("defbr")) return hlprByteR(ignoreVars,operands); 
    if (opcode.equals("defs")) return hlprString(ignoreVars,operands); 
    if (opcode.equals("defw")) return hlprWord(ignoreVars,operands); 
    if (opcode.equals("defwr")) return hlprWordR(ignoreVars,operands); 
    if (opcode.equals("equ")) return hlprNone(ignoreVars); 
    if (opcode.equals("org")) return hlprNone(ignoreVars); 

    if (opcode.equals("adc")) return hlprTwoReg(ignoreVars,0x0a,operands); 
    if (opcode.equals("add")) return hlprTwoReg(ignoreVars,0x09,operands); 
    if (opcode.equals("and")) return hlprTwoReg(ignoreVars,0x0d,operands); 

Các hlpr... chức năng chỉ đơn giản là các toán hạng và trở về một mảng byte chứa các hướng dẫn. Chúng hữu ích khi nhiều thao tác có yêu cầu toán hạng tương tự, chẳng hạn như adc , thêm and và` tất cả yêu cầu hai toán hạng đăng ký trong trường hợp trên (tham số thứ hai kiểm soát opcode được trả về cho lệnh).

Bằng cách làm cho các loại toán hạng dễ phân biệt, bạn có thể kiểm tra xem toán hạng nào được cung cấp, cho dù chúng là hợp pháp và chuỗi byte nào tạo ra. Việc tách các hoạt động thành các hàm riêng của chúng cung cấp một cấu trúc lôgic tốt đẹp. Ngoài ra, hầu hết các CPU theo một bản dịch hợp lý từ opcode đến hoạt động (để làm cho các nhà thiết kế chip sống dễ dàng hơn) do đó sẽ có các tính toán tương tự trên tất cả các mã cho phép, ví dụ, địa chỉ được lập chỉ mục.

Để tạo mã đúng cách trong CPU cho phép các hướng dẫn có độ dài thay đổi, tốt nhất bạn nên làm trong hai lần.

Trong lần truy cập đầu tiên, không tạo mã, chỉ cần tạo độ dài của hướng dẫn. Điều này cho phép bạn gán các giá trị cho tất cả các nhãn khi bạn gặp chúng. Pass thứ hai sẽ tạo ra mã và có thể điền vào các tham chiếu tới các nhãn đó vì các giá trị của chúng được biết đến. ignoreVars trong đoạn mã trên được sử dụng cho mục đích này (chuỗi byte mã được trả về để chúng tôi có thể biết độ dài nhưng bất kỳ tham chiếu nào đến các ký hiệu vừa được sử dụng 0).

6

Không khuyến khích bạn, nhưng đã có many assemblers với nhiều chuông và còi khác nhau. Vui lòng xem xét đóng góp cho một dự án nguồn mở hiện có như elftoolchain.

+0

Tôi muốn cung cấp nhiều hơn +1 cho một điểm quan trọng: đóng góp cho những nỗ lực hiện có hơn là bắt đầu lặp lại ... (trừ khi cho mục đích giáo dục, nhưng đóng góp là giáo dục quá ...) – ShinTakezou

+0

Có rất ít có thể sử dụng như thư viện tuy nhiên, và thậm chí ít hơn nếu bạn muốn một cái gì đó mà không xem xét mục tiêu chính của bạn (Windows) như vịt con xấu xí hoặc không có giấy phép virus –

Các vấn đề liên quan