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 \  | 
