如何在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

这是一篇关于我要做什么的速记

是仿真器的确切规格

我正在用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 @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++;
    }