พื้นหลัง
ฉันชอบชิป 8 บิต 6502 แบบเก่าของฉัน มันสนุกมากที่จะไขความท้าทายบางอย่างที่นี่บน PPCG ในรหัสเครื่อง 6502 แต่บางสิ่งที่ควรจะง่าย (เช่นอ่านข้อมูลหรือส่งออกไปยัง stdout) นั้นยุ่งยากโดยไม่จำเป็นในรหัสเครื่อง ดังนั้นจึงมีความคิดคร่าวๆในใจของฉัน: คิดค้นเครื่องเสมือน 8 บิตของตัวเองที่ได้รับแรงบันดาลใจจาก 6502 แต่ด้วยการออกแบบที่ปรับเปลี่ยนให้ใช้งานได้มากกว่าสำหรับความท้าทาย เริ่มใช้บางสิ่งฉันรู้ว่านี่อาจเป็นความท้าทายที่ดีถ้าการออกแบบ VM ลดลงจนเหลือน้อยที่สุด :)
งาน
ใช้เครื่องเสมือน 8 บิตที่สอดคล้องกับข้อกำหนดต่อไปนี้ นี่คือโค้ดกอล์ฟดังนั้นการติดตั้งที่มีไบต์น้อยที่สุดจะชนะ
อินพุต
การใช้งานของคุณควรใช้อินพุตต่อไปนี้:
ไบต์ที่ไม่ได้ลงนามเดียว
pcนี่คือตัวนับโปรแกรมเริ่มต้น (ที่อยู่ในหน่วยความจำที่ VM เริ่มดำเนินการ0- ตาม)รายการไบต์ที่มีความยาวสูงสุดของ
256รายการนี่คือ RAM สำหรับเครื่องเสมือน (ที่มีเนื้อหาเริ่มต้น)
คุณสามารถรับอินพุตนี้ในรูปแบบที่สมเหตุสมผล
เอาท์พุต
รายการไบต์ซึ่งเป็นเนื้อหาสุดท้ายของ RAM หลังจาก VM ยกเลิก (ดูด้านล่าง) คุณสามารถสมมติว่าคุณได้รับการป้อนข้อมูลที่นำไปสู่การยกเลิกในที่สุด อนุญาตรูปแบบที่เหมาะสม
CPU เสมือน
ซีพียูเสมือนมี
- ตัวนับโปรแกรม 8 บิต
- ตัวสะสมแบบ 8 บิตที่เรียกว่า
Aและ - ลงทะเบียนดัชนี 8
Xบิตที่เรียกว่า
มีสถานะสามสถานะ:
Z- ตั้งค่าสถานะเป็นศูนย์หลังจากการดำเนินการบางอย่างส่งผล0N- ตั้งค่าสถานะเชิงลบหลังจากที่ผลการดำเนินงานบางอย่างในจำนวนลบ (iow บิต 7 ของผลการตั้งค่า)C- ค่าสถานะพกถูกตั้งค่าโดยการเพิ่มเติมและกะสำหรับบิต "หายไป" ของผลลัพธ์
เมื่อเริ่มต้นแฟล็กจะถูกล้างทั้งหมดตัวนับโปรแกรมจะถูกตั้งค่าเป็นค่าที่กำหนดและเนื้อหาของAและXไม่แน่นอน
ค่า 8 บิตแสดงถึงอย่างใดอย่างหนึ่ง
- ที่ไม่ได้ลงนามจำนวนเต็มในช่วง
[0..255] - ลงนามจำนวนเต็ม 2 สมบูรณ์ในช่วง
[-128..127]
ขึ้นอยู่กับบริบท หากการดำเนินการมากเกินไปหรือน้อยเกินไปค่าจะถูกล้อมรอบ (และในกรณีที่มีการเพิ่มค่าสถานะการพกพาจะได้รับผลกระทบ)
การสิ้นสุด
เครื่องเสมือนจะหยุดทำงานเมื่อ
-
HLTคำแนะนำถึง - เข้าถึงที่อยู่หน่วยความจำที่ไม่มีอยู่
- ตัวนับโปรแกรมทำงานนอกหน่วยความจำ (โปรดทราบว่ามันจะไม่พันรอบแม้ว่า VM จะได้รับหน่วยความจำเต็ม 256 ไบต์)
โหมดที่อยู่
- โดยนัย - คำสั่งไม่มีข้อโต้แย้งตัวถูกดำเนินการโดยนัย
- ทันที - ตัวถูกดำเนินการเป็นไบต์โดยตรงหลังจากการเรียนการสอน
- สัมพัทธ์ - (สำหรับการแตกแขนงเท่านั้น) ไบต์หลังจากคำสั่งถูกลงชื่อ (ส่วนเติมเต็ม 2) และกำหนดออฟเซ็ตเพื่อเพิ่มลงในตัวนับโปรแกรมหากมีการแยกสาขา
0เป็นที่ตั้งของคำแนะนำต่อไปนี้ - แน่นอน - ไบต์หลังจากคำสั่งคือที่อยู่ของตัวถูกดำเนินการ
- จัดทำดัชนี - ไบต์หลังจากคำสั่งบวก
X(การลงทะเบียน) คือที่อยู่ของตัวถูกดำเนินการ
คำแนะนำ
การเรียนการสอนในแต่ละประกอบด้วย opcode (หนึ่งไบต์) และในโหมดที่อยู่ได้ทันที , ญาติ , แน่นอนและการจัดทำดัชนีไบต์อาร์กิวเมนต์ที่สอง เมื่อ CPU เสมือนดำเนินการคำสั่งจะเพิ่มตัวนับโปรแกรมตาม (โดย1หรือ2 )
opcodes ทั้งหมดที่แสดงในที่นี้เป็นเลขฐานสิบหก
LDA- ตัวถูกดำเนินการโหลดเข้าA- Opcodes: ทันที:
00, สัมบูรณ์:02, จัดทำดัชนี:04 - ธง:
Z,N
- Opcodes: ทันที:
STA- เก็บตัวAถูกดำเนินการ- Opcodes: ทันที:
08, สัมบูรณ์:0a, จัดทำดัชนี:0c
- Opcodes: ทันที:
LDX- ตัวถูกดำเนินการโหลดเข้าX- Opcodes: ทันที:
10, แน่นอน:12, จัดทำดัชนี:14 - ธง:
Z,N
- Opcodes: ทันที:
STX- เก็บตัวXถูกดำเนินการ- Opcodes: ทันที:
18, สัมบูรณ์:1a, จัดทำดัชนี:1c
- Opcodes: ทันที:
AND- bitwise และ ofAและ operand เป็นA- Opcodes: ทันที:
30, แน่นอน:32, จัดทำดัชนี:34 - ธง:
Z,N
- Opcodes: ทันที:
ORA- bitwise หรือของAและ operand เป็นA- Opcodes: ทันที:
38, แน่นอน:3a, จัดทำดัชนี:3c - ธง:
Z,N
- Opcodes: ทันที:
EOR- bitor xor (พิเศษหรือ) ของAและถูกดำเนินการไปA- Opcodes: ทันที:
40, แน่นอน:42, จัดทำดัชนี:44 - ธง:
Z,N
- Opcodes: ทันที:
LSR- ตรรกะเลื่อนไปทางขวาเลื่อนบิตของตัวถูกดำเนินการทั้งหมดที่เดียวไปทางขวาบิต 0 จะถูกนำไปใช้- Opcodes: ทันที:
48, สัมบูรณ์:4a, จัดทำดัชนี:4c - ธง:
Z,N,C
- Opcodes: ทันที:
ASL- การเลื่อนเลขคณิตไปทางซ้ายเลื่อนบิตของตัวถูกดำเนินการทั้งหมดที่เดียวไปทางซ้ายบิต 7 จะถูกดำเนินการ- Opcodes: ทันที:
50, สัมบูรณ์:52, จัดทำดัชนี:54 - ธง:
Z,N,C
- Opcodes: ทันที:
ROR- หมุนไปทางขวาเลื่อนบิตของตัวถูกดำเนินการทั้งหมดที่เดียวไปทางขวาพกไปที่บิต 7, บิต 0 ไปเพื่อดำเนินการ- Opcodes: ทันที:
58, สัมบูรณ์:5a, จัดทำดัชนี:5c - ธง:
Z,N,C
- Opcodes: ทันที:
ROL- หมุนไปทางซ้ายเลื่อนบิตทั้งหมดของตัวถูกดำเนินการไปทางซ้ายหนึ่งจุดไปยังบิต 0, บิต 7 ไปสู่การถือ- Opcodes: ทันที:
60, สัมบูรณ์:62, จัดทำดัชนี:64 - ธง:
Z,N,C
- Opcodes: ทันที:
ADC- เพิ่มด้วยการดำเนินการ, ตัวถูกดำเนินการบวกการดำเนินการเพิ่มไปยังAการดำเนินการตั้งอยู่บนล้น- Opcodes: ทันที:
68, สัมบูรณ์:6a, จัดทำดัชนี:6c - ธง:
Z,N,C
- Opcodes: ทันที:
INC- ตัวถูกดำเนินการที่เพิ่มขึ้นหนึ่ง- Opcodes: ทันที:
78, สัมบูรณ์:7a, จัดทำดัชนี:7c - ธง:
Z,N
- Opcodes: ทันที:
DEC- ตัวถูกดำเนินการลดลงหนึ่ง- Opcodes: ทันที:
80, สัมบูรณ์:82, จัดทำดัชนี:84 - ธง:
Z,N
- Opcodes: ทันที:
CMP- เปรียบเทียบAกับตัวถูกดำเนินการโดยการลบตัวถูกดำเนินการจากAลืมผลลัพธ์ Carry ถูกลบล้างอันเดอร์โฟลว์หรือไม่- Opcodes: ทันที:
88, สัมบูรณ์:8a, จัดทำดัชนี:8c - ธง:
Z,N,C
- Opcodes: ทันที:
CPX- เทียบX- เช่นเดียวCMPสำหรับX- Opcodes: ทันที:
90, สัมบูรณ์:92, จัดทำดัชนี:94 - ธง:
Z,N,C
- Opcodes: ทันที:
HLT- ยุติ- Opcodes: โดยนัย:
c0
- Opcodes: โดยนัย:
INX- เพิ่มขึ้นทีละXหนึ่ง- Opcodes: โดยนัย:
c8 - ธง:
Z,N
- Opcodes: โดยนัย:
DEX- ลดลงXหนึ่ง- Opcodes: โดยนัย:
c9 - ธง:
Z,N
- Opcodes: โดยนัย:
SEC- ตั้งธงพก- Opcodes: โดยนัย:
d0 - ธง:
C
- Opcodes: โดยนัย:
CLC- ธงพกพาที่ชัดเจน- Opcodes: โดยนัย:
d1 - ธง:
C
- Opcodes: โดยนัย:
BRA- สาขาเสมอ- Opcodes: ญาติ:
f2
- Opcodes: ญาติ:
BNE- สาขาถ้าZล้างธง- Opcodes: ญาติ:
f4
- Opcodes: ญาติ:
BEQ- สาขาถ้าZตั้งค่าสถานะ- Opcodes: ญาติ:
f6
- Opcodes: ญาติ:
BPL- สาขาถ้าNล้างธง- Opcodes: ญาติ:
f8
- Opcodes: ญาติ:
BMI- สาขาถ้าNตั้งค่าสถานะ- Opcodes: ญาติ:
fa
- Opcodes: ญาติ:
BCC- สาขาถ้าCล้างธง- Opcodes: ญาติ:
fc
- Opcodes: ญาติ:
BCS- สาขาถ้าCตั้งค่าสถานะ- Opcodes: ญาติ:
fe
- Opcodes: ญาติ:
opcodes
พฤติกรรมของ VM นั้นไม่ได้กำหนดไว้หากพบว่ามี opcode ที่ไม่ได้จับคู่กับคำสั่งที่ถูกต้องจากรายการด้านบน
ตามคำขอของ Jonathan Allanคุณสามารถเลือก opcodes ของคุณเองแทน opcodes ที่แสดงในหัวข้อคำแนะนำ หากคุณทำเช่นนั้นคุณจะต้องเพิ่มการจับคู่แบบเต็มใน opcode ที่ใช้ด้านบนในคำตอบของคุณ
การจับคู่ควรเป็นไฟล์เลขฐานสิบหกที่มีคู่<official opcode> <your opcode>เช่นหากคุณแทนที่ opcodes สองรายการ:
f4 f5
10 11
บรรทัดใหม่ไม่สำคัญที่นี่
กรณีทดสอบ (opcodes อย่างเป็นทางการ)
// 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
ฉันอาจเพิ่มการทดสอบเพิ่มเติมในภายหลัง
การอ้างอิงและการทดสอบ
เพื่อช่วยในการทดลองของตัวเองนี่คือการดำเนินการอ้างอิงบางส่วน (โดยสิ้นเชิงที่ไม่ใช่กอล์ฟ) - มันสามารถส่งออกข้อมูลการติดตาม (รวมถึงคำแนะนำในการถอดประกอบ) ไปstderrที่
วิธีที่แนะนำในการรับแหล่งที่มา:
git clone https://github.com/zirias/gvm --branch challenge --single-branch --recurse-submodules
หรือสาขาชำระเงินchallengeและทำgit submodule update --init --recursiveโคลนหลังจากเพื่อรับระบบสร้างที่กำหนดเองของฉัน
สร้างเครื่องมือด้วย GNU make (เพียงพิมพ์makeหรือgmakeถ้าอยู่ในระบบของคุณการสร้างเริ่มต้นไม่ใช่ GNU)
การใช้งาน :gvm [-s startpc] [-h] [-t] [-c convfile] [-d] [-x] <initial_ram
-s startpc- ตัวนับโปรแกรมเริ่มต้นเริ่มต้นที่0-h- อินพุตอยู่ในรูปแบบเลขฐานสิบหก (เป็นอย่างอื่นไบนารี)-t- การดำเนินการติดตามไปที่stderr-c convfile- แปลง opcodes ตามแผนที่ที่กำหนดconvfile-d- ดัมพ์หน่วยความจำที่ได้นั้นเป็นข้อมูลไบนารี่-x- ถ่ายโอนข้อมูลหน่วยความจำที่เกิดเป็น hexinitial_ram- เนื้อหา RAM เริ่มต้นไม่ว่าจะเป็นฐานสิบหกหรือไบนารี
หมายเหตุคุณลักษณะการแปลงจะล้มเหลวในโปรแกรมที่ปรับเปลี่ยน opcodes ในขณะที่ทำงาน
คำเตือน:กฎและรายละเอียดด้านบนมีอำนาจสำหรับความท้าทายไม่ใช่เครื่องมือนี้ โดยเฉพาะอย่างยิ่งนี้ใช้กับคุณสมบัติการแปลง opcode หากคุณคิดว่าเครื่องมือที่นำเสนอที่นี่มีข้อผิดพลาด wrt รายละเอียดโปรดรายงานในความคิดเห็น :)
BRA(สาขา "เสมอ") ไม่แนะนำสาขาในการควบคุมการไหลไม่ควรมันจะเรียกว่าJMP?
BRAมีอยู่ในการออกแบบชิปในภายหลัง (6502 ไม่มีคำสั่งดังกล่าว) เช่น 65C02 และ MC 68000 JMPมีอยู่เช่นกัน ความแตกต่างคือการBRAใช้การกำหนดที่อยู่แบบสัมพันธ์และJMPใช้การกำหนดที่อยู่แบบสัมบูรณ์ ดังนั้นฉันเพิ่งทำตามการออกแบบเหล่านี้ - แน่นอนว่ามันฟังดูไม่สมเหตุสมผลเลย)