File Coverage

blib/lib/VM/Dreamer.pm
Criterion Covered Total %
statement 35 51 68.6
branch 4 12 33.3
condition n/a
subroutine 8 10 80.0
pod 2 5 40.0
total 49 78 62.8


line stmt bran cond sub pod time code
1             package VM::Dreamer;
2              
3 2     2   24390 use strict;
  2         4  
  2         71  
4 2     2   11 use warnings;
  2         4  
  2         94  
5              
6             our $VERSION = '0.851';
7              
8 2     2   1240 use VM::Dreamer::Operation qw( add_one_to_counter get_new_machine );
  2         4  
  2         135  
9 2     2   1126 use VM::Dreamer::Util qw( parse_program_line parse_next_instruction );
  2         4  
  2         145  
10 2     2   1064 use VM::Dreamer::Validate qw( validate_definition build_valid_line_regex );
  2         6  
  2         1443  
11              
12             require Exporter;
13              
14             our @ISA = qw(Exporter);
15             our @EXPORT_OK = qw( initialize_machine load_program get_next_instruction increment_counter execute_next_instruction );
16              
17              
18             sub initialize_machine {
19 18     18 1 14377 my $definition = shift;
20 18         30 my $instruction_set = shift;
21              
22 18         49 validate_definition($definition);
23              
24 6         28 return get_new_machine( $definition->{base}, $definition->{op_code_width}, $definition->{operand_width}, $instruction_set );
25             }
26              
27             sub load_program {
28 3     3 1 13 my( $machine, $program ) = @_;
29              
30 3 50       155 open( my $in, "<", $program )
31             or die "Could not open program $program: $!\n";
32              
33 3 50       26 unless( ! -z $in ) {
34 0         0 die "Your file is empty!\n";
35             }
36              
37             # this is a situation in which having a single machine rather than
38             # creating a framework to define arbitrary ones would be much
39             # simpler
40              
41 3         8 my $valid_line_regex = build_valid_line_regex($machine);
42              
43 3         5 my $line_count = 1;
44 3         51 while( <$in> ) {
45 41         44 my $line = $_;
46 41         43 chomp $line;
47              
48 41 50       432 unless( $line =~ $valid_line_regex ) {
49 0         0 die "Line $line_count was not properly formatted: $line\n";
50             }
51              
52 41         127 my ( $address, $instruction ) = parse_program_line($line);
53              
54 41 50       74 if( $machine->{memory}->{$address} ) {
55 0         0 die "Address $address was used a second time on line $line_count: $line\n";
56             }
57             else {
58 41         154 $machine->{memory}->{$address} = $instruction;
59             }
60             }
61              
62 3         64 return {};
63             }
64              
65             sub get_next_instruction {
66 0     0 0 0 my $machine = shift;
67              
68 0         0 my $address = join( '', @{ $machine->{counter} } );
  0         0  
69 0         0 my $instruction = $machine->{memory}->{$address};
70            
71 0 0       0 if ($instruction) {
72 0         0 $machine->{next_instruction} = $instruction;
73             }
74             else {
75 0         0 die "Address $address did not hold an instruction. Most likely the addresses in your program don't start at 0 or skipped a number.\n";
76             # would be nice to add the previous line in the program to this error message
77             }
78             }
79            
80             sub increment_counter {
81 3     3 0 2729 my $machine = shift;
82              
83 3         17 $machine->{counter} = add_one_to_counter( $machine->{counter}, $machine->{meta}->{greatest}->{digit} );
84              
85 3         9 return 0;
86             }
87              
88             sub execute_next_instruction {
89 0     0 0   my $machine = shift;
90              
91 0           my ( $op_code, $operand ) = parse_next_instruction($machine);
92              
93 0 0         if ( $machine->{instruction_set}->{$op_code} ) {
94 0           $machine->{instruction_set}->{$op_code}->( $machine, $operand );
95             }
96             else {
97 0           die "The op_code $op_code is not known in the instruction set for $machine->{name}\n";
98             }
99             # move this to VM::Dreamer::Error ?
100              
101 0           return 0;
102             }
103              
104             1;
105              
106              
107             =pod
108              
109             =head1 NAME
110              
111             Dreamer - An arbitrary emulator of one-operand computers
112              
113             =head1 VERSION
114              
115             Version 0.851
116              
117             =head1 SYNOPSIS
118              
119             use VM::Dreamer qw( initialize_machine load_program get_next_instruction increment_counter execute_next_instruction );
120             use VM::Dreamer::Languages::Grasshopper qw( get_instruction_set );
121              
122             my $definition = {
123             base => 10,
124             op_code_width => 1,
125             operand_width => 2
126             };
127              
128             my $instruction_set = get_instruction_set();
129             my $machine = initialize_machine( $definition, $instruction_set );
130              
131             my $program = $ARGV[0];
132             load_program( $machine, $program );
133              
134             until( $machine->{halt} ) {
135             get_next_instruction($machine);
136             increment_counter($machine);
137             execute_next_instruction($machine);
138             }
139              
140             =head1 DESCRIPTION
141              
142             Dreamer is a framework to emulate arbitrary 1-operand computers. It comes with pre-defined machines, but also lets you define and operate your own. It was written as a generalization of the Little Man Computer to help myself and others understand the foundations of Computer Science.
143              
144             =head1 EXPORT
145              
146             initialize_machine
147             load_program
148             get_next_instruction
149             increment_counter
150             execute_next_instruction
151              
152             =head1 FUNCTIONS
153              
154             =head2 initialize_machine
155              
156             Takes two hash_refs as input and returns a hash ref.
157              
158             The first input is the machine's definition. It should have and the second is it's instruction set.
159              
160             =head2 load_program
161              
162             Takes a reference to your machine and a path to a program for your machine. If the file cannot be opened or is not valid, it raises an exception. Otherwise it puts the instructions into the corresponding addresses in your machine's memory and returns 0.
163              
164             =head1 GETTING STARTED
165              
166             The easiest way to get started is to execute a pre-written program for a pre-defined machine. This is as simple as:
167              
168             $ grasshopper sample_code/grasshopper/io
169              
170             It will just ask you for a number and then give it back.
171              
172             After you do that, I would try writing your own program for Grasshopper. It comes with a well-written tutorial.
173              
174             From there, you could move on to writing programs for Eagle or you could start defining your own machine and writing code for it.
175              
176             =head2 Defining your own machine
177              
178             This is explained extensively in VM::Dreamer::Tutorial::MachineDefinition. If you'd rather learn by example, the one for Grasshopper should be a good example - see VM::Dreamer::Languages::Grasshopper.
179              
180             =head2 Developing the code base
181              
182             If you really get into this and you'd like to work on the code base, please let me know:
183              
184             =head1 NEXT STEPS
185              
186              
187             You can also find the documenation on defining your own machines. Then you can start writing your own programs for it. If you really get bored, you can write an assembler. If that isn't enough you can create a higher level language and write a compiler for your instruction set. Really, the sky's the limit ;-)
188              
189             I also thought that these simple machines could be of mathematical interest in the sense that they can operate on very, very large numbers in a relatively arbitrary base (though you could probably already do this with bc).
190              
191             They might also be intersting to people who want to translate machine code from one machine to another or to target multiple platforms from a generic assembly code like UNCOL.
192              
193             Enjoy!
194              
195             =head1 AUTHOR
196              
197             William Stevenson
198              
199             =head1 COPYRIGHT AND LICENSE
200              
201             This software is Copyright (c) 2013 by William Stevenson.
202              
203             This is free software, licensed under:
204              
205             The Artistic License 2.0 (GPL Compatible)
206              
207             =cut