如何在Perl中处理二进制文件中存储的操作码?
这是一篇关于我要做什么的速记 是仿真器的确切规格 我正在用Perl编写一种仿真器。该程序将读取二进制文件,并将二进制文件的十六进制值处理为操作,处理器如何工作,但软件基于不同的操作码。只是个挑战 无论如何,我不完全确定如何将二进制文件读入数组,然后将数组中的十六进制值分配给操作码。这是我代码的一部分:如何在Perl中处理二进制文件中存储的操作码?,perl,binary,Perl,Binary,这是一篇关于我要做什么的速记 是仿真器的确切规格 我正在用Perl编写一种仿真器。该程序将读取二进制文件,并将二进制文件的十六进制值处理为操作,处理器如何工作,但软件基于不同的操作码。只是个挑战 无论如何,我不完全确定如何将二进制文件读入数组,然后将数组中的十六进制值分配给操作码。这是我代码的一部分: my $opcode=''; my $file= "path/to/file"; my $IP=0; my $SP=0; my $FLAG=0; my $A=0; my $B=0; my $X=0
my $opcode='';
my $file= "path/to/file";
my $IP=0;
my $SP=0;
my $FLAG=0;
my $A=0;
my $B=0;
my $X=0;
my @STACK=(0);
open(F, "<".$file) || die("Could not open file");
binmode(F);
my @ROM = <F>;
close(F);
while($IP >= 0)
{
$opcode="$ROM[$IP]";
if ($opcode eq 11) {
$A = $STACK[$SP];
$IP++;
}
if ($opcode eq 12) {
$B = $STACK[$SP];
$IP++;
}
if ($opcode eq 13) {
$A = $B;
$IP++;
}
if ($opcode eq 14) {
$B = $A;
$IP++;
}
my$opcode='';
my$file=“path/to/file”;
我的$IP=0;
我的$SP=0;
我的$FLAG=0;
我的$A=0;
我的$B=0;
我的$X=0;
我的@STACK=(0);
打开(F),如果您只想将文件逐字节拆分到@ROM
,请尝试
local $/; # get the whole file once, see perldoc perlvar for details
my @ROM = split //, <F>;
local$/;#一次获取整个文件,有关详细信息,请参阅perldoc perlvar
my@ROM=split/;
我不认为一次读取一个二进制字符是一个好主意(尽管对于足够小的文件,大部分时间都是从缓冲区中提取)。从概念上讲,将文件读入平面数组更好。您可以使用
my @ROM = do {
local $/ = \1;
map ord, <$fh>;
};
my@ROM=do{
本地$/=\1;
地图;
};
我想这回答了你问题的要点
现在,关于不将整个文件读取到数组中,而是将文件本身用作可执行映像的想法,这里是我根据本文早期版本和反馈编写的一个小脚本
请注意,整个scalar filehandle的作用是为运行您通过复制和粘贴链接的文章中包含的ROM提供支架,而不必创建二进制文件
#!/usr/bin/env perl
use strict;
use warnings;
use Fcntl ':seek';
use Try::Tiny;
my %ROMS = (
rom1 => [0x04, 0x41, 0x09, 0x02, 0x0a],
rom2 => [
0x04, 0x41, 0x09, 0x02,
0x07, 0x09, 0x02, 0x07,
0x09, 0x02, 0x07, 0x09,
0x02, 0x07, 0x09, 0x02,
0x0A,
],
rom3 => [
0x04, 69, 0x09, 0x02, 0x04, 110, 0x09, 0x02,
0x04, 116, 0x09, 0x02, 0x04, 101, 0x09, 0x02,
0x04, 114, 0x09, 0x02, 0x04, 32, 0x09, 0x02,
0x04, 97, 0x09, 0x02, 0x04, 32, 0x09, 0x02,
0x04, 67, 0x09, 0x02, 0x04, 104, 0x09, 0x02,
0x04, 97, 0x09, 0x02, 0x04, 114, 0x09, 0x02,
0x04, 32, 0x09, 0x02, 0x04, 58, 0x09, 0x02,
0x04, 32, 0x09, 0x02, 0x04, 50, 0x03, 0x01,
0x04, 2, 0x03, 0x04, 89, 0x09, 0x02, 0x04,
111, 0x09, 0x02, 0x04, 117, 0x09, 0x02, 0x04,
32, 0x09, 0x02, 0x04, 84, 0x09, 0x02, 0x04,
121, 0x09, 0x02, 0x04, 112, 0x09, 0x02, 0x04,
101, 0x09, 0x02, 0x04, 100, 0x09, 0x02, 0x04,
32, 0x09, 0x02, 0x04, 58, 0x09, 0x02, 0x04,
32, 0x09, 0x02, 0x04, 50, 0x03, 0x02, 0x0A,
]
);
for my $rom (sort keys %ROMS) {
my $rom_s = join '', map chr, @{ $ROMS{ $rom } };
open my $rom_h, '<:raw', \$rom_s
or die "Cannot open handle to ROM string: $!\n";
print "Executing $rom\n";
try {
execute($rom_h);
}
catch {
print "\n$rom: $_\n";
};
close $rom_h
or die "Cannot close handle to ROM string: $!\n";
}
sub get_next_byte {
my ($fh) = @_;
my $byte = do {
local $/ = \1;
scalar <$fh>;
};
return unless defined $byte;
$byte = ord $byte;
return $byte;
}
sub execute {
my ($ROM) = @_;
my $FLAG = 0;
my $SP = 0;
my $X = 0;
my @STACK;
my @machine = (
# NOP
sub {},
# INPUT
sub { $STACK[$SP] = ord(getc STDIN) },
# OUTPUT
sub { printf STDOUT '%c', $STACK[$SP] },
# MOV SP, X
sub { $SP = $X },
# MOV X, DATA
sub {
$X = get_next_byte($ROM);
},
# CMP X, DATA
sub {
$FLAG = $X - get_next_byte($ROM);
},
# JE
sub {
my $offset = get_next_byte($ROM);
if ($FLAG == 0) {
seek $ROM, $offset, SEEK_CUR
}
},
# INC X
sub { $X += 1 },
# INC SP
sub { $SP += 1 },
# MOV [SP], X
sub { $STACK[$SP] = $X },
# HALT
sub {
die "HALT\n";
},
);
while (1) {
my $opcode = get_next_byte($ROM);
last unless defined $opcode;
if (($opcode >= 0) and ($opcode < @machine)) {
$machine[ $opcode ]->();
}
else {
die sprintf(
"Invalid opcode '%02x' at offset '%x'\n",
$opcode, $.,
);
}
}
}
!/usr/bin/env perl
严格使用;
使用警告;
使用Fcntl':seek';
使用Try::Tiny;
我的%ROMS=(
rom1=>[0x04,0x41,0x09,0x02,0x0a],
rom2=>[
0x04、0x41、0x09、0x02、,
0x07,0x09,0x02,0x07,
0x09,0x02,0x07,0x09,
0x02,0x07,0x09,0x02,
0x0A,
],
rom3=>[
0x04,69,0x09,0x02,0x04,110,0x09,0x02,
0x04、116、0x09、0x02、0x04、101、0x09、0x02,
0x04、114、0x09、0x02、0x04、32、0x09、0x02,
0x04,97,0x09,0x02,0x04,32,0x09,0x02,
0x04,67,0x09,0x02,0x04,104,0x09,0x02,
0x04,97,0x09,0x02,0x04,114,0x09,0x02,
0x04,32,0x09,0x02,0x04,58,0x09,0x02,
0x04,32,0x09,0x02,0x04,50,0x03,0x01,
0x04,2,0x03,0x04,89,0x09,0x02,0x04,
111,0x09,0x02,0x04,117,0x09,0x02,0x04,
32、0x09、0x02、0x04、84、0x09、0x02、0x04,
121,0x09,0x02,0x04,112,0x09,0x02,0x04,
101,0x09,0x02,0x04,100,0x09,0x02,0x04,
32、0x09、0x02、0x04、58、0x09、0x02、0x04,
32、0x09、0x02、0x04、50、0x03、0x02、0x0A,
]
);
对于我的$rom(排序键%rom){
my$rom_s=join',map chr,@{$ROMS{$rom};
打开我的$rom_h,如果$opcode
未定义,则$rom[$IP]
未定义
在代码的第二个版本中,您没有显示正在填充@ROM
,因此$ROM[$IP]
未定义也就不足为奇了
代码的第一个版本更接近您想要的版本。但是,@ROM
的初始化不正确。文件的每一行(甚至不是由行组成)都被分配给@ROM
的一个元素,但您希望文件的每个字节都被分配给@ROM
的一个元素。具体操作如下:
my @ROM = do {
open(my $fh, '<', $file)
or die("Can't open ROM file \"$file\": $!\n");
binmode($fh);
local $/; # Read entire file at once.
map ord, split //, <$fh>
};
请注意,:raw
并不完全等同于binmode
,尽管有相关文档
注意使用或将字符转换为数值,以及使用数值比较运算符(=
)比较这些数字
请注意,我使用的是十六进制数字。十六进制(或者可能是八进制,具体取决于模拟的机器)比使用十进制更清晰,因为类似的操作码往往以位而不是数字形式变化。此外,您提供的数字已经是十六进制的,因此操作码11
实际上是十七
我现在明白了…一切都在按需运转:
my $IP=0;
my $FLAG=0;
my $SP=0;
my $A=0;
my $B=0;
my @STACK=(0);
my $byte=0;
$| = 1;
open(BOOTROM, "<bootrom.txt");
binmode(BOOTROM);
my (@ROM, $instruction);
while ((read BOOTROM, $instruction, 1) != 0) {
@ROM[$IP] = $instruction;
$IP++;
}
close(BOOTROM);
$IP = 0;
while ($IP >= 0 && $byte != 0x3C) {
$byte = ord(@ROM[$IP]);
if ($byte == 0x11) {
$A = (@STACK[$SP]);
$IP++;
}
elsif ($byte == 0x12) {
$B = (@STACK[$SP]);
$IP++;
}
elsif ($byte == 0x13) {
$A = $B;
$IP++;
}
elsif ($byte == 0x14) {
$B = $A;
$IP++;
}
my$IP=0;
我的$FLAG=0;
我的$SP=0;
我的$A=0;
我的$B=0;
我的@STACK=(0);
我的$byte=0;
$| = 1;
打开(BOOTROM,“恰恰相反,他确实需要加载整个文件;他只是加载错误。为什么ROM不能放在磁盘上?内存是线性访问的。如果你想在每次读取之前进行查找,那就去吧,但这肯定是一种浪费。还有一种情况是,如果你没有将ROM复制到RAM中,ROM就不会是只读的。你忘了吗关于存储操作?我看到你添加了一个调度表。我不确定这是最好的方法。如果他的虚拟机有任何想法,操作码不是由整个字节表示的。字节的大部分位实际上是操作数(即数据)。实际上只有12个左右的操作码。s/Memery是线性访问的。/内存不是线性访问的。/这是一篇关于我正在尝试做什么的速记。这是关于模拟器应该是什么的确切说明。它似乎有一个单独的堆栈。这是非常不现实的,但就这样吧。我已经emoved my comments on the stack.@quantumdisaster,操作码数字在PDF中是十六进制的。因此,当您比较11时,您应该比较17(0x11).调整了我的帖子。@quantumdisaster,设计这台机器的人对机器设计不太了解。这在硬件上实现起来效率很低。折线图ord,split将数据作为十进制数放在我的数组中,我需要它们作为十六进制值放在那里链接断了。
my @ROM = do {
open(my $fh, '<', $file)
or die("Can't open ROM file \"$file\": $!\n");
binmode($fh);
local $/; # Read entire file at once.
map ord, split //, <$fh>
};
my @STACK;
my $A = 0;
my $B = 0;
my $SP = 0;
my $IP = 0;
for (;;) {
die(sprintf("Bad address 0x%04X\n", $IP)) if $IP >= @ROM;
my $instruction = $ROM[$IP++];
if ($opcode == 0x11) { $A = $STACK[$SP]; }
elsif ($opcode == 0x12) { $B = $STACK[$SP]; }
elsif ($opcode == 0x13) { $A = $B; }
...
else { die(sprintf("Bad opcode 0x%02X\n", $opcode)); }
}
$char_from_file eq 11 # XXX What you had.
$char_from_file eq chr(0x11) # Ok, but a bit suboptimal.
ord($char_from_file) == 0x11 # Ok. What I have.
my $IP=0;
my $FLAG=0;
my $SP=0;
my $A=0;
my $B=0;
my @STACK=(0);
my $byte=0;
$| = 1;
open(BOOTROM, "<bootrom.txt");
binmode(BOOTROM);
my (@ROM, $instruction);
while ((read BOOTROM, $instruction, 1) != 0) {
@ROM[$IP] = $instruction;
$IP++;
}
close(BOOTROM);
$IP = 0;
while ($IP >= 0 && $byte != 0x3C) {
$byte = ord(@ROM[$IP]);
if ($byte == 0x11) {
$A = (@STACK[$SP]);
$IP++;
}
elsif ($byte == 0x12) {
$B = (@STACK[$SP]);
$IP++;
}
elsif ($byte == 0x13) {
$A = $B;
$IP++;
}
elsif ($byte == 0x14) {
$B = $A;
$IP++;
}