Lý lịch
Tôi thích con chip 6502 8 bit cũ của tôi. Thật thú vị khi giải quyết một số thách thức ở đây trên PPCG bằng mã máy 6502. Nhưng một số thứ nên đơn giản (như, đọc trong dữ liệu hoặc xuất ra thiết bị xuất chuẩn) thì không cần thiết phải cồng kềnh trong mã máy. Vì vậy, có một ý tưởng sơ bộ trong đầu tôi: Phát minh ra máy ảo 8 bit của riêng tôi lấy cảm hứng từ 6502, nhưng với thiết kế được sửa đổi để có thể sử dụng nhiều hơn cho các thách thức. Bắt đầu thực hiện một cái gì đó, tôi nhận ra đây có thể là một thử thách thú vị nếu thiết kế của VM bị giảm đến mức tối thiểu :)
Bài tập
Triển khai máy ảo 8 bit tuân theo thông số kỹ thuật sau. Đây là môn đánh gôn , vì vậy việc thực hiện với ít byte nhất sẽ thắng.
Đầu vào
Việc triển khai của bạn cần có các đầu vào sau:
Một byte không dấu duy nhất
pc
, đây là bộ đếm chương trình ban đầu (địa chỉ trong bộ nhớ nơi VM bắt đầu thực thi,0
dựa trên cơ sở)Danh sách các byte có độ dài
256
mục nhập tối đa , đây là RAM cho máy ảo (có nội dung ban đầu)
Bạn có thể lấy đầu vào này ở bất kỳ định dạng hợp lý.
Đầu ra
Danh sách các byte là nội dung cuối cùng của RAM sau khi VM kết thúc (xem bên dưới). Bạn có thể giả sử bạn chỉ nhận được đầu vào dẫn đến chấm dứt cuối cùng. Bất kỳ định dạng hợp lý đều được cho phép.
CPU ảo
CPU ảo có
- bộ đếm chương trình 8 bit,
- một thanh ghi tích lũy 8 bit được gọi
A
và - một thanh ghi chỉ mục 8 bit được gọi
X
.
Có ba cờ trạng thái:
Z
- cờ zero được đặt sau khi một số kết quả hoạt động trong0
N
- cờ âm được đặt sau khi một số thao tác dẫn đến số âm (ví dụ: bit 7 của kết quả được đặt)C
- cờ thực hiện được đặt bằng các bổ sung và dịch chuyển cho bit "bị thiếu" của kết quả
Khi bắt đầu, tất cả các cờ được xóa, bộ đếm chương trình được đặt thành một giá trị nhất định và nội dung của A
và X
không xác định.
Các giá trị 8 bit đại diện cho một trong hai
- một số nguyên không dấu trong phạm vi
[0..255]
- một số nguyên đã ký , bổ sung 2, trong phạm vi
[-128..127]
tùy thuộc vào bối cảnh. Nếu một hoạt động vượt quá hoặc dưới mức, giá trị sẽ bao quanh (và trong trường hợp bổ sung, cờ mang bị ảnh hưởng).
Chấm dứt
Máy ảo chấm dứt khi
- Một
HLT
hướng dẫn đã đạt được - Một địa chỉ bộ nhớ không tồn tại được truy cập
- Bộ đếm chương trình chạy bên ngoài bộ nhớ (lưu ý rằng nó không bao quanh ngay cả khi VM được cung cấp đầy đủ 256 byte bộ nhớ)
Chế độ địa chỉ
- ngầm định - hướng dẫn không có đối số, toán hạng được ngụ ý
- ngay lập tức - toán hạng là byte trực tiếp sau lệnh
- tương đối - (chỉ dành cho phân nhánh) byte sau khi lệnh được ký (phần bù 2) và xác định phần bù để thêm vào bộ đếm chương trình nếu lấy nhánh.
0
là vị trí của hướng dẫn sau - tuyệt đối - byte sau lệnh là địa chỉ của toán hạng
- được lập chỉ mục - byte sau lệnh cộng
X
(thanh ghi) là địa chỉ của toán hạng
Hướng dẫn
Mỗi lệnh bao gồm một opcode (một byte) và, trong các chế độ địa chỉ ngay lập tức , tương đối , tuyệt đối và được lập chỉ mục một byte đối số thứ hai. Khi CPU ảo thực thi một lệnh, nó sẽ tăng bộ đếm chương trình tương ứng (bằng 1
hoặc 2
).
Tất cả các opcodes hiển thị ở đây là trong hex.
LDA
- tải toán hạng vàoA
- Opcodes: ngay lập tức :
00
, tuyệt đối :02
, được lập chỉ mục:04
- Flags:
Z
,N
- Opcodes: ngay lập tức :
STA
- lưu trữA
vào toán hạng- Opcodes: ngay lập tức :
08
, tuyệt đối :0a
, được lập chỉ mục:0c
- Opcodes: ngay lập tức :
LDX
- tải toán hạng vàoX
- Opcodes: ngay lập tức :
10
, tuyệt đối :12
, được lập chỉ mục:14
- Flags:
Z
,N
- Opcodes: ngay lập tức :
STX
- lưu trữX
vào toán hạng- Opcodes: ngay lập tức :
18
, tuyệt đối :1a
, được lập chỉ mục:1c
- Opcodes: ngay lập tức :
AND
- bitwise và củaA
và toán hạng thànhA
- Opcodes: ngay lập tức :
30
, tuyệt đối :32
, được lập chỉ mục:34
- Flags:
Z
,N
- Opcodes: ngay lập tức :
ORA
- bitwise hoặc củaA
và toán hạng thànhA
- Opcodes: ngay lập tức :
38
, tuyệt đối :3a
, được lập chỉ mục:3c
- Flags:
Z
,N
- Opcodes: ngay lập tức :
EOR
- bitwise xor (độc quyền hoặc) củaA
và toán hạng thànhA
- Opcodes: ngay lập tức :
40
, tuyệt đối :42
, được lập chỉ mục:44
- Flags:
Z
,N
- Opcodes: ngay lập tức :
LSR
- dịch chuyển logic sang phải, dịch chuyển tất cả các bit của toán hạng một vị trí sang phải, bit 0 sẽ mang theo- Opcodes: ngay lập tức :
48
, tuyệt đối :4a
, được lập chỉ mục:4c
- Flags:
Z
,N
,C
- Opcodes: ngay lập tức :
ASL
- dịch chuyển số học sang trái, dịch chuyển tất cả các bit của toán hạng một vị trí sang trái, bit 7 sẽ mang theo- Opcodes: ngay lập tức :
50
, tuyệt đối :52
, được lập chỉ mục:54
- Flags:
Z
,N
,C
- Opcodes: ngay lập tức :
ROR
- xoay phải, dịch chuyển tất cả các bit của toán hạng một vị trí sang phải, chuyển sang bit 7, bit 0 chuyển sang mang- Opcodes: ngay lập tức :
58
, tuyệt đối :5a
, được lập chỉ mục:5c
- Flags:
Z
,N
,C
- Opcodes: ngay lập tức :
ROL
- xoay trái, dịch chuyển tất cả các bit của toán hạng một vị trí sang trái, chuyển sang bit 0, bit 7 chuyển sang mang- Opcodes: ngay lập tức :
60
, tuyệt đối :62
, được lập chỉ mục:64
- Flags:
Z
,N
,C
- Opcodes: ngay lập tức :
ADC
- thêm với carry, toán hạng cộng với carry được thêm vàoA
, carry được đặt khi tràn- Opcodes: ngay lập tức :
68
, tuyệt đối :6a
, được lập chỉ mục:6c
- Flags:
Z
,N
,C
- Opcodes: ngay lập tức :
INC
- toán hạng tăng thêm một- Opcodes: ngay lập tức :
78
, tuyệt đối :7a
, được lập chỉ mục:7c
- Flags:
Z
,N
- Opcodes: ngay lập tức :
DEC
- toán hạng giảm dần bởi một- Opcodes: ngay lập tức :
80
, tuyệt đối :82
, được lập chỉ mục:84
- Flags:
Z
,N
- Opcodes: ngay lập tức :
CMP
- so sánhA
với toán hạng bằng cách trừ toán hạng từA
, quên kết quả. Thực hiện được xóa trên dòng chảy, thiết lập khác- Opcodes: ngay lập tức :
88
, tuyệt đối :8a
, được lập chỉ mục:8c
- Flags:
Z
,N
,C
- Opcodes: ngay lập tức :
CPX
- so sánhX
- tương tự nhưCMP
đối vớiX
- Opcodes: ngay lập tức :
90
, tuyệt đối :92
, được lập chỉ mục:94
- Flags:
Z
,N
,C
- Opcodes: ngay lập tức :
HLT
- chấm dứt- Opcodes: ẩn:
c0
- Opcodes: ẩn:
INX
- tăng thêmX
một- Opcodes: ẩn:
c8
- Flags:
Z
,N
- Opcodes: ẩn:
DEX
- giảmX
bởi một- Opcodes: ẩn:
c9
- Flags:
Z
,N
- Opcodes: ẩn:
SEC
- đặt cờ mang- Opcodes: ẩn:
d0
- Cờ:
C
- Opcodes: ẩn:
CLC
- xóa cờ mang- Opcodes: ẩn:
d1
- Cờ:
C
- Opcodes: ẩn:
BRA
- Chi nhánh luôn.- Opcodes: tương đối:
f2
- Opcodes: tương đối:
BNE
- nhánh nếuZ
xóa cờ- Opcodes: tương đối:
f4
- Opcodes: tương đối:
BEQ
- nhánh nếuZ
đặt cờ- Opcodes: tương đối:
f6
- Opcodes: tương đối:
BPL
- nhánh nếuN
xóa cờ- Opcodes: tương đối:
f8
- Opcodes: tương đối:
BMI
- nhánh nếuN
đặt cờ- Opcodes: tương đối:
fa
- Opcodes: tương đối:
BCC
- nhánh nếuC
xóa cờ- Opcodes: tương đối:
fc
- Opcodes: tương đối:
BCS
- nhánh nếuC
đặt cờ- Opcodes: tương đối:
fe
- Opcodes: tương đối:
Mã nguồn
Hành vi của VM không được xác định nếu tìm thấy bất kỳ opcode nào không ánh xạ tới một hướng dẫn hợp lệ từ danh sách trên.
Theo yêu cầu của Jonathan Allan , bạn có thể chọn bộ opcodes của riêng mình thay vì opcodes được hiển thị trong phần Hướng dẫn . Nếu bạn làm như vậy, bạn phải thêm một ánh xạ đầy đủ vào các opcode được sử dụng ở trên trong câu trả lời của bạn.
Ánh xạ phải là một tệp hex với các cặp <official opcode> <your opcode>
, ví dụ: nếu bạn thay thế hai opcodes:
f4 f5
10 11
Dòng mới không quan trọng ở đây.
Các trường hợp thử nghiệm (opcodes chính thức)
// some increments and decrements
pc: 0
ram: 10 10 7a 01 c9 f4 fb
output: 10 20 7a 01 c9 f4 fb
// a 16bit addition
pc: 4
ram: e0 08 2a 02 02 00 6a 02 0a 00 02 01 6a 03 0a 01
output: 0a 0b 2a 02 02 00 6a 02 0a 00 02 01 6a 03 0a 01
// a 16bit multiplication
pc: 4
ram: 5e 01 28 00 10 10 4a 01 5a 00 fc 0d 02 02 d1 6a 21 0a 21 02 03 6a 22 0a 22 52
02 62 03 c9 f8 e6 c0 00 00
output: 00 00 00 00 10 10 4a 01 5a 00 fc 0d 02 02 d1 6a 21 0a 21 02 03 6a 22 0a 22 52
02 62 03 c9 f8 e6 c0 b0 36
Tôi có thể thêm nhiều testcase sau.
Tham khảo và thử nghiệm
Để trợ giúp với các thử nghiệm của riêng mình, đây là một số triển khai tham chiếu (hoàn toàn không được đánh gôn) - nó có thể xuất thông tin theo dõi (bao gồm các hướng dẫn tháo rời) sang stderr
và chuyển đổi các opcodes trong khi chạy.
Cách được đề xuất để có được nguồn:
git clone https://github.com/zirias/gvm --branch challenge --single-branch --recurse-submodules
Hoặc kiểm tra chi nhánh challenge
và thực hiện git submodule update --init --recursive
sau khi nhân bản, để có được hệ thống xây dựng tùy chỉnh của tôi.
Xây dựng công cụ với GNU make (chỉ cần gõ make
hoặc gmake
nếu trên hệ thống của bạn, mặc định make không phải là GNU make).
Cách sử dụng :gvm [-s startpc] [-h] [-t] [-c convfile] [-d] [-x] <initial_ram
-s startpc
- bộ đếm chương trình ban đầu, mặc định là0
-h
- đầu vào ở dạng hex (nếu không là nhị phân)-t
- thực hiện theo dõi đểstderr
-c convfile
- chuyển đổi opcodes theo một ánh xạ được đưa ra trongconvfile
-d
- kết xuất bộ nhớ dưới dạng dữ liệu nhị phân-x
- kết xuất bộ nhớ dưới dạng hexinitial_ram
- nội dung RAM ban đầu, dưới dạng hex hoặc nhị phân
Lưu ý tính năng chuyển đổi sẽ thất bại trên các chương trình sửa đổi opcodes trong khi chạy.
Tuyên bố từ chối trách nhiệm: Các quy tắc và thông số kỹ thuật ở trên là có thẩm quyền cho thử thách, không phải công cụ này. Điều này đặc biệt áp dụng cho tính năng chuyển đổi opcode. Nếu bạn nghĩ rằng công cụ được trình bày ở đây có lỗi ghi thông số kỹ thuật, vui lòng báo cáo trong một nhận xét :)
BRA
("nhánh luôn") không giới thiệu một nhánh trong luồng điều khiển, nó có nên được gọi JMP
không?
BRA
tồn tại trong các thiết kế chip sau này (6502 không có hướng dẫn như vậy) như 65C02 và MC 68000. cũng JMP
tồn tại. Sự khác biệt là BRA
sử dụng địa chỉ tương đối và JMP
sử dụng địa chỉ tuyệt đối. Vì vậy, tôi chỉ làm theo những thiết kế này - thực sự, nó không có vẻ hợp lý lắm;)