Daftar isi:

Gunakan Machine.net jika Anda ingin membuat mesin virtual platform-independen dengan hanya C#.
Saat ini, hanya X64 yang didukung, serta Chipset dan HPET Intel 8253, 8259. Saat ini saya fokus menyelesaikan masalah yang ada dengan emulator x64 daripada menambahkan lebih banyak kemungkinan. Fitur -fitur baru akan ditambahkan setelah emulator X64 mulai menunjukkan kesuksesan, namun , Anda masih dapat menyarankan fitur baru jika Anda mau.
Harap dukung proyek dengan menempatkan bintang di repositori ini.
Saat Anda menggunakan Machine.net, paket Nuget yang disebut ICED juga diinstal. Ini adalah paket populer untuk instruksi decoding dan encoding, dan digunakan di Machine.net untuk mendekode instruksi.
Untuk memulai, gunakan iced.intel untuk merakit instruksi yang kita butuhkan. Dalam kasus kami, itu adalah:
mov rcx, 150
rep add rax, 4
Itu akan menjadi:
using Iced . Intel ;
using Machine . X64 . Runtime ;
using static Iced . Intel . AssemblerRegisters ;
var assembler = new Assembler ( 64 ) ;
assembler . mov ( rcx , 150 ) ;
assembler . rep . add ( rax , 4 ) ;
var stream = new MemoryStream ( ) ;
var streamCodeWriter = new StreamCodeWriter ( stream ) ;
assembler . Assemble ( streamCodeWriter , rip : 0uL ) ;
stream . Position = 0 ;
var reader = new StreamCodeReader ( stream ) ;
var decoder = Decoder . Create ( 64 , reader ) ;
decoder . IP = 0 ;
var instrs = new List < Instruction > ( ) ;
while ( stream . Position < stream . Length )
{
decoder . Decode ( out var instr ) ;
instrs . Add ( instr ) ;
}Dan sekarang, cukup buat instance baru dari kelas cpuruntime. Anda dapat melewati jumlah memori (dalam byte) dan jumlah port I/O. Dalam kasus kami, ini adalah memori 64kB dan 8 port I/O:
var runtime = new CpuRuntime ( memorySize : 65536 , ioPortCount : 8 ) ; Anda dapat memohon metode .Run(in Instruction) pada CpuRuntime untuk memohon instruksi. Mari kita minta semua instruksi:
foreach ( var instr in instrs )
{
runtime . Run ( in instr ) ;
} Metode .Run dapat sedikit membatasi jika Anda ingin mendukung instruksi lompatan dan cabang. Dalam hal ini, dimungkinkan untuk memuat bytecode langsung ke RAM dan memuatnya dari sana:
var cpu = new CpuRuntime ( ioPortCount : 8 ) ;
ulong x = 0uL ;
cpu . IOPorts [ 1 ] = new InputOutputPort (
read : ( ) =>
{
return 1234uL ;
} ,
write : ( value ) =>
{
x = value ;
} ) ;
byte [ ] code = CodeGen . MakeBranchTestCode_1 ( ) ;
cpu . LoadProgram ( code , 0uL ) ;
cpu . ProcessorRegisters . Cs = 0 ;
cpu . ProcessorRegisters . Rip = 0 ;
cpu . Use8086Compatibility ( ) ;
cpu . SetRsp ( 0x400uL ) ;
try
{
cpu . RunUntilNotBusy ( 35 ) ;
}
catch ( ArithmeticException )
{
throw new InvalidOperationException ( cpu . LastOrExecutingInstruction . Code . ToString ( ) ) ;
}
Assert . Equal ( 42uL , x ) ;
static class CodeGen
{
public static byte [ ] MakeBranchTestCode_1 ( )
{
var assembler = new Assembler ( 64 ) ;
Label lblA = assembler . CreateLabel ( "A" ) ;
Label lblC = assembler . CreateLabel ( "C" ) ;
Label lblB = assembler . CreateLabel ( "B" ) ;
assembler . Label ( ref lblA ) ;
assembler . mov ( ax , 42 ) ;
assembler . @out ( 1 , ax ) ;
assembler . call ( lblB ) ;
assembler . Label ( ref lblC ) ;
assembler . mov ( ax , bx ) ;
assembler . @out ( 1 , ax ) ;
assembler . hlt ( ) ;
assembler . Label ( ref lblB ) ;
assembler . mov ( bx , ax ) ;
assembler . call ( lblC ) ;
return Assemble ( assembler ) ;
}
private static byte [ ] Assemble ( Assembler assembler )
{
using var memoryStream = new MemoryStream ( ) ;
assembler . Assemble ( new StreamCodeWriter ( memoryStream ) , 0uL ) ;
return memoryStream . ToArray ( ) ;
}
}Memang, X adalah 42. Metode .rununtilnotbusy mulai menjalankan instruksi dari memori di CS: RIP atau hanya rip secara default. Ini memiliki dua kelebihan: satu yang mengambil int dan satu yang tidak. Salah satu yang memang mewakili jumlah maksimum instruksi yang harus dijalankan, yang lebih aman untuk digunakan jika Anda khawatir dalam kasus loop tak terbatas. Yang tidak mengambil parameter apa pun akan terus berjalan sampai instruksi HLT.
Anda juga dapat mengakses properti .ProcessorRegisters dari kelas CpuRuntime untuk memeriksa register dan bendera CPU dan bahkan memodifikasinya kapan saja. Untuk melihat hasilnya, kami akan melihat register rax :
Console . WriteLine ( runtime . ProcessorRegisters . Rax ) ;Ini menghasilkan 600, yang benar.
Untuk melampirkan perangkat eksternal, Anda dapat membuat port I/O sendiri dan menempatkan apa pun yang Anda inginkan dalam operasi baca/tulis (ya, bahkan membuat jendela baru dan menampilkannya, jika Anda mau).
var cpu = new CpuRuntime ( ioPortCount : 8 ) ;
ulong x = 42uL ;
cpu . IOPorts [ 1 ] = new InputOutputPort (
read : ( ) =>
{
return 1234uL ;
} ,
write : ( value ) =>
{
x = value ;
} ) ;Misalnya, jika kami menjalankan kode berikut pada CPU yang ditiru:
mov eax , 7777
out 1 , eax
in eax , 1Kemudian, Anda dapat melihat bahwa CPU mengirim 7777 ke port I/O diindeks 1 (port I/O diindeks mulai dari 0), dan EAX sama dengan 1234 (lihat tes unit ini, ini cukup keren):
Assert . Equal ( 7777uL , x ) ;
Assert . Equal ( 1234uL , cpu . ProcessorRegisters . Eax ) ;
// No failuresUntuk membangun mesin.net, Anda harus menginstal .net 8.0. Anda dapat mengunduhnya dari situs web resmi .net.
Jika Anda lebih suka dengan Visual Studio:
Jika Anda lebih suka dengan .NET CLI:
dotnet build . Atau ketik dotnet build -c Release untuk membangun mode rilis (misalnya jika Anda ingin menggunakan mesin.net di aplikasi dunia nyata dengan optimasi diaktifkan).| Nama Perpustakaan | URL NUGET | Kode sumber pada repo ini |
|---|---|---|
| Mesin.x64.component.registers | Klik untuk mengarahkan kembali ke sumber |
Lisensi MIT. Hak Cipta (C) Winscripter, 2023-2024.