diff --git a/firmware/asdf/src/Arch/asdf_arch_atmega2560.c b/firmware/asdf/src/Arch/asdf_arch_atmega2560.c
new file mode 100644
index 0000000..6ddd9d5
--- /dev/null
+++ b/firmware/asdf/src/Arch/asdf_arch_atmega2560.c
@@ -0,0 +1,779 @@
+// -*- mode: C; tab-width: 2 ; indent-tabs-mode: nil -*-
+//
+// Unfified Keyboard Project
+// ASDF keyboard firmware
+//
+// asdf_arch.c
+//
+// This file contains all the architecture dependent code, including register
+// setup, I/O, timers, etc.
+//
+// Copyright 2019 David Fenyes
+//
+// 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, either version 3 of the License, or (at your option) any later
+// version.
+//
+// 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, see .
+
+
+// Wiring Information:
+// Chip: {Microcontroller type and version}
+//
+// Example:
+// PIN NAME FUNCTION
+// 14-19,9,10 PORTB COLUMN inputs (1 bit per column)
+// 23-25 PORTC0-2 ROW outputs (row number)
+// 27 PORTC4
+
+
+#include
+#include
+#include
+#include
+#include "asdf_config.h"
+#include "asdf_arch.h"
+#include "asdf_keymap_defs.h"
+
+// Tick is true every 1 ms.
+static volatile uint8_t tick = 0;
+
+// data polarity may be changed with a DIP switch, so we use a static instead of a constant
+static uint8_t data_polarity = ASDF_DEFAULT_DATA_POLARITY;
+
+// strobe polarity may be changed with a DIP switch, so we use a static instead of a constant
+static uint8_t strobe_polarity = ASDF_DEFAULT_STROBE_POLARITY;
+
+
+// PROCEDURE: ISR for Timer 0 overflow
+// INPUTS: none
+// OUTPUTS:none
+//
+// DESCRIPTION: Occurs every 1 ms. Set tick flag, kick watchdog.
+//
+// SIDE EFFECTS:
+//
+// NOTES:
+//
+// SCOPE:
+//
+// COMPLEXITY:
+//
+ISR(TIMER0_COMPA_vect)
+{
+ tick = 1;
+}
+
+// PROCEDURE: set_bit
+// INPUTS: port: pointer to a (uint8) port
+// bit: bit position to be set
+// OUTPUTS: none
+//
+// DESCRIPTION: Give a port address and bit position, set the bit position.
+//
+// SIDE EFFECTS: See DESCRIPTION
+//
+// NOTES: Declared inline. Will only be inlined for functions in this module, so
+// also declared static.
+//
+// SCOPE: private
+//
+// COMPLEXITY: 1
+//
+static inline void set_bit(volatile uint8_t *port, uint8_t bit)
+{
+ *port |= (1 << bit);
+}
+
+// PROCEDURE: clear_bit
+// INPUTS: port: pointer to a (uint8) port
+// bit: bit position to be cleared
+// OUTPUTS: none
+//
+// DESCRIPTION: Give a port address and bit position, clear the bit position.
+//
+// SIDE EFFECTS: See DESCRIPTION
+//
+// NOTES: Declared inline. Will only be inlined for functions in this module, so
+// also declared static.
+//
+// SCOPE: private
+//
+// COMPLEXITY: 1
+//
+static inline void clear_bit(volatile uint8_t *port, uint8_t bit)
+{
+ *port &= ~(1 << bit);
+}
+
+// PROCEDURE: arch_timer0_config
+//
+// INPUTS: bits: a 4 byte field containing the configuration values for the
+// 8-bit timer0 A and B control registers, and the interrupt mask register.
+//
+// OUTPUTS: none
+//
+// DESCRIPTION: Takes a 4 byte value with settings for all the control
+// registers for the 8-bit counter/timer (timer 0), and writes them all
+// to the respective registers.
+//
+// SIDE EFFECTS: see above
+//
+// NOTES: Setting all the bits together, and writing all the registers from a
+// single word permits more clear initialization of control fields that are
+// spread across more than one word.
+//
+// COMPLEXITY: 1
+//
+// SCOPE: private
+//
+static void arch_timer0_config(uint32_t bits)
+{
+ TCCR0B = 0; // first turn off timer.
+ TCCR0A = (bits >> TMR0A_POS) & 0xff;
+ TIMSK0 = (bits >> TMR0IMSK_POS) & 0xff;
+ TCCR0B = (bits >> TMR0B_POS) & 0xff; // Set the mode (and turn on timer) last
+}
+
+// PROCEDURE: arch_tick_timer_init
+// INPUTS: none
+// OUTPUTS: none
+//
+// DESCRIPTION: Sets up 1ms tick timer.
+//
+// SIDE EFFECTS:
+//
+// NOTES: Set up Timer 0 in CTC mode for 1 ms overflow.
+//
+// SCOPE: private
+//
+// COMPLEXITY: 1
+//
+static void asdf_arch_tick_timer_init(void)
+{
+ tick = 0;
+
+ // set compare register first, so timer can operate correctly as soon as it is
+ // enabled.
+ OCR0A = TICK_COUNT;
+
+ // operate in CTC mode to overflow at exactly 1 ms
+ // prescaler = 64 and output compare value is 250
+ arch_timer0_config(TIMER0_WFM_CTC | TIMER0_DIV64 | TIMER0_INT_ON_COMA);
+}
+
+// PROCEDURE: asdf_arch_tick
+// INPUTS: none
+// OUTPUTS: returns a 1 if the 1ms timer timed out, 0 otherwise
+//
+// DESCRIPTION: See Outputs.
+//
+// SIDE EFFECTS: Zeroes out the 1 ms timer flag.
+//
+// NOTES:
+//
+// SCOPE: public
+//
+// COMPLEXITY: 1
+//
+uint8_t asdf_arch_tick(void)
+{
+ uint8_t retval = tick;
+ tick = 0;
+ return retval;
+}
+
+// PROCEDURE: asdf_arch_init_timers
+// INPUTS: none
+// OUTPUTS: none
+//
+// DESCRIPTION: Sets up timer for 1 ms intervals
+//
+// SIDE EFFECTS: See DESCRIPTION
+//
+// SCOPE: private
+//
+// COMPLEXITY: 1
+//
+static void asdf_arch_init_clock(void)
+{
+ CLKPR = (CLKPCE | SYSCLK_DIV1);
+}
+
+// PROCEDURE: asdf_arch_init_outputs
+// INPUTS: none
+// OUTPUTS: none
+//
+// DESCRIPTION: Initialize all LED ports as outputs. Values are not set here.
+// They are set by the keymap code
+//
+// SIDE EFFECTS: See DESCRIPTION
+//
+// SCOPE: private
+//
+// COMPLEXITY: 1
+//
+static void asdf_arch_init_leds(void)
+{
+ set_bit(&ASDF_LED1_DDR, ASDF_LED1_BIT);
+ set_bit(&ASDF_LED2_DDR, ASDF_LED2_BIT);
+ set_bit(&ASDF_LED3_DDR, ASDF_LED3_BIT);
+}
+
+
+// PROCEDURE: asdf_arch_led1_set
+// INPUTS: (uint8_t) value
+// OUTPUTS: none
+//
+// DESCRIPTION: If value is true, turn on LED1. If value is false, turn off LED1
+//
+// SIDE EFFECTS: See above.
+//
+// NOTES: The LED1 port drives the LED directly by pulling the cathode low, so
+// clearing the bit turns the LED on.
+//
+// SCOPE: public
+//
+// COMPLEXITY: 2
+//
+void asdf_arch_led1_set(uint8_t value)
+{
+ if (value) {
+ clear_bit(&ASDF_LED1_PORT, ASDF_LED1_BIT);
+ }
+ else {
+ set_bit(&ASDF_LED1_PORT, ASDF_LED1_BIT);
+ }
+}
+
+// PROCEDURE: asdf_arch_led2_set
+// INPUTS: (uint8_t) value
+// OUTPUTS: none
+//
+// DESCRIPTION: If value is true, turn on LED2. If value is false, turn off LED2
+//
+// SIDE EFFECTS: See above.
+//
+// NOTES: The LED2 output drives the LED via an inverter buffer, so a high
+// output pulls the LED cathode low, lighting the LED.
+//
+// SCOPE: public
+//
+// COMPLEXITY: 2
+//
+void asdf_arch_led2_set(uint8_t value)
+{
+ if (value) {
+ set_bit(&ASDF_LED2_PORT, ASDF_LED2_BIT);
+ }
+ else {
+ clear_bit(&ASDF_LED2_PORT, ASDF_LED2_BIT);
+ }
+}
+
+// PROCEDURE: asdf_arch_led3_set
+// INPUTS: (uint8_t) value
+// OUTPUTS: none
+//
+// DESCRIPTION: If value is true, turn on LED3. If value is false, turn off LED3
+//
+// SIDE EFFECTS: See above.
+//
+// NOTES: The LED3 output drives the LED via an inverter buffer, so a high
+// output pulls the LED cathode low, lighting the LED.
+//
+// SCOPE: public
+//
+// COMPLEXITY: 2
+//
+void asdf_arch_led3_set(uint8_t value)
+{
+ if (value) {
+ set_bit(&ASDF_LED3_PORT, ASDF_LED3_BIT);
+ }
+ else {
+ clear_bit(&ASDF_LED3_PORT, ASDF_LED3_BIT);
+ }
+}
+
+// PROCEDURE: asdf_arch_null_output
+// INPUTS: (uint8_t) value - ignored
+// OUTPUTS: none
+//
+// DESCRIPTION: Does nothing.
+//
+// SIDE EFFECTS: See above.
+//
+// NOTES:
+//
+// SCOPE: public
+//
+// COMPLEXITY: 2
+//
+void asdf_arch_null_output(uint8_t value)
+{
+ (void) value;
+}
+
+// PROCEDURE: asdf_arch_out1_set
+// INPUTS: (uint8_t) value
+// OUTPUTS: none
+//
+// DESCRIPTION: Sets the OUT1 bit if value is true, and clear OUT1 if value is false.
+//
+// SIDE EFFECTS: See above.
+//
+// NOTES:
+//
+// SCOPE: public
+//
+// COMPLEXITY: 2
+//
+void asdf_arch_out1_set(uint8_t value)
+{
+ if (value) {
+ set_bit(&ASDF_OUT1_PORT, ASDF_OUT1_BIT);
+ }
+ else {
+ clear_bit(&ASDF_OUT1_PORT, ASDF_OUT1_BIT);
+ }
+ set_bit(&ASDF_OUT1_DDR, ASDF_OUT1_BIT);
+}
+
+// PROCEDURE: asdf_arch_out1_open_hi_set
+// INPUTS: (uint8_t) value
+// OUTPUTS: none
+//
+// DESCRIPTION: Sets the OUT1 bit to hi-z if value is true, and low if value is false.
+//
+// SIDE EFFECTS: See above.
+//
+// NOTES:
+//
+// SCOPE: public
+//
+// COMPLEXITY: 2
+//
+void asdf_arch_out1_open_hi_set(uint8_t value)
+{
+ if (value) {
+ clear_bit(&ASDF_OUT1_DDR, ASDF_OUT1_BIT);
+ set_bit(&ASDF_OUT1_PORT, ASDF_OUT1_BIT);
+ }
+ else {
+ clear_bit(&ASDF_OUT1_PORT, ASDF_OUT1_BIT);
+ set_bit(&ASDF_OUT1_DDR, ASDF_OUT1_BIT);
+ }
+}
+
+// PROCEDURE: asdf_arch_out1_open_lo_set
+// INPUTS: (uint8_t) value
+// OUTPUTS: none
+//
+// DESCRIPTION: Sets the OUT1 bit to high if value is true, and hi-z if value is false.
+//
+// SIDE EFFECTS: See above.
+//
+// NOTES:
+//
+// SCOPE: public
+//
+// COMPLEXITY: 2
+//
+void asdf_arch_out1_open_lo_set(uint8_t value)
+{
+ if (value) {
+ set_bit(&ASDF_OUT1_PORT, ASDF_OUT1_BIT);
+ set_bit(&ASDF_OUT1_DDR, ASDF_OUT1_BIT);
+ }
+ else {
+ clear_bit(&ASDF_OUT1_DDR, ASDF_OUT1_BIT);
+ clear_bit(&ASDF_OUT1_PORT, ASDF_OUT1_BIT);
+ }
+}
+
+// PROCEDURE: asdf_arch_out2_set
+// INPUTS: (uint8_t) value
+// OUTPUTS: none
+//
+// DESCRIPTION: Sets the OUT2 bit if value is true, and clear OUT2 if value is false.
+//
+// SIDE EFFECTS: See above.
+//
+// NOTES:
+//
+// SCOPE: public
+//
+// COMPLEXITY: 2
+//
+void asdf_arch_out2_set(uint8_t value)
+{
+ if (value) {
+ set_bit(&ASDF_OUT2_PORT, ASDF_OUT2_BIT);
+ }
+ else {
+ clear_bit(&ASDF_OUT2_PORT, ASDF_OUT2_BIT);
+ }
+ set_bit(&ASDF_OUT2_DDR, ASDF_OUT2_BIT);
+}
+
+// PROCEDURE: asdf_arch_out2_open_hi_set
+// INPUTS: (uint8_t) value
+// OUTPUTS: none
+//
+// DESCRIPTION: Sets the OUT2 bit to hi-z if value is true, and low if value is false.
+//
+// SIDE EFFECTS: See above.
+//
+// NOTES:
+//
+// SCOPE: public
+//
+// COMPLEXITY: 2
+//
+void asdf_arch_out2_open_hi_set(uint8_t value)
+{
+ if (value) {
+ clear_bit(&ASDF_OUT2_DDR, ASDF_OUT2_BIT);
+ set_bit(&ASDF_OUT2_PORT, ASDF_OUT2_BIT);
+ }
+ else {
+ clear_bit(&ASDF_OUT2_PORT, ASDF_OUT2_BIT);
+ set_bit(&ASDF_OUT2_DDR, ASDF_OUT2_BIT);
+ }
+}
+
+// PROCEDURE: asdf_arch_out2_open_lo_set
+// INPUTS: (uint8_t) value
+// OUTPUTS: none
+//
+// DESCRIPTION: Sets the OUT2 bit to high if value is true, and hi-z if value is false.
+//
+// SIDE EFFECTS: See above.
+//
+// NOTES:
+//
+// SCOPE: public
+//
+// COMPLEXITY: 2
+//
+void asdf_arch_out2_open_lo_set(uint8_t value)
+{
+ if (value) {
+ set_bit(&ASDF_OUT2_PORT, ASDF_OUT2_BIT);
+ set_bit(&ASDF_OUT2_DDR, ASDF_OUT2_BIT);
+ }
+ else {
+ clear_bit(&ASDF_OUT2_DDR, ASDF_OUT2_BIT);
+ clear_bit(&ASDF_OUT2_PORT, ASDF_OUT2_BIT);
+ }
+}
+
+// PROCEDURE: asdf_arch_out3_set
+// INPUTS: (uint8_t) value
+// OUTPUTS: none
+//
+// DESCRIPTION: Sets the OUT3 bit if value is true, and clear OUT3 if value is false.
+//
+// SIDE EFFECTS: See above.
+//
+// NOTES:
+//
+// SCOPE: public
+//
+// COMPLEXITY: 2
+//
+void asdf_arch_out3_set(uint8_t value)
+{
+ if (value) {
+ set_bit(&ASDF_OUT3_PORT, ASDF_OUT3_BIT);
+ }
+ else {
+ clear_bit(&ASDF_OUT3_PORT, ASDF_OUT3_BIT);
+ }
+ set_bit(&ASDF_OUT3_DDR, ASDF_OUT3_BIT);
+}
+
+// PROCEDURE: asdf_arch_out3_open_hi_set
+// INPUTS: (uint8_t) value
+// OUTPUTS: none
+//
+// DESCRIPTION: Sets the OUT3 bit to hi-z if value is true, and low if value is false.
+//
+// SIDE EFFECTS: See above.
+//
+// NOTES:
+//
+// SCOPE: public
+//
+// COMPLEXITY: 2
+//
+void asdf_arch_out3_open_hi_set(uint8_t value)
+{
+ if (value) {
+ clear_bit(&ASDF_OUT3_DDR, ASDF_OUT3_BIT);
+ set_bit(&ASDF_OUT3_PORT, ASDF_OUT3_BIT);
+ }
+ else {
+ clear_bit(&ASDF_OUT3_PORT, ASDF_OUT3_BIT);
+ set_bit(&ASDF_OUT3_DDR, ASDF_OUT3_BIT);
+ }
+}
+
+// PROCEDURE: asdf_arch_out3_open_lo_set
+// INPUTS: (uint8_t) value
+// OUTPUTS: none
+//
+// DESCRIPTION: Sets the OUT3 bit to high if value is true, and hi-z if value is false.
+//
+// SIDE EFFECTS: See above.
+//
+// NOTES:
+//
+// SCOPE: public
+//
+// COMPLEXITY: 2
+//
+void asdf_arch_out3_open_lo_set(uint8_t value)
+{
+ if (value) {
+ set_bit(&ASDF_OUT3_PORT, ASDF_OUT3_BIT);
+ set_bit(&ASDF_OUT3_DDR, ASDF_OUT3_BIT);
+ }
+ else {
+ clear_bit(&ASDF_OUT3_DDR, ASDF_OUT3_BIT);
+ clear_bit(&ASDF_OUT3_PORT, ASDF_OUT3_BIT);
+ }
+}
+
+// PROCEDURE: asdf_arch_init_strobe
+// INPUTS: none
+// OUTPUTS: none
+//
+// DESCRIPTION: Initialize strobe output
+//
+// SIDE EFFECTS: See DESCRIPTION
+//
+// SCOPE: private
+//
+// COMPLEXITY: 1
+//
+static void asdf_arch_init_strobe(void)
+{
+ if (strobe_polarity == ASDF_POSITIVE_POLARITY) {
+ clear_bit(&ASDF_STROBE_PORT, ASDF_STROBE_BIT);
+ }
+ else {
+ set_bit(&ASDF_STROBE_PORT, ASDF_STROBE_BIT);
+ }
+ set_bit(&ASDF_STROBE_DDR, ASDF_STROBE_BIT);
+}
+
+// PROCEDURE: asdf_arch_init_ascii_output
+// INPUTS: none
+// OUTPUTS: none
+//
+// DESCRIPTION: Sets up output port for ASCII output
+//
+// SIDE EFFECTS: See DESCRIPTION
+//
+// SCOPE: private
+//
+// COMPLEXITY: 1
+//
+static void asdf_arch_init_ascii_output(void)
+{
+ // set all outputs
+ ASDF_ASCII_PORT = data_polarity;
+ ASDF_ASCII_DDR = ALL_OUTPUTS;
+}
+
+// PROCEDURE: asdf_arch_init_columns
+// INPUTS: none
+// OUTPUTS: none
+//
+// DESCRIPTION: Sets up columns port as input.
+//
+// SIDE EFFECTS: See DESCRIPTION
+//
+// SCOPE: private
+//
+// COMPLEXITY: 1
+//
+static void asdf_arch_init_columns(void)
+{
+ ASDF_COLUMNS_DDR = ALL_INPUTS;
+}
+
+// PROCEDURE: asdf_arch_init_row_outputs
+// INPUTS: none
+// OUTPUTS: none
+//
+// DESCRIPTION: Sets up output port to latch keyboard matrix row for scanning.
+//
+// SIDE EFFECTS: See DESCRIPTION
+//
+// SCOPE: private
+//
+// COMPLEXITY: 1
+//
+static void asdf_arch_init_row_outputs(void)
+{
+ ASDF_HIROW_DDR = ALL_OUTPUTS;
+ ASDF_LOROW_DDR = ALL_OUTPUTS;
+}
+
+// PROCEDURE: asdf_arch_pulse_delay_short
+// INPUTS: none
+// OUTPUTS: none
+//
+// DESCRIPTION: Delays a fixed amount of time for keyboard output pulses specified by ASDF_PULSE_DELAY_SHORT_US
+//
+// SIDE EFFECTS: see above.
+//
+// NOTES: Set ASDF_PULSE_DELAY_US in asdf_config.h
+//
+// SCOPE: public
+//
+// COMPLEXITY: 1
+//
+void asdf_arch_pulse_delay_short(void)
+{
+ _delay_us(ASDF_PULSE_DELAY_SHORT_US);
+}
+
+// PROCEDURE: asdf_arch_pulse_delay_long
+// INPUTS: none
+// OUTPUTS: none
+//
+// DESCRIPTION: Delays a fixed amount of time for keyboard output pulses specified by ASDF_PULSE_DELAY_LONG_MS
+//
+// SIDE EFFECTS: see above.
+//
+// NOTES: Set ASDF_PULSE_DELAY_US in asdf_config.h
+//
+// SCOPE: public
+//
+// COMPLEXITY: 1
+//
+void asdf_arch_pulse_delay_long(void)
+{
+ _delay_us(ASDF_PULSE_DELAY_LONG_MS);
+}
+
+// PROCEDURE: asdf_arch_init
+// INPUTS: none
+// OUTPUTS: none
+//
+// DESCRIPTION: sets up all the hardware for the keyboard
+//
+// SIDE EFFECTS: see DESCRIPTION
+//
+// SCOPE: public
+//
+// COMPLEXITY: 1
+//
+void asdf_arch_init(void)
+{
+ // disable interrupts:
+ cli();
+
+ // clear the 1ms timer flag;
+ tick = 0;
+
+ // set up timers for 1 msec intervals
+ asdf_arch_init_clock();
+ asdf_arch_tick_timer_init();
+
+ // set up ASCII output port
+ asdf_arch_init_ascii_output();
+
+ // initialize keyboard data and strobe to positive polairy
+ data_polarity = ASDF_DEFAULT_DATA_POLARITY;
+ strobe_polarity = ASDF_DEFAULT_STROBE_POLARITY;
+
+ asdf_arch_init_strobe();
+ asdf_arch_init_leds();
+
+ // set up row and column ports
+ asdf_arch_init_row_outputs();
+ asdf_arch_init_columns();
+
+ // enable interrupts:
+ sei();
+}
+
+
+// PROCEDURE: asdf_arch_read_row
+// INPUTS: (uint8_t) row: the row number to be scanned
+// OUTPUTS: returns a word containing the active (pressed) columns
+//
+// DESCRIPTION: Outputs the argument to the ROW ports, then reads the column
+// port and returns the value. The value is a binary representation of the keys
+// pressed within the row, with 1=pressed, 0=released.
+//
+// SIDE EFFECTS: Sets ROW output port.
+//
+// NOTES:
+//
+// 1) The keymap represents an unpressed key as a "0" and a pressed key as a
+// "1". So, if a keypress pulls the column line low, then the reading of the
+// physical bits must be inverted.
+//
+// SCOPE: public
+//
+// COMPLEXITY: 1
+//
+asdf_cols_t asdf_arch_read_row(uint8_t row)
+{
+ uint16_t rows = (1 << row);
+
+ ASDF_HIROW_PORT = rows >> 8;
+ ASDF_LOROW_PORT = rows & 0xff;
+
+ return (asdf_cols_t) ASDF_COLUMNS_PORT;
+}
+
+
+// PROCEDURE: asdf_arch_send_code
+// INPUTS: (keycode_t) code - the 7-bit ASCII code to be output by the keyboard
+// OUTPUTS: none
+//
+// DESCRIPTION: Takes a character code and outputs the code on a parallel ASCII
+// port, with a strobe. This routine could be replaced with UART, I2C, USB, or
+// other output mechanism, of course.
+//
+// SIDE EFFECTS: See above.
+//
+// NOTES: The strobe is set by the ASDF_STROBE_LENGTH definition. The data
+// output and strobe polarity are set by the static data_polarity and static
+// strobe_polarity variables.
+//
+// SCOPE:
+//
+// COMPLEXITY:
+//
+
+void asdf_arch_send_code(asdf_keycode_t code)
+{
+ ASDF_ASCII_PORT = (code ^ data_polarity);
+
+
+ // toggle strobe. Must test before setting to avoid spurious strobe
+ set_bit(&ASDF_STROBE_PIN, ASDF_STROBE_BIT);
+
+ _delay_us(ASDF_STROBE_LENGTH_US);
+
+ set_bit(&ASDF_STROBE_PIN, ASDF_STROBE_BIT);
+}
+
+//-------|---------|---------+---------+---------+---------+---------+---------+
+// Above line is 80 columns, and should display completely in the editor.
+//
diff --git a/firmware/asdf/src/Arch/asdf_arch_atmega2560.h b/firmware/asdf/src/Arch/asdf_arch_atmega2560.h
new file mode 100644
index 0000000..ecd1a35
--- /dev/null
+++ b/firmware/asdf/src/Arch/asdf_arch_atmega2560.h
@@ -0,0 +1,462 @@
+// -*- mode: C; tab-width: 4 ; indent-tabs-mode: nil -*-
+//
+// Unfified Keyboard Project
+// ASDF keyboard firmware
+//
+// asdf_arch_atmega2560.h
+//
+// Contains architecture-specific definitions for the atmega 2560
+//
+//
+// Copyright 2019 David Fenyes
+//
+// 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, either version 3 of the License, or (at your option) any later
+// version.
+//
+// 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, see .
+
+#if !defined(ASDF_ARCH_H)
+#define ASDF_ARCH_H
+
+#include
+#include
+
+#include "asdf.h"
+
+// ASDF keyboard definitions:
+
+#define ASDF_STROBE_LENGTH_US 10 // strobe length in microseconds
+
+
+// Clock definitions:
+#define SYSCLK_DIV1 0
+#define SYSCLK_DIV2 (CLKPS0)
+#define SYCCLK_DIV4 (CLKPS1)
+#define SYSCLK_DIV8 (CLKPS1 | CLKPS0)
+#define SYSCLK_DIV16 (CLKPS2)
+#define SYSCLK_DIV32 (CLKPS2 | CLKPS0)
+#define SYSCLK_DIV64 (CLKPS2 | CLKPS1)
+#define SYSCLK_DIV128 (CLKPS2 | CLKPS1 | CLKPS0)
+#define SYSCLK_DIV256 (CLKPS3)
+
+// Timer 0 definitions
+// Define fields for register A, B, interrupt mask as 8-bit masks, and
+// as masks offset into a combined config word
+#define TMR0A_POS 0
+#define TMR0B_POS 8
+#define TMR0IMSK_POS 16
+#define TMR0A (1L << TMR0A_POS)
+#define TMR0B (1L << TMR0B_POS)
+#define TMR0IMSK (1L << TMR0IMSK_POS)
+
+#define TIMER0_COM_A_DISCONNECTED 0
+#define TIMER0_COM_B_DISCONNECTED 0
+#define TIMER0_WFM_CTC (TMR0A << WGM01)
+#define TIMER0_DIV64 ((TMR0B << CS01) | (TMR0B << CS00))
+#define TIMER0_INT_ON_COMA (TMR0IMSK << OCIE0A)
+#define TIMER0_INT_ON_COMB (TMR0IMSK << OCIE0B)
+#define TIMER0_INT_OV_ENABLE (TMR0IMSK << TOIE0)
+
+// Macros for 16-bit timers 1, 3, 4, 5. Datasheet section 17, p. 133
+//
+// Define fields for Registers A, B, C, INT Mask registers as 8-bit
+// masks, and as masks offset into a 32-bit combined config word.
+//
+// Macro definitions for individual registers are named TMRXA_*,
+// TMRXB_*, and TMRXC_*.
+//
+// Macros for the one-step combined timer config functions are named
+// TMRX_*.
+//
+//
+// Examples:
+// // Use TMRXB_* definition to set TCCR4B register
+// TCCR4B |= TMRXB_DIV1;
+//
+// // Use TMRX_* definitions to configure timer with config function
+// processor_timer4_config(TMRX_WFM_CTC | TMRX_INT_ON_CMPA);
+//
+#define TMRXA_POS 0
+#define TMRXB_POS 8
+#define TMRXC_POS 16
+#define TMRXIMSK_POS 24
+#define TMRXA (1L << TMRXA_POS)
+#define TMRXB (1L << TMRXB_POS)
+#define TMRXC (1L << TMRXC_POS)
+#define TMRXIMSK (1L << TMRXIMSK_POS)
+
+// 16-bit timer reg A - Datasheet 17.11.1, p. 154
+#define TMRXA_CMPA_CLR_MATCH_SET_BOTTOM (1 << COM1A1)
+#define TMRXA_CMPB_CLR_MATCH_SET_BOTTOM (1 << COM1B1)
+#define TMRXA_CMPC_CLR_MATCH_SET_BOTTOM (1 << COM1C1)
+#define TMRX_CMPA_CLR_MATCH_SET_BOTTOM (TMRXA << COM1A1)
+#define TMRX_CMPB_CLR_MATCH_SET_BOTTOM (TMRXA << COM1B1)
+#define TMRX_CMPC_CLR_MATCH_SET_BOTTOM (TMRXA << COM1C1)
+
+// 16-bit timer reg B - Datasheet 17.11.6, p. 156
+
+#define TMRXB_IN_CAP_POS 0x40L
+#define TMRXB_IN_CAP_NEG 0L
+#define TMRXB_IN_CAP_NOISE_CANCEL 0x80L
+
+// 16-bit timer reg C -- see datasheet, 17.11.9, p. 157
+#define TMRXC_FOCA 0x80L
+#define TMRXC_FOCB 0x40L
+#define TMRXC_FOCC 0x20L
+
+// 16-bit timer int mask -- see datasheet 17.11.33, p. 161.
+#define TMRXIM_INT_CMP_MATCH_A (1L << OCIE1A)
+#define TMRXIM_INT_CMP_MATCH_B (1L << OCIE1B)
+#define TMRXIM_INT_CMP_MATCH_C (1L << OCIE1C)
+
+// 16-bit timer all registers:
+#define TMRX_CMPA_DISCONNECTED 0L
+#define TMRX_CMPB_DISCONNECTED 0L
+#define TMRX_CMPC_DISCONNECTED 0L
+#define TMRX_INT_ON_CMPA (TMRXIM_INT_CMP_MATCH_A << TMRXIMSK_POS)
+
+// 16-bit timer clock modes - see Datasheet table 17-6, p. 157
+#define TMRXB_OFF 0
+#define TMRX_OFF 0
+#define TMRXB_DIV1 (0x01L << CS10)
+#define TMRXB_DIV8 (0x02L << CS10)
+#define TMRXB_DIV64 (0x03L << CS10)
+#define TMRXB_DIV256 (0x04L << CS10)
+#define TMRXB_DIV1024 (0x05L << CS10)
+#define TMRXB_EXT_FALLING_EDGE (0x06L << CS10)
+#define TMRXB_EXT_RISING_EDGE (0x07L << CS10)
+#define TMRXB_CLK_MASK 0x07L
+#define TMRX_DIV1 (TMRXB_DIV1 << TMRXB_POS)
+#define TMRX_DIV8 (TMRXB_DIV8 << TMRXB_POS)
+#define TMRX_DIV64 (TMRXB_DIV64 << TMRXB_POS)
+#define TMRX_DIV256 (TMRXB_DIV256 << TMRXB_POS)
+#define TMRX_DIV1024 (TMRXB_DIV1024 << TMRXB_POS)
+#define TMRX_EXT_FALLING_EDGE (TMRXB_EXT_FALLING_EDGE << TMRXB_POS)
+#define TMRX_EXT_RISING_EDGE (TMRXB_EXT_RISING_EDGE << TMRXB_POS)
+
+#define TMRXB_EDGE_SEL_POSITIVE (1 << ICES1)
+#define TMRXB_EDGE_SEL_NEGATIVE 0L
+#define TMRX_EDGE_SEL_POSITIVE (TMRXB << ICES1)
+#define TMRX_EDGE_SEL_NEGATIVE 0L
+
+// 16-bit waveform modes (across reg A and B) Datasheet Table 17.2, p 145
+#define TMRX_WFM_NORMAL 0L
+#define TMRX_WFM_PWM_PC8 (TMRXA << WGM10) // PWM Phase Correct 8-bit
+#define TMRX_WFM_PWM_PC9 (TMRXA << WGM11) // PWM Phase COrrect 9-bit
+#define TMRX_WFM_PWM_PC10 ((TMRXA << WGM11) | (TMRXA << WGM10)) // PWM Phase Correct 10-bit
+#define TMRX_WFM_CTC (TMRXB << WGM12) // CTC
+#define TMRX_WFM_PWM_FAST8 ((TMRXB << WGM12) | (TMRXA << WGM10)) // PWM Fast 8-bit
+#define TMRX_WFM_PWM_FAST9 ((TMRXB << WGM12) | (TMRXA << WGM11)) // PWM Fast 9-bit
+#define TMRX_WFM_PWM_FAST10 \
+ ((TMRXB << WGM12) | (TMRXA << WGM11) | (TMRXA << WGM10)) // PWM Fast 10-bit
+#define TMRX_WFM_PWM_PFC_ICR (TMRXB << WGM13) // PWM Phase and Freq Correct, TpOP=ICR
+#define TMRX_WFM_PWM_PFC_OCRA \
+ ((TMRXB << WGM13) | (TMRXA << WGM10)) // PWM Phase and Freq Correct, TOP = OCRA
+#define TMRX_WFM_PWM_PC_ICR ((TMRXB << WGM13) | (TMRXA << WGM11)) // PWM PhaseCorrect, TOP = ICR
+#define TMRX_WFM_PWM_PC_OCRA \
+ ((TMRXB << WGM13) | (TMRXA << WGM11) | (TMRXA << WGM12)) // PWM PhaseCorrect, TOP=OCRA
+#define TMRX_WFM_CTC_ICR ((TMRXB << WGM13) | (TMRXB << WGM12)) // CTC, TOP = ICR
+#define TMRX_WFM_PWM_FAST_ICR \
+ ((TMRXB << WGM13) | (TMRXB << WGM12) | (TMRXA << WGM11)) // PWM Fast, TOP = ICR
+#define TMRX_WFM_PWM_FAST_OCRA \
+ ((TMRXB << WGM13) | (TMRXB << WGM12) | (TMRXA << WGM11) \
+ | (TMRXA << WGM10)) // PWM Fast, TOP = OCRA
+
+// USART configuration (Datasheet section 22, p. 200)
+//
+// Macro definitions for individual registers are named USARTA_*,
+// USARTB_*, and USARTC_*.
+//
+// Macros for the one-step combined timer config functions are named
+// USART_*.
+//
+//
+// Examples:
+// // Use USARTB_* definition to set UCSR1B register
+// UCSR1B |= USARTB_DATA_REG_EMPTY_INT_EN; // enable interrupt on tx reg empty
+//
+// // Use USART_* definitions to configure usart with config function
+// processor_usart3_config(USART_SIZE_8 | USART_PARITY_NONE | USART_STOP_1 | USART_DATA_TXEN |
+// USART_DATA_RXEN | USART_RX_COMPLETE_INT_EN);
+//
+#define USARTA_POS 0
+#define USARTB_POS 8
+#define USARTC_POS 16
+#define USARTA (1L << USARTA_POS)
+#define USARTB (1L << USARTB_POS)
+#define USARTC (1L << USARTC_POS)
+
+// USART Register A, Datasheet 22.10.2, p. 219
+
+#define USARTA_DATA_REG_EMPTY (1 << UDR0)
+#define USARTA_FRAME_ERROR (1 << FE0)
+#define USARTA_DATA_OVERRUN (1 << DOR0)
+#define USARTA_PARITY_ERROR (1 << UPE0)
+#define USARTA_DOUBLE_SPEED (1 << U2X0)
+#define USARTA_MULTI_PROCESSOR (1 << MPCM0)
+#define USART_DATA_REG_EMPTY (USARTA << UDR0)
+#define USART_FRAME_ERROR (USARTA << FE0)
+#define USART_DATA_OVERRUN (USARTA << DOR0)
+#define USART_PARITY_ERROR (USARTA << UPE0)
+#define USART_DOUBLE_SPEED (USARTA << U2X0)
+#define USART_MULTI_PROCESSOR (USARTA << MPCM0)
+
+// USART Register B, Datasheet 22.10.3, p. 220
+
+#define USARTB_RX_COMPLETE_INT_EN (1 << RXCIE0)
+#define USARTB_TX_COMPLETE_INT_EN (1 << TXCIE0)
+#define USARTB_DATA_REG_EMPTY_INT_EN (1 << UDRIE0)
+#define USARTB_DATA_RXEN (1 << RXEN0)
+#define USARTB_DATA_TXEN (1 << TXEN0)
+#define USARTB_SIZE_9 (1 << UCSZ02)
+#define USARTB_RX_BIT_8 (1 << RXB80)
+#define USARTB_TX_BIT_8 (1 << TXB80)
+#define USART_RX_COMPLETE_INT_EN (USARTB << RXCIE0)
+#define USART_TX_COMPLETE_INT_EN (USARTB << TXCIE0)
+#define USART_DATA_REG_EMPTY_INT_EN (USARTB << UDRIE0)
+#define USART_DATA_RXEN (USARTB << RXEN0)
+#define USART_DATA_TXEN (USARTB << TXEN0)
+#define USART_RX_BIT_8 (USARTB << RXB80)
+#define USART_TX_BIT_8 (USARTB << TXB80)
+
+// USART Register C, Datasheet 22.10.4, p.221
+#define USARTC_MODE_ASYNC_USART (0x00L << UMSEL00)
+#define USARTC_MODE_SYNC_USART (0x01L << UMSEL00)
+#define USARTC_MODE_MSPIM (0x03L << UMSEL00)
+#define USART_MODE_ASYNC_USART (USARTC_MODE_ASYNC_USART << USARTC_POS)
+#define USART_MODE_SYNC_USART (USARTC_MODE_SYNC_USART << USARTC_POS)
+#define USART_MODE_MSPIM (USARTC_MODE_MSPIM << USARTC_POS)
+
+#define USARTC_PARITY_NONE 0
+#define USARTC_PARITY_EVEN (0x01L << UPM00)
+#define USARTC_PARITY_ODD (0x03 << UPM00)
+#define USART_PARITY_NONE 0
+#define USART_PARITY_EVEN (USARTC_PARITY_EVEN << USARTC_POS)
+#define USART_PARITY_ODD (USARTC_PARITY_ODD << USARTC_POS)
+
+#define USARTC_STOP_1 0
+#define USARTC_STOP_2 (1 << USBS0)
+#define USART_STOP_1 0
+#define USART_STOP_2 (USARTC << USBS0)
+
+#define USARTC_SIZE_5 0
+#define USARTC_SIZE_6 (0x01L << UCSZ00)
+#define USARTC_SIZE_7 (0x02L << UCSZ00)
+#define USARTC_SIZE_8 (0x03L << UCSZ00)
+#define USARTC_SIZE_9 USARTC_SIZE_8
+#define USART_SIZE_5 0
+#define USART_SIZE_6 (USARTC_SIZE_6 << USARTC_POS)
+#define USART_SIZE_7 (USARTC_SIZE_7 << USARTC_POS)
+#define USART_SIZE_8 (USARTC_SIZE_8 << USARTC_POS)
+#define USART_SIZE_9 ((USARTB_SIZE_9 << USARTB_POS) | (USARTC_SIZE_9 << USARTC_POS))
+
+#define USARTC_CLK_TX_RISING 0
+#define USART_CLK_TX_RISING 0
+#define USARTC_CLK_TX_FALLING (1 << UCPOL0)
+#define USART_CLK_TX_FALLING (USARTC << UCPOL0)
+
+
+
+// I/O port definitions:
+
+#define PIN_INPUT 0
+#define PIN_OUTPUT 1
+#define ALL_INPUTS 0
+#define ALL_OUTPUTS 0xff
+
+#define ASDF_HIROW_PORT PORTA
+#define ASDF_HIROW_DDR DDRA
+
+#define ASDF_LOROW_PORT PORTJ
+#define ASDF_LOROW_DDR DDRJ
+
+#define ASDF_COLUMNS_PORT PORTC
+#define ASDF_COLUMNS_PIN PINC
+#define ASDF_COLUMNS_DDR DDRC
+#define ASDF_COLUMNS_PULLUPS 0 // disable weak pullup
+
+#define ASDF_ASCII_PORT PORTH
+#define ASDF_ASCII_DDR DDRH
+
+#define ASDF_LED1_PORT PORTD
+#define ASDF_LED1_DDR DDRD
+#define ASDF_LED1_BIT 5
+
+#define ASDF_LED2_PORT PORTD
+#define ASDF_LED2_DDR DDRD
+#define ASDF_LED2_BIT 6
+
+#define ASDF_LED3_PORT PORTD
+#define ASDF_LED3_DDR DDRD
+#define ASDF_LED3_BIT 7
+
+#define ASDF_OUT1_PORT PORTB
+#define ASDF_OUT1_PIN PINB
+#define ASDF_OUT1_DDR DDRB
+#define ASDF_OUT1_BIT 5
+
+#define ASDF_OUT2_PORT PORTB
+#define ASDF_OUT2_PIN PINB
+#define ASDF_OUT2_DDR DDRB
+#define ASDF_OUT2_BIT 6
+
+#define ASDF_OUT3_PORT PORTB
+#define ASDF_OUT3_PIN PINB
+#define ASDF_OUT3_DDR DDRB
+#define ASDF_OUT3_BIT 7
+
+#define ASDF_STROBE_PORT PORTB
+#define ASDF_STROBE_PIN PINB
+#define ASDF_STROBE_DDR DDRB
+#define ASDF_STROBE_BIT 4
+
+#define FUSE_INTERNAL_8MHZ_OSC_4MS (FUSE_CKSEL1 | FUSE_SUT0)
+#define FUSE_INTERNAL_8MHZ_OSC_65MS (FUSE_CKSEL1 | FUSE_SUT1)
+#define FUSE_XTAL_16MHZ_4MS (FUSE_CKSEL2 | FUSE_CKSEL1 | CKSEL0 | FUSE_SUT1)
+#define FUSE_XTAL_16MHZ_65MS (FUSE_CKSEL2 | FUSE_CKSEL1 | CKSEL0 | FUSE_SUT1 | FUSE_SUT0)
+#define FLASH PROGMEM
+
+// not implemented with do-while(0) because this is a function call that returns
+// a value, and parameters are expanded inside the parameter list, so this will
+// be valid when substituting for function-like syntax.
+#define FLASH_READ (a) pgm_read_byte((a))
+#define FLASH_READ_MATRIX_ELEMENT(matrix, row, col) pgm_read_byte(&((matrix)[(row)][(col)]))
+
+// For 1 ms tick, (16000000 / 64(prescale)) / 1000(usec) - 1 = 249
+#define TICK_COUNT 249
+
+// PROCEDURE: asdf_arch_null_output
+// INPUTS: (uint8_t) value - ignored
+// OUTPUTS: none
+// DESCRIPTION: null/dummy output function
+// NOTES: Not supported for the ATMega-328 ASCII interface.
+void asdf_arch_null_output(uint8_t value);
+
+// PROCEDURE: asdf_arch_led1_set
+// INPUTS: (uint8_t) value
+// OUTPUTS: none
+// DESCRIPTION: If value is true, turn on LED1. If value is false, turn off LED1
+void asdf_arch_led1_set(uint8_t value);
+
+// PROCEDURE: asdf_arch_led2_set
+// INPUTS: (uint8_t) value
+// OUTPUTS: none
+// DESCRIPTION: If value is true, turn on LED2. If value is false, turn off LED2
+void asdf_arch_led2_set(uint8_t value);
+
+// PROCEDURE: asdf_arch_led3_set
+// INPUTS: (uint8_t) value
+// OUTPUTS: none
+// DESCRIPTION: If value is true, turn on LED3. If value is false, turn off LED3
+void asdf_arch_led3_set(uint8_t value);
+
+// PROCEDURE: asdf_arch_out1_set
+// INPUTS: (uint8_t) value
+// OUTPUTS: none
+// DESCRIPTION: Sets the OUT1 bit if value is true, and clear OUT1 if value is false.
+void asdf_arch_out1_set(uint8_t value);
+
+// PROCEDURE: asdf_arch_out1_open_hi_set
+// INPUTS: (uint8_t) value
+// OUTPUTS: none
+// DESCRIPTION: Sets the OUT1 bit to hi-z if value is true, and low if value is false.
+void asdf_arch_out1_open_hi_set(uint8_t value);
+
+// PROCEDURE: asdf_arch_out1_open_lo_set
+// INPUTS: (uint8_t) value
+// OUTPUTS: none
+// DESCRIPTION: Sets the OUT1 bit to hi-z if value is true, and low if value is false.
+void asdf_arch_out1_open_lo_set(uint8_t value);
+
+// PROCEDURE: asdf_arch_out2_set
+// INPUTS: (uint8_t) value
+// OUTPUTS: none
+// DESCRIPTION: Sets the OUT2 bit if value is true, and clear OUT2 if value is false.
+void asdf_arch_out2_set(uint8_t value);
+
+// PROCEDURE: asdf_arch_out2_open_hi_set
+// INPUTS: (uint8_t) value
+// OUTPUTS: none
+// DESCRIPTION: Sets the OUT2 bit to hi-z if value is true, and low if value is false.
+// NOTES: Not supported for the ATMega-328 ASCII interface.
+void asdf_arch_out2_open_hi_set(uint8_t value);
+
+// PROCEDURE: asdf_arch_out2_open_lo_set
+// INPUTS: (uint8_t) value
+// OUTPUTS: none
+// DESCRIPTION: Sets the OUT2 bit to high if value is true, and hi-z if value is false.
+void asdf_arch_out2_open_lo_set(uint8_t value);
+
+// PROCEDURE: asdf_arch_out3_set
+// INPUTS: (uint8_t) value
+// OUTPUTS: none
+// DESCRIPTION: Sets the OUT3 bit if value is true, and clear OUT3 if value is false.
+void asdf_arch_out3_set(uint8_t value);
+
+// PROCEDURE: asdf_arch_out3_open_hi_set
+// INPUTS: (uint8_t) value
+// OUTPUTS: none
+// DESCRIPTION: Sets the OUT3 bit to hi-z if value is true, and low if value is false.
+void asdf_arch_out3_open_hi_set(uint8_t value);
+
+// PROCEDURE: asdf_arch_out3_open_lo_set
+// INPUTS: (uint8_t) value
+// OUTPUTS: none
+// DESCRIPTION: Sets the OUT3 bit to hi-z if value is true, and low if value is false.
+void asdf_arch_out3_open_lo_set(uint8_t value);
+
+// PROCEDURE: asdf_arch_read_row
+// INPUTS: (uint8_t) row: the row number to be scanned
+// OUTPUTS: returns a word containing the active (pressed) columns
+// DESCRIPTION: Outputs the argument to the ROW port, then reads the column port
+// and returns the value. The value is a binary representation of the keys
+// pressed within the row, with 1=pressed, 0=released.
+asdf_cols_t asdf_arch_read_row(uint8_t row);
+
+// PROCEDURE: asdf_arch_pulse_delay_short
+// INPUTS: none
+// OUTPUTS: none
+// DESCRIPTION: Delays a fixed amount of time for keyboard output pulses specified by
+// ASDF_PULSE_DELAY_SHORT_US
+void asdf_arch_pulse_delay_short(void);
+
+
+// PROCEDURE: asdf_arch_pulse_delay_long
+// INPUTS: none
+// OUTPUTS: none
+// DESCRIPTION: Delays a fixed amount of time for keyboard output pulses specified by
+// ASDF_PULSE_DELAY_LONG_MS
+void asdf_arch_pulse_delay_long(void);
+
+
+// PROCEDURE: asdf_arch_tick
+// INPUTS: none
+// OUTPUTS: returns a 1 if the 1ms timer timed out, 0 otherwise
+uint8_t asdf_arch_tick(void);
+
+// PROCEDURE: asdf_arch_send_code
+// INPUTS: (keycode_t) code - the code to be output by the keyboard
+// OUTPUTS: none
+// DESCRIPTION: Takes a character code and outputs the code on a parallel ASCII
+// port, with a strobe. This routine could be replaced with UART, I2C, USB, or
+// other output mechanism, of course.
+void asdf_arch_send_code(asdf_keycode_t code);
+
+// PROCEDURE: asdf_arch_init
+// INPUTS: none
+// OUTPUTS: none
+// DESCRIPTION: sets up all the hardware for the keyboard
+void asdf_arch_init(void);
+
+#endif /* !defined (ASDF_ARCH_H) */
+
+//-------|---------|---------+---------+---------+---------+---------+---------+
+// Above line is 80 columns, and should display completely in the editor.
diff --git a/firmware/asdf/src/Makefile b/firmware/asdf/src/Makefile
index 3189444..39ce15c 100644
--- a/firmware/asdf/src/Makefile
+++ b/firmware/asdf/src/Makefile
@@ -1,23 +1,25 @@
# -*- makefile -*-
-ARCH ?= atmega328p
-KEYMAP ?= ascii
+ARCH ?= atmega2560
+TARGET_KEYMAP ?= production
+TARGET_MAKEFILE ?= Makefile.$(ARCH)
BUILD_DIR = ../build
all: app
app:
- make -f Makefile.app
+ make -f $(TARGET_MAKEFILE)
test:
make -f Makefile.test
clean:
- make -f Makefile.app clean
+ make -f $(TARGET_MAKEFILE) clean
make -f Makefile.test clean
cleanall:
- make -f Makefile.app cleanall
+ make -f $(TARGET_MAKEFILE)
+cleanall
make -f Makefile.test cleanall
diff --git a/firmware/asdf/src/Makefile.atmega2560 b/firmware/asdf/src/Makefile.atmega2560
new file mode 100644
index 0000000..bfd8562
--- /dev/null
+++ b/firmware/asdf/src/Makefile.atmega2560
@@ -0,0 +1,163 @@
+# -*- makefile -*-
+
+ARCH ?= atmega2560
+KEYMAP ?= production
+
+ARCH_TOKEN = _Arch_$(ARCH)
+
+TARGET = asdf-$(ARCH)
+
+TEST_DIR = ../test
+UNITY_DIR = $(TEST_DIR)/unity
+BUILD_DIR = ../build
+DEP_DIR := ./.deps
+
+TARGET_BIN := $(BUILD_DIR)/$(TARGET).elf
+TARGET_HEX := $(BUILD_DIR)/$(TARGET).hex
+TARGET_MAP := $(BUILD_DIR)/$(TARGET).map
+
+UNITY_SCRIPTS = $(UNITY_DIR)/auto
+ARCH_DIR = Arch
+KEYMAPS_DIR = Keymaps
+VERSION =
+RELEASE=
+SIZE_COMMAND = avr-size
+
+CLOCK = 16000000L
+
+
+CLEAN_FILES =
+CLEANALL_FILES =
+
+CC = avr-gcc
+
+CFLAGS = -std=c99
+CFLAGS += -Wall
+CFLAGS += -funsigned-char
+CFLAGS += -funsigned-bitfields
+CFLAGS += -ffunction-sections
+CFLAGS += -fdata-sections
+CFLAGS += -fpack-struct
+CFLAGS += -fshort-enums
+CFLAGS += -O2
+CFLAGS += -Wall
+CFLAGS += -Wextra
+CFLAGS += -Wpointer-arith
+CFLAGS += -Wcast-align
+CFLAGS += -Wwrite-strings
+CFLAGS += -Wswitch-default
+CFLAGS += -Wunreachable-code
+CFLAGS += -Winit-self
+CFLAGS += -Wmissing-field-initializers
+CFLAGS += -Wno-unknown-pragmas
+CFLAGS += -Wstrict-prototypes
+CFLAGS += -Wundef
+CFLAGS += -Wold-style-definition
+CFLAGS += -mmcu=$(ARCH)
+CFLAGS += -DF_CPU=$(CLOCK)
+LDFLAGS = -Wl,-Map=$(TARGET_MAP)
+LDFLAGS += -Wl,--start-group
+LDFLAGS += -Wl,-lm
+LDFLAGS += -Wl,--end-group
+LDFLAGS += -Wl,--gc-sections
+
+DEPFLAGS = -MT $@ -MMD -MP -MF $(DEPDIR)/$*.d
+
+MAKEDEPEND = $(CPP) $(DEPFLAGS) $(CPPFLAGS) $< \
+ | sed -n 's,^\# *[0-9][0-9]* *"\([^"<]*\)".*,$@: \1\n\1:,p' \
+ | sort -u > $*.d
+
+SRC_FILES = main.c asdf.c asdf_modifiers.c asdf_repeat.c asdf_keymaps.c
+SRC_FILES += asdf_buffer.c asdf_arch.c asdf_virtual.c asdf_physical.c
+
+OBJ_FILES := $(SRC_FILES:.c=.o)
+DEP_FILES := $(SRC_FILES:%.c=$(DEP_DIR)/%.d)
+MAP_FILE = $(TARGET).map
+
+CLEAN_FILES += $(MAP_FILE)
+
+CLEAN_FILES += $(TARGET_BIN)
+CLEAN_FILES += $(TARGET_MAP)
+CLEANALL_FILES += $(TARGET_HEX)
+
+MAKEFILES = Makefile
+GENERATED_FILES = conventions.h machine.h
+ALL_FILES = $(MAKEFILES) $(SRC_FILES) $(TXTFILES) $(GENERATED_FILES)
+
+.SUFFIXES:
+.SUFFIXES: .c .o .bin .hex
+
+
+all: $(TARGET_HEX)
+
+%.d : %.c $(DEP_DIR)/%.d | $(DEP_DIR)
+ @$(MAKEDEPEND)
+
+$(DEP_DIR): ; @mkdir -p $@
+
+$(DEPFILES):
+
+include $(wildcard $(DEPFILES))
+
+%.o: %.c $(DEP_DIR)/%.d | $(DEP_DIR)
+ $(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEPFLAGS) $<
+
+asdf_keymap_defs.h: $(KEYMAPS_DIR)/asdf_all_keymap_defs_$(KEYMAP).h $(KEYMAPDEFS_H_TOKEN)
+ cp $< $@
+GENERATED_FILES += asdf_keymap_defs.h
+
+
+asdf_arch.c: $(ARCH_DIR)/asdf_arch_$(ARCH).c $(ARCH_C_TOKEN)
+ cp $< $@
+GENERATED_FILES += asdf_arch.c
+
+
+asdf_arch.h: $(ARCH_DIR)/asdf_arch_$(ARCH).h $(ARCH_H_TOKEN)
+ cp $< $@
+GENERATED_FILES += asdf_arch.h
+
+$(ARCH_H_TOKEN):
+ touch $@
+
+$(ARCH_C_TOKEN):
+ touch $@
+
+$(KEYMAP_H_TOKEN):
+ touch $@
+
+size:
+ $(SIZE_COMMAND) $(TARGET_BIN)
+
+$(TARGET_HEX): $(TARGET_BIN)
+ avr-objcopy -j .text -j .data -j .fuses -O ihex $< $@
+
+$(TARGET_BIN): $(OBJ_FILES)
+ $(CC) $(CFLAGS) -o $@ $(LDFLAGS) $^
+ $(SIZE_COMMAND) $(TARGET_BIN)
+
+asdf_keymaps.o: asdf_keymaps.c asdf.h asdf_ascii.h asdf_modifiers.h asdf_arch.h asdf_keymaps.h asdf_keymap_defs.h
+main.o: main.c asdf.h asdf_arch.h
+asdf.o: asdf.c asdf.h asdf_arch.h asdf_keymaps.h asdf_config.h asdf_keymap_defs.h
+asdf_repeat.o: asdf_repeat.c asdf_repeat.h asdf_config.h
+asdf_buffer.o: asdf_buffer.c asdf.h asdf_config.h
+asdf_modifiers.o: asdf_modifiers.c asdf_modifiers.h
+asdf_virtual.o: asdf_virtual.c asdf_virtual.h asdf_arch.h asdf_physical.h
+asdf_physical.o: asdf_physical.c asdf_virtual.h asdf_arch.h
+
+tags: $(SRC_FILES)
+ etags $(SRC_FILES)
+
+CLEAN_FILES += $(TEST_BUILD_FILES) _Arch_* *.o
+CLEAN_FILES += ~* *\#
+
+CLEANALL_FILES += $(GENERATED_FILES) $(TARGET_BUILD_FILES) $(TEST_BUILD_FILES)
+
+.PHONY: clean
+clean:
+ rm -f $(CLEAN_FILES)
+
+.PHONY: cleanall
+cleanall:
+ rm -f $(CLEAN_FILES) $(CLEANALL_FILES)
+
+
diff --git a/firmware/asdf/src/Makefile.app b/firmware/asdf/src/Makefile.atmega328p
similarity index 99%
rename from firmware/asdf/src/Makefile.app
rename to firmware/asdf/src/Makefile.atmega328p
index 75c89a1..048d0d0 100644
--- a/firmware/asdf/src/Makefile.app
+++ b/firmware/asdf/src/Makefile.atmega328p
@@ -5,7 +5,7 @@ KEYMAP ?= production
ARCH_TOKEN = _Arch_$(ARCH)
-TARGET = asdf
+TARGET = asdf-$(ARCH)
TEST_DIR = ../test
UNITY_DIR = $(TEST_DIR)/unity