ย้อนกลับไปในยุค 80 บริษัทที่ไม่รู้จักชื่อ Binary Systems ได้เผยแพร่เกม Starflight เกมดังกล่าวทำให้ผู้เล่นสวมบทบาทเป็นกัปตันยานอวกาศที่ถูกส่งไปสำรวจกาแลคซี ไม่มีเส้นทางที่กำหนดไว้ ทำให้ผู้เล่นสามารถสลับระหว่างการขุด การต่อสู้ระหว่างเรือ และการทูตจากต่างดาวได้อย่างอิสระ โครงเรื่องที่กว้างขึ้นของเกมปรากฏอย่างช้าๆ ในขณะที่ผู้เล่นค้นพบว่าเผ่าพันธุ์แห่งสิ่งมีชีวิตโบราณกำลังทำให้ดวงดาวลุกเป็นไฟและทำลายสิ่งมีชีวิตทั้งหมด เกมดังกล่าวได้รับการยกย่องอย่างกว้างขวางจากนักวิจารณ์ทั้งในปัจจุบันและสมัยใหม่ และเป็นหนึ่งในเกมแรกสุดของเกมแซนด์บ็อกซ์ เกมดังกล่าวมีอิทธิพลต่อการออกแบบเกมอื่นๆ มากมายมานานหลายทศวรรษหลังจากวางจำหน่าย
หากต้องการข้อมูลเพิ่มเติมเกี่ยวกับเกม โปรดตรวจสอบลิงก์ต่อไปนี้:
คุณสามารถซื้อเกมได้ที่ GoG
ครั้งแรกที่ฉันได้ยินเกี่ยวกับเกมฉันก็อยากเล่นมัน อย่างไรก็ตาม ฉันยังเด็กเกินไปและพูดภาษาอังกฤษไม่ได้ 20 ต่อมาฉันลองอีกครั้ง และมันก็เป็นประสบการณ์ที่น่าพึงพอใจมาก การสำรวจนั้นสนุก โครงเรื่องยิ่งใหญ่และจบลงด้วยความประหลาดใจ นั่นเป็นหนึ่งในสิ่งที่ดีที่สุดที่ฉันเคยพบมา แน่นอนว่าเกมยังไม่เก่ามากนัก แต่คุณสามารถสัมผัสได้ถึงความทุ่มเทของผู้พัฒนาเกม เกมนี้มีความเป็นศิลปะเช่นเดียวกับความใส่ใจในรายละเอียดของช่างฝีมือ
การเล่นเกมที่น่าทึ่งอย่างแท้จริงนี้เป็นเรื่องสนุกพอ ๆ กับการเล่นเกมวิศวกรรมย้อนกลับนี้ คุณเดินตามรอยของนักพัฒนาและสัมผัสกับกระบวนการคิดของพวกเขาราวกับว่าเป็นปี 1985 อีกครั้ง สำหรับเกมนี้คาดหวังสิ่งที่ไม่คาดคิด โดยปกติเมื่อคุณทำวิศวกรรมย้อนกลับกับเกมเก่าๆ ดังกล่าว คุณจะต้องได้รับโค้ดแอสเซมเบลอร์แท้นับหมื่นบรรทัด ซึ่งคุณสามารถวิเคราะห์ด้วยเครื่องมือทั่วไป เช่น IDA Pro แต่ไม่ใช่ครั้งนี้ จริงๆ แล้วสำหรับเกมนี้ คุณสามารถทิ้งเครื่องมือธรรมดาๆ ทิ้งไปได้ พวกเขาไร้ประโยชน์ คุณอยู่ด้วยตัวคุณเอง เหตุผลก็คือ Starflight เขียนด้วยภาษา Forth ซึ่งเป็นภาษาที่ฉันแทบไม่รู้เลย
Forth เป็นภาษาที่มีความเรียบง่ายที่สุดเกี่ยวกับไวยากรณ์ ไม่มีไวยากรณ์ใดมากไปกว่าช่องว่างระหว่าง "คำ" คุณสามารถเขียนโปรแกรมอ่านและล่าม Forth โดยใช้โค้ดเพียงไม่กี่บรรทัด
ในภาษาสมัยใหม่คุณเขียนอะไรทำนองนี้
print ( 2 + 3 )
เพื่อพิมพ์ผลลัพธ์ของ 2+3 ใน Forth มีลักษณะเช่นนี้
2 3 + .
Forth เป็นเครื่องเรียงซ้อนซึ่งมีสัญลักษณ์โปแลนด์แบบย้อนกลับ การตีความมีดังนี้
ไวยากรณ์นั้นเรียบง่ายและล่ามก็เรียบง่าย "2", "3", "+" และ "." เป็นเพียง "คำพูด" ไม่มีความแตกต่างทางวากยสัมพันธ์ระหว่างข้อมูลและโค้ด แน่นอนว่าเป็นภาษาที่รองรับข้อจำกัดของคอมพิวเตอร์ในบ้านยุคแรกๆ
เมื่อคุณวิเคราะห์ STARFLT.COM ที่สามารถเรียกใช้งานได้ มันจะเผยให้เห็นระบบภายในที่น่าอัศจรรย์บางอย่าง
ตามที่อธิบายไว้ข้างต้น Forth เป็นเครื่องสแต็ก เนื่องจากกลไกการเขียนโค้ดนั้น ใช้เธรดทางอ้อม ซึ่งเป็นวิธีที่มีประสิทธิภาพในการใช้พื้นที่ในการจัดเก็บโค้ดที่คอมไพล์แล้ว โค้ดเธรดมีรูปแบบที่ประกอบด้วยการเรียกรูทีนย่อยทั้งหมด เธรดทางอ้อมใช้พอยน์เตอร์ไปยังตำแหน่งที่ชี้ไปที่รหัสเครื่อง
สมมติว่าตัวชี้คำสั่งของคุณชี้ไปยังที่อยู่ 0x1000 และมีค่า 16 บิต Read16(0x1000)=0x0f72
0x1000 : dw 0x0f72
ค่า 0x0f72 เป็นรหัสที่เทียบเท่ากับคำที่สี่ '+' จำคำอธิบายข้างต้น คำว่า '+' จะแสดงรายการสแต็กสองรายการสุดท้าย เพิ่มเข้าด้วยกัน และผลักผลลัพธ์กลับไปที่ด้านบนของสแต็ก ตามเธรดทางอ้อมค่า 16 บิตนี้ 0x0f72 เป็นตัวชี้ไปยังตำแหน่งที่จะชี้ไปที่รหัสเครื่อง เมื่อคุณอ่านเนื้อหาหน่วยความจำ Read16(0x0f72) คุณจะได้รับตัวชี้ไปที่ 0x0f74 และแน่นอน เมื่อคุณดูตำแหน่งหน่วยความจำนี้และแยกชิ้นส่วนออก คุณจะได้รับสิ่งต่อไปนี้
0x0f72 : dw 0x0f74
0x0f74 : pop ax
0x0f75 : pop bx
0x0f76 : add ax , bx
0x0f78 : push ax
0x0f79 : lodsw
0x0f7a : mov bx , ax
0x0f7c : jmp word ptr [ bx ]
คำสั่งสี่คำสั่งแรกดำเนินการตามที่คำว่า "+" ควรทำทุกประการ คำสั่งแอสเซมเบลอร์สามคำสั่งสุดท้ายที่เริ่มต้นจาก "lodsw" จะเพิ่มตัวชี้คำสั่งและข้ามไปยังโค้ดถัดไป
ให้เราดำเนินต่อไป ตอนนี้ตัวชี้คำสั่งชี้ไปที่ 0x1002
0x1002 : dw 0x53a3
การอ่านที่อยู่ 0x53a3 เผยให้เห็น
0x53a3 : dw 0x1d29
0x53a5 : dw 0x0001
และรหัสที่เกี่ยวข้อง
0x1d29 : inc bx
0x1d2a : inc bx
0x1d2b : push bx
0x1d2c : lodsw
0x1d2d : mov bx , ax
0x1d2f : jmp word ptr [ bx ]
ในขณะนี้ register bx มีคำว่า address 0x53a3 ดังนั้นโค้ดนี้จึงพุชที่อยู่ 0x53a5 ไว้ด้านบนของสแต็ก สิ่งที่เราทำคือการจัดเตรียมตัวชี้ให้โปรแกรมไปยังตัวแปร ตัวแปรมีเนื้อหา 0x0001 คำที่สี่ '@' จะแสดงที่อยู่จากสแต็ก อ่านเนื้อหา และดันกลับเข้าไปในสแต็ก
จนถึงตอนนี้ฉันสามารถระบุคำได้ 6,256 คำที่มีรหัสหรือข้อมูล
และนั่นคือทั้งหมดที่คุณต้องรู้เกี่ยวกับโครงสร้างโค้ด อย่างที่คุณเห็นว่านี่อาจเป็นการเข้ารหัสที่มีประสิทธิภาพด้านพื้นที่ แต่เมื่อเร่งความเร็วกลับกลายเป็นหายนะ คำสั่งรหัสเครื่องทุกสองสามคำสั่งคุณต้องข้ามไปยังบล็อคโค้ดอื่น
การเทียบเท่าของเธรดทางอ้อมในภาษา C จะมีลักษณะเช่นนี้
uint16_t instruction_pointer = start_of_program_pointer ;
void Call ( uint16_t word_adress )
{
// the first two byte of the word's address contain
// the address of the corresponding code, which must be executed for this word
uint16_t code_address = Read16 ( word_address );
switch ( code_address )
{
.
.
.
case 0x0f74 : // word '+'
Push16 ( Pop16 () + Pop16 ());
break ;
.
.
.
}
}
void Run ()
{
while ( 1 )
{
uint16_t word_address = Read16 ( instruction_pointer );
instruction_pointer += 2 ;
Call ( word_address );
}
}
รหัสที่ดำเนินการสำหรับคำเฉพาะสามารถเข้าถึงตัวแปรหลัก 5 ตัว (16 บิต)
การแยกส่วนจะแปลงรหัส FORTH ไปเป็นโค้ดแบบ C .. โค้ดที่แปลงส่วนใหญ่จะคอมไพล์ เพื่อทำความเข้าใจว่าโปรแกรมทำอะไรได้บ้าง โปรดดูตารางต่อไปนี้ ใช้ "bytecode" (ซึ่งส่วนใหญ่เป็นพอยน์เตอร์ 16 บิต) เป็นอินพุตและแปลงเป็น C
รหัสมา:
: .C ( -- )
Display context stack contents.
CR CDEPTH IF CXSP @ 3 + END-CX
DO I 1.5@ .DRJ -3 +LOOP
ELSE ." MT STK"
THEN CR ;
EXIT
การเปลี่ยนแปลง:
พอยน์เตอร์ 16 บิต | ออกไป | ค |
---|---|---|
: .ค ( -- ) | void DrawC() { | |
unsigned short int i, imax; | ||
0x0642 | CR | Exec("CR"); |
0x75d5 | ซีดีพีที | CDEPTH(); |
0x15fa 0x0020 | ถ้า | if (Pop() != 0) { |
0x54ae | ซีเอ็กซ์เอสพี | Push(Read16(pp_CXSP) + 3); |
0xbae | - | |
0x3b73 | 3 | |
0x0f72 | - | |
0x4ffd | END-CX | Push(Read16(cc_END_dash_CX)); |
0x15b8 | ทำ | i = Pop(); |
imax = Pop(); | ||
do { | ||
0x50e0 | ฉัน | Push(i); |
0x4995 | 1.5@ | _1_dot_5_at_(); |
0x81d5 | .DRJ | DrawDRJ(); |
0x175d 0xfffd | -3 | Push(-3); |
0x155c 0xffff | +ลูป | int step = Pop(); |
i += step; | ||
if (((step>=0) && (i>=imax)) || ((step<0) && (i<=imax))) break; | ||
} while(1); | ||
0x1660 0x000b | อื่น | } else { |
0x1bdc | "เอ็มที เอสทีเค" | PRINT("MT STK", 6); |
0x06 | ||
0x4d | 'เอ็ม' | |
0x54 | 'ที' | |
0x20 | - | |
0x53 | 'ส' | |
0x54 | 'ที' | |
0x4b | 'เค' | |
แล้ว | } | |
0x0642 | CR | Exec("CR"); |
0x1690 | ออก | } |
เกมดังกล่าวมี 3 ไฟล์
เนื้อหาของ STARA.com
รายการ | ขนาด | คำอธิบาย |
---|---|---|
ไดเรกทอรี | 4096 | มีไดเรกทอรีของ STARA และ STARB |
ELO-ซีปิก | 4816 | |
GAZ-CPIC | 3120 | |
MEC-CPIC | 2848 | |
MYS-CPIC | 6064 | |
NOM-CPIC | 1136 | |
SPE-CPIC | พ.ศ. 2431 | |
THR-CPIC | 2480 | |
เวล-ซีปิค | 4672 | |
VPR-CPIC | 1248 | |
MIN-CPIC | 2096 | |
สาด | 16384 | รูปภาพ |
MED-PIC | 2048 | รูปภาพ |
เฟส | 6144 | |
ฮัม-PIC | 480 | รูปภาพ |
VEL-PIC | 432 | รูปภาพ |
THR-PIC | 272 | รูปภาพ |
ELO-PIC | 608 | รูปภาพ |
และ-PIC | 640 | รูปภาพ |
บันทึก | 124000 | |
ดนตรี | 4960 | การซ้อนทับโค้ด |
โลก | 1152 | แผนที่ของดาวเคราะห์โลก |
กาแล็กซี่ | 6304 | |
เครดิต | 16384 | รูปภาพ |
ตำรวจ-CPIC | 2928 | |
แบบอักษร | 768 | |
ซีจีเอ | 3600 | รูทีนรหัสเครื่องสำหรับกราฟิกการ์ด CGA |
กฟผ | 3600 | รูทีนรหัสเครื่องสำหรับกราฟิกการ์ด EGA |
เนื้อหาของ STARB.COM
รายการ | ขนาด | คำอธิบาย |
---|---|---|
ไดเรกทอรี | 4096 | มีไดเรกทอรีของ STARA และ STARB |
ตัวอย่าง | 150528 | โครงสร้างแบบต้นไม้ที่มีเนื้อหาส่วนใหญ่ของเกม |
กล่อง | 1,024 | โต๊ะ |
โอนเงินผ่านธนาคาร | 144 | โต๊ะ |
ลูกเรือ | 128 | โต๊ะ |
เรือ | 2479 | โต๊ะ |
องค์ประกอบ | 544 | โต๊ะ |
สิ่งประดิษฐ์ | 1584 | โต๊ะ |
ดาวเคราะห์ | 1360 | โต๊ะ |
ตัวอย่าง | 448 | โต๊ะ |
ไบโอดาต้า | 448 | โต๊ะ |
ทีพอร์ต-PIC | 2416 | รูปภาพ |
BPORT-PIC | 3984 | รูปภาพ |
วิเคราะห์ข้อความ | 3200 | โต๊ะ |
ปุ่ม | 944 | โต๊ะ |
ไอคอน1:1 | 912 | |
ไอคอน1:2 | 912 | |
ไอคอน1:4 | 912 | |
ไอคอน-ชื่อ | 736 | |
DPART-OV | 1552 | การซ้อนทับโค้ด |
ภูมิภาค | 176 | โต๊ะ |
สิ่งมีชีวิต | 17024 | โต๊ะ |
CHKFLIGHT-OV | 960 | การซ้อนทับโค้ด |
แฟรค-OV | 4640 | การซ้อนทับโค้ด |
ไอคอนพี-OV | 832 | การซ้อนทับโค้ด |
ไซต์-OV | พ.ศ. 2431 | การซ้อนทับโค้ด |
HYPERMSG-OV | 4112 | การซ้อนทับโค้ด |
จีโปลี | 368 | |
แง่มุม | 288 | |
เวอร์เท็กซ์ | 416 | |
บีแอลที-OV | 864 | การซ้อนทับโค้ด |
เบ็ดเตล็ด-OV | 1440 | การซ้อนทับโค้ด |
ธนาคาร-OV | 1520 | การซ้อนทับโค้ด |
สกรู-OV | 2800 | การซ้อนทับโค้ด |
บุคลากร-อฟ | 4192 | การซ้อนทับโค้ด |
SHIPGRPH-OV | 2112 | การซ้อนทับโค้ด |
กำหนดค่า-OV | 3072 | การซ้อนทับโค้ด |
ทีดีพอต-OV | 4800 | การซ้อนทับโค้ด |
PORTMENU-OV | 3120 | การซ้อนทับโค้ด |
วิต้า-โอวี | 3552 | การซ้อนทับโค้ด |
HP-OV | 4832 | การซ้อนทับโค้ด |
LP-OV | 5280 | การซ้อนทับโค้ด |
ส่ง-OV | 4784 | การซ้อนทับโค้ด |
ทีวี-OV | 3472 | การซ้อนทับโค้ด |
คอมม์-OV | 7232 | การซ้อนทับโค้ด |
COMMSPEC-OV | 2864 | การซ้อนทับโค้ด |
เมล็ด-OV | 2400 | การซ้อนทับโค้ด |
รายการ | 720 | การซ้อนทับโค้ด |
ย้าย-OV | 3808 | การซ้อนทับโค้ด |
วิศวกร | 2320 | การซ้อนทับโค้ด |
หมอ | 1280 | การซ้อนทับโค้ด |
วงโคจร-OV | 6640 | การซ้อนทับโค้ด |
กัปตัน | 5952 | การซ้อนทับโค้ด |
ศาสตร์ | 3952 | การซ้อนทับโค้ด |
ระบบนำทาง | 880 | การซ้อนทับโค้ด |
ปุ่มจัดส่ง | 1984 | |
แผนที่-OV | 4160 | การซ้อนทับโค้ด |
ไฮเปอร์-OV | 7168 | การซ้อนทับโค้ด |
วิเคราะห์-OV | 2560 | การซ้อนทับโค้ด |
เปิดตัว-OV | 1360 | การซ้อนทับโค้ด |
ฟลักซ์เอฟเฟกต์ | 464 | |
โอพี-โอวี | 4400 | การซ้อนทับโค้ด |
รายการ-OV | 6016 | การซ้อนทับโค้ด |
แอลซีซิคอน | 752 | |
ไมซิสคอน | 448 | |
ซิสซิคอน | 176 | |
บีฮาฟ-OV | 5360 | |
ซีแมป | 1008 | |
ติดตั้ง | 800 | |
ฮีล-OV | 1232 | การซ้อนทับโค้ด |
ซ่อม-OV | 1696 | การซ้อนทับโค้ด |
เกม-OV | 5920 | การซ้อนทับโค้ด |
PLSET-OV | 2400 | การซ้อนทับโค้ด |
แผนที่-OV | 2240 | การซ้อนทับโค้ด |
VES-BLT | 4528 | |
สตอร์ม-OV | 1232 | การซ้อนทับโค้ด |
สารประกอบ | 176 | โต๊ะ |
ไอที-OV | 2479 | การซ้อนทับโค้ด |
การต่อสู้-OV | 6192 | การซ้อนทับโค้ด |
ความเสียหาย-OV | 2752 | การซ้อนทับโค้ด |
แลนด์-OV | 1088 | การซ้อนทับโค้ด |
สถิตย์ | 64 | โต๊ะ |
เอสทีพี-โอวี | 1440 | การซ้อนทับโค้ด |
วางไฟล์ของเกม Starflight ดั้งเดิมลงในโฟลเดอร์ starflt1-in
และ starflt2-in
แล้วเรียกใช้ make
คุณควรได้รับไฟล์ปฏิบัติการสองรายการ ( disasOV1
และ disasOV2
) ซึ่งสร้างเนื้อหาในโฟลเดอร์ starflt1-out
และ starflt2-out
เอาต์พุตที่สร้างขึ้นเป็นส่วนหนึ่งของที่เก็บนี้