Tự viết Emulator: CHIP-8 Interpreter (Phần 1)
Emulator
2
Giả lập
2
White

Huy Trần viết ngày 09/10/2015

Nhắc đến game giả lập, chắc không ai lạ gì và ai cũng từng chơi qua (giả lập NES, Gameboy, PS1, PS2,...)

Và chắc hẳn, cũng có không ít bạn nung nấu ý định tự viết một bộ emulator cho riêng mình nhưng không biết bắt đầu từ đâu.

Mình cũng vậy, luôn mơ có một ngày sẽ tự viết được một bộ giả lập cho NES, Gameboy hay thậm chí là Play Station :)) nhưng vẫn dậm chân tại chỗ trong nhiều năm trời chỉ vì không biết phải bắt đầu như thế nào.

Những tài liệu, bài viết nói về giả lập NES hay Gameboy tràn lan trên mạng rất nhiều, nhưng đa số đều bị mình vứt qua một bên vì đọc không hiểu gì cả :sweat_smile: thế nên mình quyết định sẽ tạm gác ý định giả lập máy NES hay Gameboy sang một bên và bắt đầu với một loại đơn giản hơn - đó là CHIP-8.

alt text

Loạt bài này sẽ có 3 phần:

  1. Phần 1: Giới thiệu CHIP-8 và các khái niệm cơ bản
  2. Phần 2: Tập lệnh của CHIP-8
  3. Phần 3: Implement

Các bạn chỉ thực sự đụng đến code từ phần 3, dục tốc bất đạt, vậy nên, cứ bình tĩnh và từ từ mà nuốt trôi hết cái đống khái niệm này. Không thừa đâu :D


Vậy CHIP-8 là cái gì? tại sao không phải NES, Gameboy mà lại là CHIP-8?

Phần 1: Một số khái niệm cơ bản

Giả lập là gì?

Thực ra chắc không ai thắc mắc cái này đâu, nhưng đề phòng lỡ có ai thắc mắc thì: Giả lập là một phần mềm mô phỏng lại hoạt động của một cái gì đó. Trong trường hợp này, chúng ta đang chuẩn bị viết phần mềm mô phỏng hoạt động của trình thông dịch CHIP-8 (CHIP-8 interpreter)

CHIP-8 là gì?

CHIP-8 là một ngôn ngữ thông dịch (interpreted programming language), thiết kế bởi Joseph Weisbecker, được dùng cho các máy tính 8-bit COSMAC VIP và Telmac 1800 vào những năm 1970.

Máy tính Telmac 1800

Các máy tính này có đầy đủ các thành phần như màn hình (2 màu đen trắng), âm thanh và bàn phím (nhưng không phải bàn phím QWERTY như bây giờ).

COSMAC VIP 4 và quả bàn phím khác người của nó

Ngày nay, CHIP-8 vẫn còn được sử dụng trong cộng đồng hobby, với sự đơn giản về cấu trúc cũng như tập lệnh nhỏ (chỉ có 36 lệnh), CHIP-8 Interpreter (thường bị nhầm lẫn là CHIP-8 Emulator) đã trở thành ứng dụng được đa số các lập trình viên lựa chọn khi muốn bắt đầu con đường lập trình emulator của mình.

Tại sao lại chọn CHIP-8?

Sở dĩ chọn CHIP-8 thay vì NES hay Gameboy là vì cấu trúc đơn giản của nó. Cấu trúc chỉ có một bộ xử lý và số tập lệnh cực kì ít, chưa kể đến khi làm việc với các hệ máy khác, bạn còn phải đau đầu với việc giả lập những module khác, không hề thích hợp cho người mới bắt đầu.

Ngược lại, sau khi hoàn thành được một bản CHIP-8 emulator cho riêng mình, bạn sẽ có đủ kiến thức cơ bản để tiếp tục nghiên cứu các hệ máy khác như NES hay Gameboy.

Chuẩn bị kiến thức

Để bắt đầu con đường lập trình Emulator, bạn chắc chắn cần phải có các kiến thức về:

  • Các phép toán thao tác bit (bitwise operation)
  • Mã máy (Assembly)

Nếu đọc đến đây mà bạn đã bắt đầu thấy ngại vì không có những kiến thức này thì cũng không sao. Cứ đọc tiếp, trong bài mình sẽ cố gắng giải thích thật kĩ các khái niệm cần thiết. :trollface:

ROM và nguyên lý hoạt động của CHIP-8

ROM là gì?

Khi sử dụng bất cứ một phần mềm giả lập cho bất kì hệ mày gì, chúng ta đều nhắc đến khái niệm ROM.

Hiểu đơn giản, ROM là một tập hợp các lệnh thực thi, còn gọi là opcode (operation code). Các opcode này là các lệnh được hỗ trợ bởi bộ xử lý.

Ví dụ, đối với CHIP-8, opcode có mã hex là 0x00E0 làm nhiệm vụ xoá toàn bộ màn hình, opcode có mã hex 0xD123 có nhiệm vụ vẽ cái gì đó ra màn hình.

Nguyên lý hoạt động của CHIP-8

Khi hoạt động, trình thông dịch (intepreter) CHIP-8 sẽ đọc các opcode từ ROM, đưa vào bộ nhớ (RAM) rồi CPU sẽ chạy qua từng opcode đã đọc được và thực thi chúng. Dữ liệu nếu cần sẽ được ghi trở lại vào RAM.

Tóm tắt có thể xem trong hình sau:

Giả lập mà chúng ta sắp viết, sẽ mô phỏng đúng y quá trình này.

Các thành phần cơ bản của một thiết bị CHIP-8

Một thiết bị sử dụng CHIP-8 có các thành phần cơ bản sau:

  • Bộ nhớ (memory)
  • Các thanh ghi (registers)
  • Màn hình hiển thị
  • Bàn phím, Timers.

Và nhiệm vụ của chúng ta là mô phỏng các thành phần này. Vì thế, chúng ta phải biết cấu trúc, nhiệm vụ, chức năng của mỗi phần.

Memory

Bộ nhớ cần dùng của một thiết bị CHIP-8 là 4KB (4096 bytes). Được đánh số từ 0x000 (0) đến 0xFFF (4095).

alt text

512 bytes đầu tiên (từ 0x000 đến 0x1FF) là vị trí của trình thông dịch (interpreter) và chương trình của chúng ta không thể truy xuất đến các địa chỉ này.

Bắt đầu từ vị trí 0x200 cho đến 0xFFF là nơi mà chương trình của chúng ta được nạp vào.

Và interpreter sẽ bắt đầu chạy chương trình từ vị trí 0x200 (trên các máy ETI 660 thì điểm bắt đầu sẽ là 0x600)

Registers

CHIP-8 có 16 thanh ghi đa dụng (general purpose registers) 8 bit, thường được đánh số là Vx, với x là các số hexa từ 0 đến F.

Thanh ghi VF mặc dù tồn tại nhưng chương trình không thể sử dụng vì nó được dùng làm cờ (flag) cho một số lệnh. Cái này mình sẽ giải thích ở bài sau, khi đi vào chi tiết các lệnh.

Tiếp đến là thanh ghi I, được dùng để lưu các địa chỉ trong bộ nhớ. Ở phần sau chúng ta sẽ hiểu rõ cơ chế hoạt động của thanh ghi này hơn.

Ngoài ra còn có 2 timer đặc biệt là Delay Timer và Sound Timer, sẽ nói ở bên dưới. Các register này chứa một giá trị 8 bit.

Keyboard

Bàn phím của máy tính sử dụng CHIP-8 thường có dạng 16 phím hexa. Khi viết emulator, chúng ta nên map các phím này vào bàn phím máy tính bây giờ. Như hình sau:

Bên trái là bàn phím hexa, bên phải là bàn phím máy tính thời bây giờ
(Bên trái là bàn phím hexa thời cổ đại, bên phải là bàn phím máy tính thời bây giờ)

Việc map phím như thế nào là tuỳ ý các bạn, nhưng trong bài viết này sẽ map như vậy, nên code cũng sẽ theo layout này.

Display

Màn hình trên các thiết bị CHIP-8 là màn hình đơn sắc (monochrome), có độ phân giải 64x32 pixel.

alt text

Một số máy ETI 660 có độ phân giải 64x48 hoặc 64x64 pixel.

CHIP-8 vẽ các hình ảnh lên màn hình thông qua các sprites. Sprite là một tập hợp nhiều bytes chuyển từ dữ liệu binary tương ứng với hình ảnh mà chúng ta cần vẽ. Mỗi byte có 8 bit, như vậy một sprite sẽ có kích thước 8x*N* (trong đó N là tuỳ ý, từ 1 đến 15)

Các CHIP-8 interpreter thường có một bộ font cài đặt sẵn (built-in), được lưu trong memory (ở vùng 0x000 - 0x1FF) mỗi kí tự trong bộ font này thực chất là các sprite có kích thước 8x5 (tức là có 5 bytes).

alt text

Timers và Sound

Timer, đúng theo tên gọi của nó, thì là một cái bộ đếm (bên điện tử nhiều người gọi là bộ định thời). Chức năng của nó đơn giản là: khi được kích hoạt, nó sẽ bắt đầu đếm ngược theo một chu kì thời gian nào đó, đến một lúc nào đó nó sẽ ngừng.

Trong CHIP-8, chúng ta có 2 timers: Delay timerSound timer

Delay timer là một bộ đếm được kích hoạt khi giá trị của thanh ghi Delay Timer (DT) - đã đề cập ở phần Register, xem hình - là khác 0. Giá trị này sẽ tự động giảm (trừ 1) liên tục theo chu kì có tần số 60Hz (60 lần 1 giây). Khi nào nó về 0 thì timer sẽ ngưng hoạt động.

alt text

Mục đích của Delay timer là làm cho chương trình ngừng lại trong một khoảng thời gian, giống như các hàm Delay() hay Sleep() ở các ngôn ngữ lập trình khác.

Sound timer cũng tương tự như Delay timer, khi giá trị của thanh ghi Sound Timer (ST) khác 0 thì nó sẽ tự động kích hoạt, và giảm giá trị này theo chu kì tần số 60Hz, rồi ngưng khi nó về 0. Nhưng điểm khác biệt là, chừng nào giá trị trong thanh ghi ST khác 0 thì cái loa (buzzer) của máy sẽ hú :))

alt text


Phù!!! Thế là cũng xong phần lý thuyết. Dài quá nhưng đề nghị các bạn đừng có nhảy cóc. Vì trên đây là những khái niệm rất quan trọng cần phải có trước khi bắt tay vào viết Emulator.

Hy vọng các bạn đã hiểu được cơ chế hoạt động và phần nào hình dung ra được hướng đi mà chúng ta đang thực hiện.

Ở phần tiếp theo, mình sẽ giới thiệu và giải thích tập lệnh (instruction set) của CHIP-8, và nói sơ về công dụng của chúng trong thực tế. Từ đó, chúng ta có thể đi sang phần 3, đó là bắt tay vào code và hoàn thành dự án.

Bình luận


White
{{ comment.user.name }}
Bỏ hay Hay
{{comment.like_count}}
Male avatar
{{ comment_error }}
Hủy
   

Hiển thị thử

Chỉnh sửa

White

Huy Trần

115 bài viết.
1764 người follow
Kipalog
{{userFollowed ? 'Following' : 'Follow'}}
Cùng một tác giả
White
168 46
Tại sao phải viết blog kĩ thuật? Có rất nhiều bài viết trên mạng nói về vấn đề tại sao một lập trình viên nên thường xuyên viết các bài blog kĩ thu...
Huy Trần viết hơn 3 năm trước
168 46
White
151 39
(Ảnh) Tiếp tục sêri (Link) lần này, chúng ta sẽ cùng tìm hiểu và mô phỏng lại một chức năng mà mọi người đang bắt đầu sử dụng hằng ngày, đó là chứ...
Huy Trần viết hơn 2 năm trước
151 39
White
108 17
Phần 1: Tự truyện Tui và Toán đã từng là hai kẻ thù không đội trời chung trong suốt hơn mười lăm năm ròng rã. Ngay từ ánh nhìn đầu tiên đã ghét nh...
Huy Trần viết hơn 2 năm trước
108 17
Bài viết liên quan
White
4 8
Ở phần trước, chúng ta đã nắm được các khái niệm cơ bản cần có của một hệ thống CHIP8. Phần này chúng ta sẽ tìm hiểu về cách hoạt động của trình th...
Huy Trần viết 3 năm trước
4 8
{{like_count}}

kipalog

{{ comment_count }}

bình luận

{{liked ? "Đã kipalog" : "Kipalog"}}


White
{{userFollowed ? 'Following' : 'Follow'}}
115 bài viết.
1764 người follow

 Đầu mục bài viết

Vẫn còn nữa! x

Kipalog vẫn còn rất nhiều bài viết hay và chủ đề thú vị chờ bạn khám phá!