diff options
Diffstat (limited to 'altoslib')
-rw-r--r-- | altoslib/AltosDebug.java | 280 | ||||
-rw-r--r-- | altoslib/AltosFlash.java | 353 | ||||
-rw-r--r-- | altoslib/AltosFlashListener.java | 22 | ||||
-rw-r--r-- | altoslib/AltosHexfile.java | 298 | ||||
-rw-r--r-- | altoslib/AltosRomconfig.java | 148 | ||||
-rw-r--r-- | altoslib/Makefile.am | 5 |
6 files changed, 1106 insertions, 0 deletions
diff --git a/altoslib/AltosDebug.java b/altoslib/AltosDebug.java new file mode 100644 index 00000000..4d8e3ae7 --- /dev/null +++ b/altoslib/AltosDebug.java @@ -0,0 +1,280 @@ +/* + * Copyright © 2010 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +package org.altusmetrum.altoslib_1; + +import java.io.*; + +public class AltosDebug { + + public static final byte WR_CONFIG = 0x1d; + public static final byte RD_CONFIG = 0x24; + public static final byte CONFIG_TIMERS_OFF = (1 << 3); + public static final byte CONFIG_DMA_PAUSE = (1 << 2); + public static final byte CONFIG_TIMER_SUSPEND = (1 << 1); + public static final byte SET_FLASH_INFO_PAGE = (1 << 0); + + public static final byte GET_PC = 0x28; + public static final byte READ_STATUS = 0x34; + public static final byte STATUS_CHIP_ERASE_DONE = (byte) (1 << 7); + public static final byte STATUS_PCON_IDLE = (1 << 6); + public static final byte STATUS_CPU_HALTED = (1 << 5); + public static final byte STATUS_POWER_MODE_0 = (1 << 4); + public static final byte STATUS_HALT_STATUS = (1 << 3); + public static final byte STATUS_DEBUG_LOCKED = (1 << 2); + public static final byte STATUS_OSCILLATOR_STABLE = (1 << 1); + public static final byte STATUS_STACK_OVERFLOW = (1 << 0); + + public static final byte SET_HW_BRKPNT = 0x3b; + public static byte HW_BRKPNT_N(byte n) { return (byte) ((n) << 3); } + public static final byte HW_BRKPNT_N_MASK = (0x3 << 3); + public static final byte HW_BRKPNT_ENABLE = (1 << 2); + + public static final byte HALT = 0x44; + public static final byte RESUME = 0x4c; + public static byte DEBUG_INSTR(byte n) { return (byte) (0x54|(n)); } + public static final byte STEP_INSTR = 0x5c; + public static byte STEP_REPLACE(byte n) { return (byte) (0x64|(n)); } + public static final byte GET_CHIP_ID = 0x68; + + + AltosLink link; + + boolean debug_mode; + + void ensure_debug_mode() { + if (!debug_mode) { + link.printf("D\n"); + try { + link.flush_input(); + } catch (InterruptedException ie) { + } + debug_mode = true; + } + } + + void dump_memory(String header, int address, byte[] bytes, int start, int len) { + System.out.printf("%s\n", header); + for (int j = 0; j < len; j++) { + if ((j & 15) == 0) { + if (j != 0) + System.out.printf("\n"); + System.out.printf ("%04x:", address + j); + } + System.out.printf(" %02x", bytes[start + j]); + } + System.out.printf("\n"); + } + + public void close() { + link.close(); + } + + /* + * Write target memory + */ + public void write_memory(int address, byte[] bytes, int start, int len) { + ensure_debug_mode(); +// dump_memory("write_memory", address, bytes, start, len); + link.printf("O %x %x\n", len, address); + for (int i = 0; i < len; i++) + link.printf("%02x", bytes[start + i]); + } + + public void write_memory(int address, byte[] bytes) { + write_memory(address, bytes, 0, bytes.length); + } + + /* + * Read target memory + */ + public byte[] read_memory(int address, int length) + throws IOException, InterruptedException { + byte[] data = new byte[length]; + + link.flush_input(); + ensure_debug_mode(); + link.printf("I %x %x\n", length, address); + int i = 0; + int start = 0; + while (i < length) { + String line = link.get_reply().trim(); + if (!AltosLib.ishex(line) || line.length() % 2 != 0) + throw new IOException( + String.format + ("Invalid reply \"%s\"", line)); + int this_time = line.length() / 2; + for (int j = 0; j < this_time; j++) + data[start + j] = (byte) ((AltosLib.fromhex(line.charAt(j*2)) << 4) + + AltosLib.fromhex(line.charAt(j*2+1))); + start += this_time; + i += this_time; + } +// dump_memory("read_memory", address, data, 0, length); + + return data; + } + + /* + * Write raw bytes to the debug link using the 'P' command + */ + public void write_bytes(byte[] bytes) throws IOException { + int i = 0; + ensure_debug_mode(); + while (i < bytes.length) { + int this_time = bytes.length - i; + if (this_time > 8) + this_time = 0; + link.printf("P"); + for (int j = 0; j < this_time; j++) + link.printf(" %02x", bytes[i+j]); + link.printf("\n"); + i += this_time; + } + } + + public void write_byte(byte b) throws IOException { + byte[] bytes = { b }; + write_bytes(bytes); + } + + /* + * Read raw bytes from the debug link using the 'G' command + */ + public byte[] read_bytes(int length) + throws IOException, InterruptedException { + + link.flush_input(); + ensure_debug_mode(); + link.printf("G %x\n", length); + int i = 0; + byte[] data = new byte[length]; + while (i < length) { + String line = link.get_reply(); + + if (line == null) + throw new IOException("Timeout in read_bytes"); + line = line.trim(); + String tokens[] = line.split("\\s+"); + for (int j = 0; j < tokens.length; j++) { + if (!AltosLib.ishex(tokens[j]) || + tokens[j].length() != 2) + throw new IOException( + String.format + ("Invalid read_bytes reply \"%s\"", line)); + try { + if (i + j >= length) + throw new IOException( + String.format + ("Invalid read_bytes reply \"%s\"", line)); + else + data[i + j] = (byte) Integer.parseInt(tokens[j], 16); + } catch (NumberFormatException ne) { + throw new IOException( + String.format + ("Invalid read_bytes reply \"%s\"", line)); + } + } + i += tokens.length; + } + return data; + } + + public byte read_byte() throws IOException, InterruptedException { + return read_bytes(1)[0]; + } + + public byte debug_instr(byte[] instruction) throws IOException, InterruptedException { + byte[] command = new byte[1 + instruction.length]; + command[0] = DEBUG_INSTR((byte) instruction.length); + for (int i = 0; i < instruction.length; i++) + command[i+1] = instruction[i]; + write_bytes(command); + return read_byte(); + } + + public byte resume() throws IOException, InterruptedException { + write_byte(RESUME); + return read_byte(); + } + + public int read_uint16() throws IOException, InterruptedException { + byte[] d = read_bytes(2); + return ((int) (d[0] & 0xff) << 8) | (d[1] & 0xff); + } + + public int read_uint8() throws IOException, InterruptedException { + byte[] d = read_bytes(1); + return (int) (d[0] & 0xff); + } + + public int get_chip_id() throws IOException, InterruptedException { + write_byte(GET_CHIP_ID); + return read_uint16(); + } + + public int get_pc() throws IOException, InterruptedException { + write_byte(GET_PC); + return read_uint16(); + } + + public byte read_status() throws IOException, InterruptedException { + write_byte(READ_STATUS); + return read_byte(); + } + + static final byte LJMP = 0x02; + + public void set_pc(int pc) throws IOException, InterruptedException { + byte high = (byte) (pc >> 8); + byte low = (byte) pc; + byte[] jump_mem = { LJMP, high, low }; + debug_instr(jump_mem); + } + + public boolean check_connection() throws IOException, InterruptedException { + byte reply = read_status(); + if ((reply & STATUS_CHIP_ERASE_DONE) == 0) + return false; + if ((reply & STATUS_PCON_IDLE) != 0) + return false; + if ((reply & STATUS_POWER_MODE_0) == 0) + return false; + return true; + } + + public AltosRomconfig romconfig() { + try { + byte[] bytes = read_memory(0xa0, 10); + return new AltosRomconfig(bytes, 0); + } catch (IOException ie) { + } catch (InterruptedException ie) { + } + return new AltosRomconfig(); + } + + /* + * Reset target + */ + public void reset() { + link.printf ("R\n"); + } + + public AltosDebug (AltosLink link) { + this.link = link; + } +}
\ No newline at end of file diff --git a/altoslib/AltosFlash.java b/altoslib/AltosFlash.java new file mode 100644 index 00000000..010274b9 --- /dev/null +++ b/altoslib/AltosFlash.java @@ -0,0 +1,353 @@ +/* + * Copyright © 2010 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +package org.altusmetrum.altoslib_1; + +import java.io.*; + +public class AltosFlash { + File file; + FileInputStream input; + AltosHexfile image; + AltosLink link; + AltosDebug debug; + AltosRomconfig rom_config; + boolean aborted; + AltosFlashListener listener; + + static final byte MOV_direct_data = (byte) 0x75; + static final byte MOV_DPTR_data16 = (byte) 0x90; + static final byte MOV_A_data = (byte) 0x74; + static final byte MOVX_atDPTR_A = (byte) 0xf0; + static final byte MOVX_A_atDPTR = (byte) 0xe0; + static final byte INC_DPTR = (byte) 0xa3; + static final byte TRAP = (byte) 0xa5; + + static final byte JB = (byte) 0x20; + + static final byte MOV_A_direct = (byte) 0xe5; + static final byte MOV_direct1_direct2 = (byte) 0x85; + static final byte MOV_direct_A = (byte) 0xf5; + static final byte MOV_R0_data = (byte) (0x78 | 0); + static final byte MOV_R1_data = (byte) (0x78 | 1); + static final byte MOV_R2_data = (byte) (0x78 | 2); + static final byte MOV_R3_data = (byte) (0x78 | 3); + static final byte MOV_R4_data = (byte) (0x78 | 4); + static final byte MOV_R5_data = (byte) (0x78 | 5); + static final byte MOV_R6_data = (byte) (0x78 | 6); + static final byte MOV_R7_data = (byte) (0x78 | 7); + static final byte DJNZ_R0_rel = (byte) (0xd8 | 0); + static final byte DJNZ_R1_rel = (byte) (0xd8 | 1); + static final byte DJNZ_R2_rel = (byte) (0xd8 | 2); + static final byte DJNZ_R3_rel = (byte) (0xd8 | 3); + static final byte DJNZ_R4_rel = (byte) (0xd8 | 4); + static final byte DJNZ_R5_rel = (byte) (0xd8 | 5); + static final byte DJNZ_R6_rel = (byte) (0xd8 | 6); + static final byte DJNZ_R7_rel = (byte) (0xd8 | 7); + + static final byte P1DIR = (byte) 0xFE; + static final byte P1 = (byte) 0x90; + + /* flash controller */ + static final byte FWT = (byte) 0xAB; + static final byte FADDRL = (byte) 0xAC; + static final byte FADDRH = (byte) 0xAD; + static final byte FCTL = (byte) 0xAE; + static final byte FCTL_BUSY = (byte) 0x80; + static final byte FCTL_BUSY_BIT = (byte) 7; + static final byte FCTL_SWBSY = (byte) 0x40; + static final byte FCTL_SWBSY_BIT = (byte) 6; + static final byte FCTL_CONTRD = (byte) 0x10; + static final byte FCTL_WRITE = (byte) 0x02; + static final byte FCTL_ERASE = (byte) 0x01; + static final byte FWDATA = (byte) 0xAF; + + static final byte ACC = (byte) 0xE0; + + /* offsets within the flash_page program */ + static final int FLASH_ADDR_HIGH = 8; + static final int FLASH_ADDR_LOW = 11; + static final int RAM_ADDR_HIGH = 13; + static final int RAM_ADDR_LOW = 14; + static final int FLASH_WORDS_HIGH = 16; + static final int FLASH_WORDS_LOW = 18; + static final int FLASH_TIMING = 21; + + /* sleep mode control */ + static final int SLEEP = (byte) 0xbe; + static final int SLEEP_USB_EN = (byte) 0x80; + static final int SLEEP_XOSC_STB = (byte) 0x40; + static final int SLEEP_HFRC_STB = (byte) 0x20; + static final int SLEEP_RST_MASK = (byte) 0x18; + static final int SLEEP_RST_POWERON = (byte) 0x00; + static final int SLEEP_RST_EXTERNAL = (byte) 0x10; + static final int SLEEP_RST_WATCHDOG = (byte) 0x08; + static final int SLEEP_OSC_PD = (byte) 0x04; + static final int SLEEP_MODE_MASK = (byte) 0x03; + static final int SLEEP_MODE_PM0 = (byte) 0x00; + static final int SLEEP_MODE_PM1 = (byte) 0x01; + static final int SLEEP_MODE_PM2 = (byte) 0x02; + static final int SLEEP_MODE_PM3 = (byte) 0x03; + + /* clock controller */ + static final byte CLKCON = (byte) 0xC6; + static final byte CLKCON_OSC32K = (byte) 0x80; + static final byte CLKCON_OSC = (byte) 0x40; + static final byte CLKCON_TICKSPD = (byte) 0x38; + static final byte CLKCON_CLKSPD = (byte) 0x07; + + static final byte[] flash_page_proto = { + + MOV_direct_data, P1DIR, (byte) 0x02, + MOV_direct_data, P1, (byte) 0xFF, + + MOV_direct_data, FADDRH, 0, /* FLASH_ADDR_HIGH */ + + MOV_direct_data, FADDRL, 0, /* FLASH_ADDR_LOW */ + + MOV_DPTR_data16, 0, 0, /* RAM_ADDR_HIGH, RAM_ADDR_LOW */ + + MOV_R7_data, 0, /* FLASH_WORDS_HIGH */ + + MOV_R6_data, 0, /* FLASH_WORDS_LOW */ + + + MOV_direct_data, FWT, 0x20, /* FLASH_TIMING */ + + MOV_direct_data, FCTL, FCTL_ERASE, +/* eraseWaitLoop: */ + MOV_A_direct, FCTL, + JB, ACC|FCTL_BUSY_BIT, (byte) 0xfb, + + MOV_direct_data, P1, (byte) 0xfd, + + MOV_direct_data, FCTL, FCTL_WRITE, +/* writeLoop: */ + MOV_R5_data, 2, +/* writeWordLoop: */ + MOVX_A_atDPTR, + INC_DPTR, + MOV_direct_A, FWDATA, + DJNZ_R5_rel, (byte) 0xfa, /* writeWordLoop */ +/* writeWaitLoop: */ + MOV_A_direct, FCTL, + JB, ACC|FCTL_SWBSY_BIT, (byte) 0xfb, /* writeWaitLoop */ + DJNZ_R6_rel, (byte) 0xf1, /* writeLoop */ + DJNZ_R7_rel, (byte) 0xef, /* writeLoop */ + + MOV_direct_data, P1DIR, (byte) 0x00, + MOV_direct_data, P1, (byte) 0xFF, + TRAP, + }; + + public byte[] make_flash_page(int flash_addr, int ram_addr, int byte_count) { + int flash_word_addr = flash_addr >> 1; + int flash_word_count = ((byte_count + 1) >> 1); + + byte[] flash_page = new byte[flash_page_proto.length]; + for (int i = 0; i < flash_page.length; i++) + flash_page[i] = flash_page_proto[i]; + + flash_page[FLASH_ADDR_HIGH] = (byte) (flash_word_addr >> 8); + flash_page[FLASH_ADDR_LOW] = (byte) (flash_word_addr); + flash_page[RAM_ADDR_HIGH] = (byte) (ram_addr >> 8); + flash_page[RAM_ADDR_LOW] = (byte) (ram_addr); + + byte flash_words_low = (byte) (flash_word_count); + byte flash_words_high = (byte) (flash_word_count >> 8); + /* the flashing code has a minor 'bug' */ + if (flash_words_low != 0) + flash_words_high++; + + flash_page[FLASH_WORDS_HIGH] = (byte) flash_words_high; + flash_page[FLASH_WORDS_LOW] = (byte) flash_words_low; + return flash_page; + } + + static byte[] set_clkcon_fast = { + MOV_direct_data, CLKCON, 0x00 + }; + + static byte[] get_sleep = { + MOV_A_direct, SLEEP + }; + + public void clock_init() throws IOException, InterruptedException { + if (debug != null) { + debug.debug_instr(set_clkcon_fast); + + byte status; + for (int times = 0; times < 20; times++) { + Thread.sleep(1); + status = debug.debug_instr(get_sleep); + if ((status & SLEEP_XOSC_STB) != 0) + return; + } + throw new IOException("Failed to initialize target clock"); + } + } + + void action(String s, int percent) { + if (listener != null && !aborted) + listener.position(s, percent); + } + + void action(int part, int total) { + int percent = 100 * part / total; + action(String.format("%d/%d (%d%%)", + part, total, percent), + percent); + } + + void altos_run(int pc) throws IOException, InterruptedException { + debug.set_pc(pc); + int set_pc = debug.get_pc(); + if (pc != set_pc) + throw new IOException("Failed to set target program counter"); + debug.resume(); + + for (int times = 0; times < 20; times++) { + byte status = debug.read_status(); + if ((status & AltosDebug.STATUS_CPU_HALTED) != 0) + return; + } + + throw new IOException("Failed to execute program on target"); + } + + public void flash() { + try { + if (!check_rom_config()) + throw new IOException("Invalid rom config settings"); + if (image.address + image.data.length > 0x8000) + throw new IOException(String.format("Flash image too long %d", + image.address + + image.data.length)); + if ((image.address & 0x3ff) != 0) + throw new IOException(String.format("Flash image must start on page boundary (is 0x%x)", + image.address)); + int ram_address = 0xf000; + int flash_prog = 0xf400; + + /* + * Store desired config values into image + */ + rom_config.write(image); + /* + * Bring up the clock + */ + clock_init(); + + int remain = image.data.length; + int flash_addr = image.address; + int image_start = 0; + + action("start", 0); + action(0, image.data.length); + while (remain > 0 && !aborted) { + int this_time = remain; + if (this_time > 0x400) + this_time = 0x400; + + if (debug != null) { + /* write the data */ + debug.write_memory(ram_address, image.data, + image_start, this_time); + + /* write the flash program */ + byte[] flash_page = make_flash_page(flash_addr, + ram_address, + this_time); + debug.write_memory(flash_prog, flash_page); + + altos_run(flash_prog); + byte[] check = debug.read_memory(flash_addr, this_time); + for (int i = 0; i < this_time; i++) + if (check[i] != image.data[image_start + i]) + throw new IOException(String.format("Flash write failed at 0x%x (%02x != %02x)", + image.address + image_start + i, + check[i], image.data[image_start + i])); + } else { + Thread.sleep(100); + } + + remain -= this_time; + flash_addr += this_time; + image_start += this_time; + + action(image.data.length - remain, image.data.length); + } + if (!aborted) { + action("done", 100); + if (debug != null) { + debug.set_pc(image.address); + debug.resume(); + } + } + if (debug != null) + debug.close(); + } catch (IOException ie) { + action(ie.getMessage(), -1); + abort(); + } catch (InterruptedException ie) { + abort(); + } + } + + public void close() { + if (debug != null) + debug.close(); + } + + synchronized public void abort() { + aborted = true; + close(); + } + + public boolean check_rom_config() { + if (debug == null) + return true; + if (rom_config == null) + rom_config = debug.romconfig(); + return rom_config != null && rom_config.valid(); + } + + public void set_romconfig (AltosRomconfig romconfig) { + rom_config = romconfig; + } + + public AltosRomconfig romconfig() { + if (!check_rom_config()) + return null; + return rom_config; + } + + public AltosFlash(File file, AltosLink link, AltosFlashListener listener) + throws IOException, FileNotFoundException, InterruptedException { + this.file = file; + this.link = link; + this.listener = listener; + if (link != null) + debug = new AltosDebug(link); + input = new FileInputStream(file); + image = new AltosHexfile(input); + if (debug != null && !debug.check_connection()) { + debug.close(); + throw new IOException("Debug port not connected"); + } + } +}
\ No newline at end of file diff --git a/altoslib/AltosFlashListener.java b/altoslib/AltosFlashListener.java new file mode 100644 index 00000000..ab50b74a --- /dev/null +++ b/altoslib/AltosFlashListener.java @@ -0,0 +1,22 @@ +/* + * Copyright © 2013 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +package org.altusmetrum.altoslib_1; + +public interface AltosFlashListener { + public void position(String label, int percent); +} diff --git a/altoslib/AltosHexfile.java b/altoslib/AltosHexfile.java new file mode 100644 index 00000000..68f42f14 --- /dev/null +++ b/altoslib/AltosHexfile.java @@ -0,0 +1,298 @@ +/* + * Copyright © 2010 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +package org.altusmetrum.altoslib_1; + +import java.io.*; +import java.util.LinkedList; +import java.util.Arrays; + +class HexFileInputStream extends PushbackInputStream { + public int line; + + public HexFileInputStream(FileInputStream o) { + super(new BufferedInputStream(o)); + line = 1; + } + + public int read() throws IOException { + int c = super.read(); + if (c == '\n') + line++; + return c; + } + + public void unread(int c) throws IOException { + if (c == '\n') + line--; + if (c != -1) + super.unread(c); + } +} + +class HexRecord implements Comparable<Object> { + public int address; + public int type; + public byte checksum; + public byte[] data; + + static final int NORMAL = 0; + static final int EOF = 1; + static final int EXTENDED_ADDRESS = 2; + + enum read_state { + marker, + length, + address, + type, + data, + checksum, + newline, + white, + done, + } + + boolean ishex(int c) { + if ('0' <= c && c <= '9') + return true; + if ('a' <= c && c <= 'f') + return true; + if ('A' <= c && c <= 'F') + return true; + return false; + } + + boolean isspace(int c) { + switch (c) { + case ' ': + case '\t': + return true; + } + return false; + } + + int fromhex(int c) { + if ('0' <= c && c <= '9') + return c - '0'; + if ('a' <= c && c <= 'f') + return c - 'a' + 10; + if ('A' <= c && c <= 'F') + return c - 'A' + 10; + return -1; + } + + public byte checksum() { + byte got = 0; + + got += data.length; + got += (address >> 8) & 0xff; + got += (address ) & 0xff; + got += type; + for (int i = 0; i < data.length; i++) + got += data[i]; + return (byte) (-got); + } + + public int compareTo(Object other) { + HexRecord o = (HexRecord) other; + return address - o.address; + } + + public String toString() { + return String.format("%04x: %02x (%d)", address, type, data.length); + } + + public HexRecord(HexFileInputStream input) throws IOException { + read_state state = read_state.marker; + int nhexbytes = 0; + int hex = 0; + int ndata = 0; + byte got_checksum; + + while (state != read_state.done) { + int c = input.read(); + if (c < 0 && state != read_state.white) + throw new IOException(String.format("%d: Unexpected EOF", input.line)); + if (c == ' ') + continue; + switch (state) { + case marker: + if (c != ':') + throw new IOException("Missing ':'"); + state = read_state.length; + nhexbytes = 2; + hex = 0; + break; + case length: + case address: + case type: + case data: + case checksum: + if(!ishex(c)) + throw new IOException(String.format("Non-hex char '%c'", c)); + hex = hex << 4 | fromhex(c); + --nhexbytes; + if (nhexbytes != 0) + break; + + switch (state) { + case length: + data = new byte[hex]; + state = read_state.address; + nhexbytes = 4; + break; + case address: + address = hex; + state = read_state.type; + nhexbytes = 2; + break; + case type: + type = hex; + if (data.length > 0) + state = read_state.data; + else + state = read_state.checksum; + nhexbytes = 2; + ndata = 0; + break; + case data: + data[ndata] = (byte) hex; + ndata++; + nhexbytes = 2; + if (ndata == data.length) + state = read_state.checksum; + break; + case checksum: + checksum = (byte) hex; + state = read_state.newline; + break; + default: + break; + } + hex = 0; + break; + case newline: + if (c != '\n' && c != '\r') + throw new IOException("Missing newline"); + state = read_state.white; + break; + case white: + if (!isspace(c)) { + input.unread(c); + state = read_state.done; + } + break; + case done: + break; + } + } + got_checksum = checksum(); + if (got_checksum != checksum) + throw new IOException(String.format("Invalid checksum (read 0x%02x computed 0x%02x)\n", + checksum, got_checksum)); + } +} + +public class AltosHexfile { + public int address; + public byte[] data; + + public byte get_byte(int a) { + return data[a - address]; + } + + public AltosHexfile(FileInputStream file) throws IOException { + HexFileInputStream input = new HexFileInputStream(file); + LinkedList<HexRecord> record_list = new LinkedList<HexRecord>(); + boolean done = false; + + while (!done) { + HexRecord record = new HexRecord(input); + + if (record.type == HexRecord.EOF) + done = true; + else + record_list.add(record); + } + + long extended_addr = 0; + long base = 0xffffffff; + long bound = 0x00000000; + for (HexRecord record : record_list) { + switch (record.type) { + case 0: + long addr = extended_addr + record.address; + long r_bound = addr + record.data.length; + if (addr < base) + base = addr; + if (r_bound > bound) + bound = r_bound; + break; + case 1: + break; + case 2: + if (record.data.length != 2) + throw new IOException("invalid extended segment address record"); + extended_addr = ((record.data[0] << 8) + (record.data[1])) << 4; + break; + case 4: + if (record.data.length != 2) + throw new IOException("invalid extended segment address record"); + extended_addr = ((record.data[0] << 8) + (record.data[1])) << 16; + break; + default: + throw new IOException ("invalid hex record type"); + } + } + + if (base >= bound) + throw new IOException("invalid hex file"); + + if (bound - base > 4 * 1024 * 1024) + throw new IOException("hex file too large"); + + data = new byte[(int) (bound - base)]; + address = (int) base; + Arrays.fill(data, (byte) 0xff); + + /* Paint the records into the new array */ + for (HexRecord record : record_list) { + switch (record.type) { + case 0: + long addr = extended_addr + record.address; + long r_bound = addr + record.data.length; + for (int j = 0; j < record.data.length; j++) + data[(int) (addr - base) + j] = record.data[j]; + break; + case 1: + break; + case 2: + if (record.data.length != 2) + throw new IOException("invalid extended segment address record"); + extended_addr = ((record.data[0] << 8) + (record.data[1])) << 4; + break; + case 4: + if (record.data.length != 2) + throw new IOException("invalid extended segment address record"); + extended_addr = ((record.data[0] << 8) + (record.data[1])) << 16; + break; + default: + throw new IOException ("invalid hex record type"); + } + } + } +}
\ No newline at end of file diff --git a/altoslib/AltosRomconfig.java b/altoslib/AltosRomconfig.java new file mode 100644 index 00000000..0800a2c4 --- /dev/null +++ b/altoslib/AltosRomconfig.java @@ -0,0 +1,148 @@ +/* + * Copyright © 2010 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +package org.altusmetrum.altoslib_1; + +import java.io.*; + +public class AltosRomconfig { + public boolean valid; + public int version; + public int check; + public int serial_number; + public int radio_calibration; + + static int get_int(byte[] bytes, int start, int len) { + int v = 0; + int o = 0; + while (len > 0) { + v = v | ((((int) bytes[start]) & 0xff) << o); + start++; + len--; + o += 8; + } + return v; + } + + static void put_int(int value, byte[] bytes, int start, int len) { + while (len > 0) { + bytes[start] = (byte) (value & 0xff); + start++; + len--; + value >>= 8; + } + } + + static void put_string(String value, byte[] bytes, int start) { + for (int i = 0; i < value.length(); i++) + bytes[start + i] = (byte) value.charAt(i); + } + + static final int AO_USB_DESC_STRING = 3; + + static void put_usb_serial(int value, byte[] bytes, int start) { + int offset = start + 0xa; + int string_num = 0; + + while (offset < bytes.length && bytes[offset] != 0) { + if (bytes[offset + 1] == AO_USB_DESC_STRING) { + ++string_num; + if (string_num == 4) + break; + } + offset += ((int) bytes[offset]) & 0xff; + } + if (offset >= bytes.length || bytes[offset] == 0) + return; + int len = ((((int) bytes[offset]) & 0xff) - 2) / 2; + String fmt = String.format("%%0%dd", len); + + String s = String.format(fmt, value); + if (s.length() != len) { + System.out.printf("weird usb length issue %s isn't %d\n", + s, len); + return; + } + for (int i = 0; i < len; i++) { + bytes[offset + 2 + i*2] = (byte) s.charAt(i); + bytes[offset + 2 + i*2+1] = 0; + } + } + + public AltosRomconfig(byte[] bytes, int offset) { + version = get_int(bytes, offset + 0, 2); + check = get_int(bytes, offset + 2, 2); + if (check == (~version & 0xffff)) { + switch (version) { + case 2: + case 1: + serial_number = get_int(bytes, offset + 4, 2); + radio_calibration = get_int(bytes, offset + 6, 4); + valid = true; + break; + } + } + } + + public AltosRomconfig(AltosHexfile hexfile) { + this(hexfile.data, 0xa0 - hexfile.address); + } + + public void write(byte[] bytes, int offset) throws IOException { + if (!valid) + throw new IOException("rom configuration invalid"); + + if (offset < 0 || bytes.length < offset + 10) + throw new IOException("image cannot contain rom config"); + + AltosRomconfig existing = new AltosRomconfig(bytes, offset); + if (!existing.valid) + throw new IOException("image does not contain existing rom config"); + + switch (existing.version) { + case 2: + put_usb_serial(serial_number, bytes, offset); + case 1: + put_int(serial_number, bytes, offset + 4, 2); + put_int(radio_calibration, bytes, offset + 6, 4); + break; + } + } + + public void write (AltosHexfile hexfile) throws IOException { + write(hexfile.data, 0xa0 - hexfile.address); + AltosRomconfig check = new AltosRomconfig(hexfile); + if (!check.valid()) + throw new IOException("writing new rom config failed\n"); + } + + public AltosRomconfig(int in_serial_number, int in_radio_calibration) { + valid = true; + version = 1; + check = (~version & 0xffff); + serial_number = in_serial_number; + radio_calibration = in_radio_calibration; + } + + public boolean valid() { + return valid && serial_number != 0; + } + + public AltosRomconfig() { + valid = false; + } +} diff --git a/altoslib/Makefile.am b/altoslib/Makefile.am index 30a9d954..18b028d6 100644 --- a/altoslib/Makefile.am +++ b/altoslib/Makefile.am @@ -16,6 +16,7 @@ altoslib_JAVA = \ AltosConfigValues.java \ AltosConvert.java \ AltosCRCException.java \ + AltosDebug.java \ AltosEepromChunk.java \ AltosEepromIterable.java \ AltosEepromLog.java \ @@ -24,12 +25,15 @@ altoslib_JAVA = \ AltosEepromRecord.java \ AltosEepromTeleScience.java \ AltosFile.java \ + AltosFlash.java \ + AltosFlashListener.java \ AltosFlightReader.java \ AltosFrequency.java \ AltosGPS.java \ AltosGPSQuery.java \ AltosGPSSat.java \ AltosGreatCircle.java \ + AltosHexfile.java \ AltosIdleMonitor.java \ AltosIdleMonitorListener.java \ AltosIgnite.java \ @@ -53,6 +57,7 @@ altoslib_JAVA = \ AltosRecordTM.java \ AltosRecordMM.java \ AltosReplayReader.java \ + AltosRomconfig.java \ AltosSensorMM.java \ AltosSensorTM.java \ AltosState.java \ |