#! /usr/bin/perl # This script converts intel-hex data into fast flash download # binary format, for use with the PJRC MP3 player fast flash # firmware download. # Typical usages: # hex2fdl mp3player.hex > mp3player.fdl # hex2fdl mp3player.hex | xmit115200 $ascii_output = 0; $code_zero = 0x5A; $code_two_ff = 0x2D; $code_three_ff = 0x19; $code_escape_byte = 0x59; $code_address = 0x29; $code_115200_baud = 0x6A; $code_checksum = 0x3C; $code_end_of_data = 0x58; $code_undef_skip = 0x1D; $code_abort = 0x1B; $h = '[0-9A-F]'; $addr_hi = 0; while (<>) { $addr_hi = 0x00000, next if /^:020000020000FC/; $addr_hi = 0x10000, next if /^:020000021000EC/; next unless /^:($h{2})/; #todo: this parsing is slow, can it be faster somehow? $len = hex($1) * 2; $hh = "$h\{$len\}"; next unless /^:($h{2})($h{4})00($hh)($h{2})/; $addr = hex($2) + $addr_hi; $data_text = $3; $sum = hex($4); #todo: check the checksum for ($i=0; $i<$len/2; $i++) { $data_text =~ /^($h{2})/; $data[$addr + $i] = hex($1); $data_text = $'; } } $cksum = 0; output_range(0x00000, 0x0FFFF); output_range(0x10000, 0x1FFFF); emit_byte($code_end_of_data); print "\n" if $ascii_output; sub output_range { my $begin = shift; my $end = shift; my $addr; my $undef_count = 100000; my $ff_count = 0; my $data_count = 0; for ($addr=$begin; $addr<$end+1; $addr++) { if (!defined($data[$addr])) { if ($ff_count > 0) { emit_ff($ff_count); $ff_count = 0; } $undef_count++; } else { if ($undef_count > 0) { if ($undef_count < 120) { emit_undef_skip($undef_count); } else { emit_addr($addr); } $undef_count = 0; } if ($data[$addr] == 0xFF) { $ff_count++; } else { if ($ff_count > 0) { emit_ff($ff_count); $ff_count = 0; } emit_data($data[$addr]); $data_count++; if ($data_count > 400) { emit_cksum(); $data_count = 0; } } } } emit_ff($ff_count) if $ff_count > 0; emit_cksum() if $data_count > 0; } sub emit_addr { my $addr = shift; my $byte; warn sprintf "Addr: %04X\n", $addr; emit_byte($code_address); $byte = ($addr & 0x7F) | 0x80; $cksum = ($cksum + (($byte ^ 0x6B) << 8)) & 0xFFFF; emit_byte($byte); $byte = (($addr >> 8) & 0x7F) | 0x80; $cksum = ($cksum + (($byte ^ 0x5A) << 8)) & 0xFFFF; emit_byte($byte); $byte = (($addr >> 7) & 1) | (($addr >> 14) & 6) | 0x80; $cksum = ($cksum + ($byte << 8)) & 0xFFFF; emit_byte($byte); } sub emit_cksum { emit_byte($code_checksum); #warn("cksum: $cksum\n"); emit_byte(($cksum & 0x7F) | 0x80); emit_byte((($cksum >> 8) & 0x7F) | 0x80); $cksum = 0; } sub emit_undef_skip { my $num = shift; my $byte; #warn("undef: $num\n"); emit_byte($code_undef_skip); $byte = $num | 0x80; $cksum = ($cksum + (($byte ^ 0x4E) << 8)) & 0xFFFF; emit_byte($byte); } sub emit_ff { my $num = shift; while ($num >= 3) { #warn("Three FFs\n"); $cksum = ($cksum + 0x3C00) & 0xFFFF; emit_byte($code_three_ff); $num -= 3; } if ($num == 2) { #warn("Two FFs\n"); $cksum = ($cksum + 0x5D00) & 0xFFFF; emit_byte($code_two_ff); } if ($num == 1) { #warn("One FFs\n"); emit_data(0xFF); } } sub emit_data { my $byte = shift; $cksum = ($cksum + $byte + (($byte ^ 0x56) << 8)) & 0xFFFF; if ($byte == 0) { emit_byte($code_zero); return; } if ($byte == $code_zero || $byte == $code_two_ff || $byte == $code_three_ff || $byte == $code_escape_byte || $byte == $code_address || $byte == $code_115200_baud || $byte == $code_checksum || $byte == $code_end_of_data || $byte == $code_undef_skip || $byte == $code_abort) { #warn("ESC: $byte\n"); emit_byte($code_escape_byte); emit_byte($byte | 0x80); return; } emit_byte($byte); } sub emit_byte { my $byte = shift; if ($ascii_output) { printf("%02X ", $byte); print "\n" if ++$bytecount % 16 == 0; } else { print pack('C', $byte); } }