Как я могу обработать коды операций, хранящиеся в двоичном файле в Perl? - PullRequest
2 голосов
/ 21 ноября 2011

Создание собственного абстрактного процессора - это краткая статья о том, что я пытаюсь сделать.

http://malcon.org/ctm.rar - это точные спецификации того, что я пытаюсь сделать.эмулятор должен быть.

Я пишу эмулятор в 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= "CTM-bootrom";
my $IP=0;
my $SP=0;
my $FLAG=0;
my $A=0;
my $B=0;
my $X=0;
my @STACK=(0);

open my $ROM, '<:raw', $file or die "Cannot open '$file': $!";

{
    local $/ = \1; # Read one byte at a time
    while (my $byte = <$ROM>){
        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++;
            }

Но теперь я получаю ошибку:

Использование неинициализированного значения $ opcode в строковом эквалайзере в строке 73 Aod8.pl,

<$ ROM> для меня это выглядит такИнициализирован код операции ... Как я могу исправить эту проблему?

Ответы [ 4 ]

2 голосов
/ 21 ноября 2011

Я не обязательно думаю, что будет хорошей идеей читать двоичный символ по одному за раз (хотя, для достаточно маленьких файлов вы будете извлекать из буфера большую часть времени).Концептуально чтение файла в плоский массив лучше.Вы можете получить это, используя

my @ROM = do {
    local $/ = \1;
    map ord, <$fh>;
};

Я думаю, что это отвечает сути вашего вопроса.

Теперь, что касается идеи не читать весь файл в массив, а использоватьсам файл в качестве исполняемого образа, вот небольшой скрипт, который я написал на основе более ранних версий этого поста и отзывов.

Обратите внимание, что вся функция скалярного дескриптора файла предназначена для предоставления лесов для запуска ПЗУ, включенных в статью.Вы связались путем копирования и вставки вместо того, чтобы создавать двоичные файлы.

#!/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, $.,
            );
        }
    }
}

Вывод:

Executing rom1
A
rom1: HALT

Executing rom2
ABCDE
rom2: HALT

Executing rom3
Enter a Char : d
You Typed : d
rom3: HALT
1 голос
/ 22 ноября 2011

Если $opcode - undef, то $ROM[$IP] - undef.

Во второй версии кода вы не показали заполнение @ROM, поэтому неудивительно, что $ROM[$IP]тогда undef.

Первая версия кода ближе к тому, что вы хотите.Однако @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>
};

После исправления остальной части кода это выглядит следующим образом:

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)); }
}

Обратите внимание, что :raw не совсем эквивалентно binmode несмотря на документацию по этому вопросу.

Обратите внимание на использование или ord для преобразования символов в их числовое значение и использование операторов числового сравнения (==) для сравненияэти цифры.

Обратите внимание, что я использовал шестнадцатеричные числа.Шестнадцатеричный код (или, возможно, восьмеричный, в зависимости от эмулируемой машины) будет более понятным, чем использование десятичного числа, поскольку подобные коды операций имеют тенденцию варьироваться в битах, а не в цифрах.Кроме того, номера, которыми вы пользовались, уже в шестнадцатеричном формате, поэтому код операции 11 на самом деле семнадцать .

$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.
0 голосов
/ 23 ноября 2011

Я понял ... теперь все работает, как нужно:

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++;
    }
0 голосов
/ 21 ноября 2011

Если вы просто хотите разбить байт файла на @ROM, попробуйте

local $/; # get the whole file once, see perldoc perlvar for details
my @ROM = split //, <F>;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...