File Coverage

blib/lib/CPU/Emulator/Memory.pm
Criterion Covered Total %
statement 65 65 100.0
branch 32 34 94.1
condition 15 16 93.7
subroutine 13 13 100.0
pod 7 7 100.0
total 132 135 97.7


line stmt bran cond sub pod time code
1             package CPU::Emulator::Memory;
2              
3 7     7   276310 use strict;
  7         39  
  7         207  
4 7     7   49 use warnings;
  7         15  
  7         205  
5              
6 7     7   39 use vars qw($VERSION);
  7         12  
  7         6945  
7              
8             $VERSION = '1.1005';
9              
10             =head1 NAME
11              
12             CPU::Emulator::Memory - memory for a CPU emulator
13              
14             =head1 SYNOPSIS
15              
16             my $memory = CPU::Emulator::Memory->new();
17             $memory->poke(0xBEEF, ord('s'));
18            
19             my $value = $memory->peek(0xBEEF); # 115 == ord('s')
20              
21             =head1 DESCRIPTION
22              
23             This class provides a flat array of values which you can 'peek'
24             and 'poke'.
25              
26             =head1 METHODS
27              
28             =head2 new
29              
30             The constructor returns an object representing a flat memory
31             space addressable by byte. It takes four optional named parameters:
32              
33             =over
34              
35             =item file
36              
37             if provided, will provide a disc-based backup of the
38             RAM represented. This file will be read when the object is created
39             (if it exists) and written whenever anything is altered. If no
40             file exists or no filename is provided, then memory is initialised
41             to all zeroes. If the file exists it must be writeable and of the
42             correct size.
43              
44             =item endianness
45              
46             defaults to LITTLE, can be set to BIG. This matters for the peek16
47             and poke16 methods.
48              
49             =item size
50              
51             the size of the memory to emulate. This defaults to 64K (65536 bytes),
52             or to the length of the string passed to C (plus C if
53             applicable).
54             .
55             Note that this does *not* have to be a power of two.
56              
57             =item bytes
58              
59             A string of characters with which to initialise the memory. Note that
60             the length must match the size parameter.
61              
62             =item org
63              
64             an integer, Used in conjunction with C, load the data at the specified
65             offset in bytes
66              
67             =back
68              
69             =cut
70              
71             sub new {
72 14     14 1 7862 my($class, %params) = @_;
73 14 100 100     70 if(exists($params{bytes}) && exists($params{org})) {
74 1         5 $params{bytes} = (chr(0) x $params{org}).$params{bytes};
75             }
76              
77 14 100       46 if(!exists($params{size})) {
78 9 100       34 if(exists($params{bytes})) {
79 1         5 $params{size} = length($params{bytes});
80             } else {
81 8         21 $params{size} = 0x10000;
82             }
83             }
84 14 100       37 if(!exists($params{bytes})) {
85 10         552 $params{bytes} = chr(0) x $params{size};
86             }
87             die("bytes and size don't match\n")
88 14 100       70 if(length($params{bytes}) != $params{size});
89              
90 13 100       48 if(exists($params{file})) {
91 3 100       87 if(-e $params{file}) {
92 1         9 $params{bytes} = $class->_readRAM($params{file}, $params{size});
93             } else {
94             $class->_writeRAM($params{file}, $params{bytes})
95 2         20 }
96             }
97             return bless(
98             {
99             contents => $params{bytes},
100             size => $params{size},
101             ($params{file} ? (file => $params{file}) : ()),
102 13 100 100     228 endianness => $params{endianness} || 'LITTLE'
103             },
104             $class
105             );
106             }
107              
108             =head2 peek, peek8
109              
110             This method takes a single parameter, an address from 0 the memory size - 1.
111             It returns the value stored at that address, taking account of what
112             secondary memory banks are active. 'peek8' is simply another name
113             for the same function, the suffix indicating that it returns an 8
114             bit (ie one byte) value.
115              
116             =head2 peek16
117              
118             As peek and peek8, except it returns a 16 bit value. This is where
119             endianness matters.
120              
121             =cut
122              
123             sub peek8 {
124 3     3 1 12 my($self, $addr) = @_;
125 3         10 $self->peek($addr);
126             }
127             sub peek16 {
128 3     3 1 21 my($self, $address) = @_;
129             # assume little-endian
130 3         9 my $r = $self->peek($address) + 256 * $self->peek($address + 1);
131             # swap bytes if necessary
132 3 100       29 if($self->{endianness} eq 'BIG') {
133 1         5 $r = (($r & 0xFF) << 8) + int($r / 256);
134             }
135 3         15 return $r;
136             }
137             sub peek {
138 27     27 1 1514 my($self, $addr) = @_;
139 27 100 100     170 die("Address $addr out of range") if($addr< 0 || $addr > $self->{size} - 1);
140 23         114 return ord(substr($self->{contents}, $addr, 1));
141             }
142              
143             =head2 poke, poke8
144              
145             This method takes two parameters, an address and a byte value.
146             The value is written to the address.
147              
148             It returns 1 if something was written, or 0 if nothing was written.
149              
150             =head2 poke16
151              
152             This method takes two parameters, an address and a 16-bit value.
153             The value is written to memory as two bytes at the address specified
154             and the following one. This is where endianness matters.
155              
156             Return values are undefined.
157              
158             =cut
159              
160             sub poke8 {
161 1     1 1 4 my($self, $addr, $value) = @_;
162 1         4 $self->poke($addr, $value);
163             }
164             sub poke16 {
165 2     2 1 13 my($self, $addr, $value) = @_;
166             # if BIGendian, swap bytes, ...
167 2 100       13 if($self->{endianness} eq 'BIG') {
168 1         7 $value = (($value & 0xFF) << 8) + int($value / 256);
169             }
170             # write in little-endian order
171 2         13 $self->poke($addr, $value & 0xFF);
172 2         8 $self->poke($addr + 1, ($value >> 8));
173             }
174             sub poke {
175 13     13 1 1286 my($self, $addr, $value) = @_;
176 13 100 100     82 die("Value $value out of range") if($value < 0 || $value > 255);
177 11 100 100     62 die("Address $addr out of range") if($addr< 0 || $addr > $self->{size} - 1);
178 9         22 $value = chr($value);
179 9         33 substr($self->{contents}, $addr, 1) = $value;
180             $self->_writeRAM($self->{file}, $self->{contents})
181 9 100       30 if(exists($self->{file}));
182 9         28 return 1;
183             }
184              
185             # input: filename, required size
186             # output: file contents, or fatal error
187             sub _read_file {
188 10     10   24 my($self, $file, $size) = @_;
189 10         44 local $/ = undef;
190 10 50       356 open(my $fh, $file) || die("Couldn't read $file\n");
191             # Win32 is stupid, see RT 62379
192 10         43 binmode($fh);
193 10         323 my $contents = <$fh>;
194 10 100       64 die("$file is wrong size\n") unless(length($contents) == $size);
195 9         73 close($fh);
196 9         83 return $contents;
197             }
198              
199             # input: filename, required size
200             # output: file contents, or fatal error
201             sub _readRAM {
202 1     1   4 my($self, $file, $size) = @_;
203 1         5 my $contents = $self->_read_file($file, $size);
204 1         6 $self->_writeRAM($file, $contents);
205 1         7 return $contents;
206             }
207              
208             # input: filename, data
209             # output: none, fatal on error
210             sub _writeRAM {
211 9     9   25 my($self, $file, $contents) = @_;
212 9 50       10908 open(my $fh, '>', $file) || die("Can't write $file\n");
213 9         48 binmode($fh);
214 9   50     1078 print $fh $contents || die("Can't write $file\n");
215 9         532 close($fh);
216             }
217              
218             =head1 SUBCLASSING
219              
220             Most useful emulators will need a subclass of this module. For an example,
221             look at the CPU::Emulator::Memory::Banked module bundled with it, which
222             adds some methods of its own, and overrides the peek and poke methods.
223             Note that {peek,poke}{8,16} are *not* overridden but still get all the
224             extra magic, as they are simple wrappers around the peek and poke methods.
225              
226             You may use the _readRAM and _writeRAM methods for disk-backed RAM, and
227             _read_file may be useful for ROM. These
228             are only useful for subclasses:
229              
230             =over
231              
232             =item _read_file
233              
234             Takes a filename and the required size, returns the file's contents
235              
236             =item _readRAM
237              
238             Takes a filename and the required size, returns the file's contents and
239             checks that the file is writeable.
240              
241             =item _writeRAM
242              
243             Takes a filename and a chunk of data, writes the data to the file.
244              
245             =back
246              
247             =head1 BUGS/WARNINGS/LIMITATIONS
248              
249             It is assumed that the emulated memory will fit in the host's memory.
250              
251             When memory is disk-backed, the entire memory is written to disk on each
252             poke().
253              
254             The size of a byte in the emulated memory is the same as that of a char
255             on the host machine. Perl only runs on machines with 8 bit bytes.
256              
257             Bug reports should be made on Github or by email.
258              
259             =head1 FEEDBACK
260              
261             I welcome feedback about my code, including constructive criticism
262             and bug reports. The best bug reports include files that I can add
263             to the test suite, which fail with the current code in CVS and will
264             pass once I've fixed the bug.
265              
266             Feature requests are far more likely to get implemented if you submit
267             a patch yourself.
268              
269             =head1 SOURCE CODE REPOSITORY
270              
271             L
272              
273             =head1 THANKS TO
274              
275             Paulo Custodio for finding and fixing some bugs on Win32, see RT 62375,
276             62379
277              
278             =head1 AUTHOR, LICENCE and COPYRIGHT
279              
280             Copyright 2008 David Cantrell EFE
281              
282             This module is free-as-in-speech software, and may be used,
283             distributed, and modified under the same terms as Perl itself.
284              
285             =head1 CONSPIRACY
286              
287             This module is also free-as-in-mason software.
288              
289             =cut
290              
291             1;