Trammell Hudson avatar Trammell Hudson committed d11b0ac Draft

State machine parser works!

Comments (0)

Files changed (2)

 
 avrfid2.elf: avrfid2.c
 	$(CC) $(CFLAGS) -o $@ $<
+	$(SIZE) $@
 
 CFLAGS := \
 	-mmcu=attiny85 \
 #include <avr/pgmspace.h>
 #include <avr/sfr_defs.h>
 
-static void _0(void);
-static void _1(void);
+static void manchester_0(void);
+static void manchester_1(void);
 static void hid_header(void);
+static void hid_reset(void);
+//int main(void);
 
 #define HID_MFG_CODE        0x01002  // Do not modify
 #define HID_SITE_CODE       42
 #define HID_UNIQUE_ID       23946     // May be written on the back of the card
 
-static const void * hid_bits[] PROGMEM = {
+#define HID_HEADER "2"
+#define HID_RESET "3"
+
+static const char hid_bits[]
+PROGMEM __attribute__((__used__)) = {
+	HID_HEADER
+	"0000"
+	"0001"
+	"0000"
+	"0000"
+	"0010" // HID Manufacturer code
+	"00101010" // Site code 42
+	"01011101"
+	"10001010" // ID 23946
+	"0" // parity
+	HID_RESET
+};
+
+typedef void (*state_function)(void);
+
+static const state_function state_handlers[]
+PROGMEM __attribute__((__used__)) = {
+	manchester_0,
+	manchester_1,
+	hid_header,
+	hid_reset,
+};
+
+
+#if 0
 // HID manufacturer code (20 bits) == 0x01002
 _0, _0, _0, _0,
 _0, _0, _0, _1, _0, _0, _0, _0,
 // And return to the header when we're done
 hid_header
 };
+#endif
 
 
 /** Use r16 and r17 to track the state of the pins.
 volatile register uint8_t bit_num __asm__("r15"); 
 
 
-/** Load from a flash address pointed to by z_hi:z_lo and increment Z.
- *
- * Z = r31:r30.
- * 3 clocks.
- */
-static inline uint8_t
-lpm_z_inc(void)
-{
-	uint8_t r;
-	__asm__ __volatile__(
-		"lpm %0, Z+"
-		: "=r"(r)
-	);
-	return r;
-}
 
-
-/** Copy a value + offset into z_lo.
- * 1 clock if offset is a constant 0, 2 clocks otherwise.
- */
-static inline void
-z_lo(
-	uint8_t x,
-	uint8_t offset
-)
-{
-	__asm__ __volatile__("mov r30, %0" : : "r"(x));
-
-	// Only add the offset if it is not known at compile time
-	// or if it is known at compile time and non-zero.
-	if (!__builtin_constant_p(offset) || offset != 0)
-		__asm__ __volatile__("add r30, %0" : : "r"(offset));
-}
-
-
-/** Copy a value into z_hi.
- * 1 clock.
- */
-static inline void
-z_hi(
-	uint8_t x
-)
-{
-	__asm__ __volatile__("mov r31, %0" : :  "r"(x));
-}
-
-
-/** Jump to what ever has been stored into Z with z_lo() and z_hi()
+/** Jump to what ever has been stored into Z (r31:r30)
  *
  * PC <- Z
  * 2 clocks
  *
  * 2 clocks.
  */
+asm(
+	".macro toggle\n"
+	"eor r16, r17\n"
+	"out 0x17, r16\n" // _SFR_IO_ADDR(DDRB)
+	".endm\n"
+);
+
+
 static void
 __attribute__((__always_inline__))
 toggle_raw(void)
 {
-	__asm__ __volatile__(
-		"eor r16, r17\n"
-		"out %0, r16\n"
-		: : "I"(_SFR_IO_ADDR(DDRB))
-	);
+	__asm__ __volatile__("toggle");
 }
 
 
  * Interleaved with the FSK are the operations to load the next
  * function pointer.  Once the function "returns", the Z register
  * will contain the address of the next function in the state machine.
+ *
+ * This was too difficult to write in C and have gcc output the correct
+ * stream of instructions.  Instead it is in inline assembly.
+ * The rough translation into C:
+ *
+ *	toggle 5
+ *			z = &hid_bits[bit_num];
+ *	toggle 10
+ *			next_state = lpm(z);
+ *	toggle 15
+ *			next_state = (next_state - '0') * 2
+ *	toggle 20
+ *			z = &state_handlers[next_state];
+ *	toggle 25
+ *			next_func_lo = lpm(z++);
+ *	toggle 30
+ *			next_func_hi = lpm(z++);
+ *	toggle 35
+ *			z = next_func_hi << 8 | next_func_lo;
+ *	toggle 40
+ *			bit_num++;
+ *	toggle 45
+ *			delay
+ *	toggle 50
+ *			No delay (leave these free for caller)
  */
 static void
 __attribute__((__always_inline__))
 baseband_1_load(void)
 {
-	toggle(0); //  5
-				uint16_t x = (uint16_t) hid_bits;
-				uint8_t ptr_lo = x >> 0;
-				uint8_t ptr_hi = x >> 8;
-				delay(1);
-
-	toggle(0); // 10
-				z_lo(ptr_lo, bit_num);
-				z_hi(ptr_hi);
-
-	toggle(0); // 15
-				uint8_t next_lo = lpm_z_inc();
-
-	toggle(0); // 20
-				uint8_t next_hi = lpm_z_inc();
-
-	toggle(0); // 25
-				delay(1);
-				z_lo(next_lo, 0);
-				z_hi(next_hi);
-
-	toggle(0); // 30
-				delay(1);
-				// word indexed, 2 clocks
-				__asm__ __volatile__(
+	__asm__ __volatile__(
+		"toggle /* 5 */\n"
+					"ldi r30, lo8(hid_bits)\n"
+					"ldi r31, hi8(hid_bits)\n"
+					"add r30, %0\n"
+		"toggle /* 10 */\n"
+					"lpm r24, Z\n"
+		"toggle /* 15 */\n"
+					"ldi r30, lo8(state_handlers)\n"
+					"ldi r31, hi8(state_handlers)\n"
+					"nop\n"
+		"toggle /* 20 */\n"
+					"subi r24, '0'\n"
+					"lsl r24\n"
+					"add r30, r24\n"
+		"toggle /* 25 */\n"
+					"lpm r24, Z+\n"
+		"toggle /* 30 */\n"
+					"lpm r31, Z\n"
+		"toggle /* 35 */\n"
+					"mov r30, r24\n"
+					"rjmp .+0\n"
+		"toggle /* 40 */\n"
 					"inc %0\n"
-					"inc %0\n"
-					: "=r"(bit_num)
-				);
-
-	toggle(ONE_FREQ); // 35
-	toggle(ONE_FREQ); // 40
-	toggle(ONE_FREQ); // 45
-	toggle(0); // 50
+					"rjmp .+0\n"
+		"toggle /* 45 */\n"
+					"nop\n"
+					"rjmp .+0\n"
+		"toggle /* 50 */\n"
+					"/* Leave slot free */\n"
+		: "=r" (bit_num) // 0
+	);
 }
 
 
  *
  * The last baseband 1 will load the first state machine function
  * pointer and jump into the statemachine.
- *
- * The last state in the state machine will
- * bring us back here for a continuous loop.
  */
 static void
 hid_header(void)
 
 	baseband_0(1);
 	baseband_0(1);
-	baseband_0(0);
-				// force this operation here
-				__asm__ __volatile__ ("eor r15, r15");
-				delay(1);
-
+	baseband_0(1);
 	baseband_1();
 	baseband_1();
 	baseband_1_load();
 }
 
 
+
 /** Output a manchester 0.
  *
  * Output a baseband 0, followed by a baseband 1.
  * we jump to the next state.
  */
 static void
-_0(void)
+manchester_0(void)
 {
 	baseband_0(1);
 	baseband_1_load();
  * we jump to the next state.
  */
 static void
-_1(void)
+manchester_1(void)
 {
 	baseband_1_load();
 				delay(3); // 3 delays slots remain
 }
 
 
-/** Entry point at 0x0
+/** Restart the state machine at state 0.
+ *
+ * This must be the last state in the machine and is the first one
+ * called from main to kick things off.
+ */
+static void
+hid_reset(void)
+{
+	// We will start in state 0, so the next to read is 1
+	// gcc keeps optimizign writes to r15 out for some reason
+	__asm__ __volatile__(
+		"eor %0, %0\n"
+		"inc %0\n"
+		: "=r"(bit_num)
+	);
+
+	__asm__ __volatile__("rjmp hid_header");
+}
+
+
+/** Entry point at 0x0.
  *
  * Since we linking with -nostdlib, main needs to be at 0x0.
  * The easiest way to force that with the default linker script
 	r16 = 0;
 	r17 = _BV(PINB3) | _BV(PINB4);
 
-	hid_header();
+	hid_reset();
 
 	/* Never returns */
 }
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.