File Coverage

blib/lib/CPU/Z80/Assembler/List.pm
Criterion Covered Total %
statement 15 54 27.7
branch 0 14 0.0
condition 0 11 0.0
subroutine 5 9 55.5
pod 2 2 100.0
total 22 90 24.4


line stmt bran cond sub pod time code
1             # $Id$
2              
3             package CPU::Z80::Assembler::List;
4              
5             #------------------------------------------------------------------------------
6              
7             =head1 NAME
8              
9             CPU::Z80::Assembler::List - Assembly listing output class
10              
11             =cut
12              
13             #------------------------------------------------------------------------------
14              
15 31     31   228 use strict;
  31         98  
  31         1015  
16 31     31   196 use warnings;
  31         70  
  31         810  
17              
18 31     31   16782 use Text::Tabs;
  31         25412  
  31         4151  
19 31     31   257 use Iterator::Simple::Lookahead;
  31         68  
  31         339  
20              
21             our $VERSION = '2.23';
22              
23             use Class::Struct (
24 31         229 output => '$', # output file handle for the list
25             input => '$', # input lines or iterators passed to z80asm
26            
27             _line_stream => '$', # input line stream with whole program
28             _address => '$', # output address
29            
30             _current_line => '$', # line of the current opcode(s)
31             _current_address => '$', # address of the current opcode(s)
32             _current_bytes => '$', # all bytes of all opcodes of _current_line
33 31     31   18218 );
  31         59078  
34              
35             #------------------------------------------------------------------------------
36              
37             =head1 SYNOPSIS
38              
39             use CPU::Z80::Assembler::List;
40             my $lst = CPU::Z80::Assembler::List->new(input => $asm_input, output => \*STDOUT);
41             $lst->add($line, $address, $bytes);
42             $lst->flush();
43              
44             =head1 DESCRIPTION
45              
46             This module handles the output of the assembly listing file.
47             It is fead with each assembled opcode and generates the full
48             assembly list file on the given output handle.
49              
50             If output is undef, does not generate any output.
51              
52             =head1 EXPORTS
53              
54             Nothing.
55              
56             =head1 FUNCTIONS
57              
58             =head2 new
59              
60             my $lst = CPU::Z80::Assembler::List->new(input => $asm_input, output => \*STDOUT);
61              
62             Creates a new object, see L.
63              
64             =head2 input
65              
66             input is the input as passed to z80asm, i.e. list of text lines to parse or iterators
67             that return text lines to parse.
68              
69             =head2 output
70              
71             output is the output file handle to receive the listing file. It can be an open
72             file for writing, or one of the standard output file handles.
73              
74             If output is undefined, no output is generated.
75              
76             =cut
77              
78             #------------------------------------------------------------------------------
79              
80             =head2 add
81              
82             $self->add($line, $address, $bytes);
83              
84             Adds a new opcode to the output listing. Receives the opcode L,
85             address and bytes. Generates the output lines including this new opcode.
86              
87             The output is held in an internal buffer until an opcode for the next line is passed to a
88             subsequent add() call.
89              
90             The last output line
91             is only output on flush() or DESTROY()
92              
93             =cut
94              
95             #------------------------------------------------------------------------------
96              
97 0     0 1   sub add { my($self, $opcode_line, $address, $bytes) = @_;
98 0           my $output = $self->output;
99 0 0         if ($output) {
100 0 0 0       if ($self->_current_line && $self->_current_line != $opcode_line) {
101 0           $self->flush(); # different line
102             }
103            
104 0 0         if (! $self->_current_line) { # new or different line
105 0           $self->_current_line($opcode_line);
106 0           $self->_current_address($address);
107 0           $self->_current_bytes($bytes);
108             }
109             else { # same line as last
110 0           $self->_current_bytes($self->_current_bytes . $bytes);
111             }
112             }
113             }
114              
115             #------------------------------------------------------------------------------
116              
117             =head2 flush
118              
119             $self->flush();
120              
121             Dumps the current line to the output. Called on DESTROY().
122              
123             =cut
124              
125             #------------------------------------------------------------------------------
126              
127 0     0 1   sub flush { my($self) = @_;
128 0           my $output = $self->output;
129 0 0 0       if ($output && $self->_current_line) {
130              
131             # print all input lines up to the current position
132 0           my $rewind_count;
133             my $line;
134 0           for (;;) {
135 0   0       while (! defined($self->_line_stream) ||
136             ! defined($line = $self->_line_stream->next)) {
137 0           $rewind_count++; # end of input, rewind
138 0 0         die "Cannot find $line in list" if $rewind_count > 1;
139             # assert input is OK
140             $self->_line_stream(CPU::Z80::Assembler::z80preprocessor(
141 0           @{$self->input}));
  0            
142             }
143            
144 0 0         last if $line == $self->_current_line; # found current line
145 0           print $output $self->_format_line($self->_address, $line), "\n";
146             }
147            
148 0           print $output $self->_format_line($self->_current_address, $self->_current_line);
149 0           for (split(//, $self->_current_bytes)) {
150 0           print $output sprintf("%02X ", ord($_));
151             }
152 0           print $output "\n";
153              
154 0           $self->_address($self->_current_address);
155 0           $self->_current_line(undef);
156 0           $self->_current_address(undef);
157 0           $self->_current_bytes(undef);
158             }
159             }
160              
161 0     0     sub DESTROY { my($self) = @_;
162 0           $self->flush();
163             }
164              
165             #------------------------------------------------------------------------------
166              
167 0     0     sub _format_line { my($self, $address, $line) = @_;
168 0   0       $address ||= 0;
169 0           my $text = $line->text;
170 0           $text = expand($text); # untabify
171 0           $text =~ s/\s+$//;
172 0 0         substr($text, 34) = ' ...' if(length($text) > 37);
173 0           return sprintf("0x%04X: %-38s | ", $address, $text);
174             }
175