1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
|
#!/usr/bin/perl -w
# render jumpman junior levels to PNG files, by reading the graphics
# and map data from the ROM image. input is 'jumpman.rom' in the current
# directory, output is level01.png through level12.png in the current dir.
# this code won't win any beauty contests, but it does work.
$verbose = 0; # 0 = quiet, 2 = very chatty
use Image::Magick;
use bytes;
sub getbyte {
return ord(substr($rom, $_[0], 1));
}
sub getword {
return getbyte($_[0]) + 256 * getbyte($_[0] + 1);
}
sub getdelta {
return $_[0] < 128 ? ($_[0]) : ($_[0] - 256);
}
# placeholder colors. ideally we'd have a full Atari palette, and read
# the color register values from the level desc in the rom.
%palette = (
0 => [ 0x0000, 0x0000, 0x0000 ], # background
1 => [ 0x0000, 0x0000, 0x8000 ], # girders, up-ropes
2 => [ 0x0000, 0x8000, 0x0000 ], # ladders, down-ropes
3 => [ 0x8000, 0x0000, 0x8000 ], # bombs
);
sub draw {
my ($img, $shape, $dx, $dy, $xpos, $ypos, $copies) = @_;
warn sprintf "drawing shape %04x at $xpos, $ypos, $copies copies, delta ($dx, $dy)\n", $shape if $verbose > 1;
while($copies--) {
my $width;
my $addr = $shape;
while(($width = getbyte($addr++)) != 0xff) {
my $xoffs = getdelta(getbyte($addr++));
my $yoffs = getdelta(getbyte($addr++));
for(my $p = 0; $p < $width; $p++) {
my $pixel = getbyte($addr++);
my $color = $palette{$pixel};
$img->SetPixel(x => $xpos + $xoffs + $p, y => $ypos + $yoffs, color => $color);
}
}
$xpos += $dx;
$ypos += $dy;
}
}
open ROM, "<jumpmanjr.rom" or die $!;
if((read ROM, $rom, 0x4000, 0x8000) != 0x4000) {
die "couldn't read ROM\n";
}
for my $level (1..12) {
my $img = Image::Magick->new;
$img->Set(size => '160x100');
$img->ReadImage('canvas:black');
my $desc = 0xa000 + 0x40 * ($level - 1) + 22;
my $mapaddr = getword($desc);
if($level == 9) {
# show real map for blackout, not the blank one.
# the bombs are still invisible though.
$mapaddr = 0xb000;
}
warn sprintf("level $level, map pointer at \$%04x, points to \$%04x\n", $desc, $mapaddr) if $verbose;
# don't initialize these, we want warnings if some level
# tries to draw without selecting shape and direction.
my $shape;
my $copies;
my $dx;
my $dy;
my $xpos;
my $ypos;
while(1) {
my $opcode = getbyte($mapaddr);
if($opcode eq 0xff) {
warn sprintf " got end opcode (\$ff) at \$%04x\n", $mapaddr if $verbose > 1;
last;
}
my $operand1 = getbyte($mapaddr + 1);
my $operand2 = getbyte($mapaddr + 2);
my $opword = getword($mapaddr + 1);
warn sprintf "%04x: %02x %02x %02x\n", $mapaddr, $opcode, $operand1, $operand2 if $verbose > 1;
$mapaddr += 3;
if($opcode == 0xfe) {
$shape = $opword;
warn sprintf "set shape %04x\n", $shape if $verbose > 1;
} elsif($opcode == 0xfd) {
$dx = getdelta($operand1);
$dy = getdelta($operand2);
warn "set delta X $dx, delta Y $dy\n" if $verbose > 1;
} elsif($opcode == 0xfc) {
$mapaddr = $opword;
next;
} else {
$xpos = $opcode;
$ypos = $operand1;
$copies = $operand2;
draw($img, $shape, $dx, $dy, $xpos, $ypos, $copies);
}
}
my $pngfile = sprintf("level%02d.png", $level);
open my $out, ">$pngfile";
$img->Write(file => $out, filename => $pngfile);
close $out;
warn "wrote $pngfile\n";
}
|