summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJames McKenzie <root@ka-ata-killa.panaceas.james.local>2024-04-07 03:21:18 +0100
committerJames McKenzie <root@ka-ata-killa.panaceas.james.local>2024-04-07 03:21:18 +0100
commit5917edfb601abc878eb1ebc971c6ab1e9d47b645 (patch)
tree4a24525e8aeec685841ab9b16e0cffa156d7c0b3
parent81b486e475463a58cda2b706dcf4fb1a39f8612f (diff)
downloadSensor-Watch-pq-5917edfb601abc878eb1ebc971c6ab1e9d47b645.tar.gz
Sensor-Watch-pq-5917edfb601abc878eb1ebc971c6ab1e9d47b645.tar.bz2
Sensor-Watch-pq-5917edfb601abc878eb1ebc971c6ab1e9d47b645.zip
upadte for d2b5ed7
-rw-r--r--.gitignore1
-rw-r--r--main/debug-compile.patch29
-rw-r--r--main/dont_set_time_if_no_debugger.patch564
-rw-r--r--main/fix-serial.patch149
-rw-r--r--main/metric.patch388
-rw-r--r--main/series3
-rw-r--r--main/set-time.patch83
7 files changed, 748 insertions, 469 deletions
diff --git a/.gitignore b/.gitignore
index 8be5547..f5038c3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,2 @@
status
+*~
diff --git a/main/debug-compile.patch b/main/debug-compile.patch
new file mode 100644
index 0000000..838dce9
--- /dev/null
+++ b/main/debug-compile.patch
@@ -0,0 +1,29 @@
+diff --git a/make.mk b/make.mk
+index bb2d153..ee40100 100644
+--- a/make.mk
++++ b/make.mk
+@@ -58,11 +58,13 @@ CFLAGS += -fdata-sections -ffunction-sections
+ CFLAGS += -funsigned-char -funsigned-bitfields
+ CFLAGS += -mcpu=cortex-m0plus -mthumb
+ CFLAGS += -MD -MP -MT $(BUILD)/$(*F).o -MF $(BUILD)/$(@F).d
++CFLAGS += -g
+
+ LDFLAGS += -mcpu=cortex-m0plus -mthumb
+ LDFLAGS += -Wl,--gc-sections
+ LDFLAGS += -Wl,--script=$(TOP)/watch-library/hardware/linker/saml22j18.ld
+ LDFLAGS += -Wl,--print-memory-usage
++LDFLAGS += -g
+
+ LIBS += -lm
+
+diff --git a/movement/movement.h b/movement/movement.h
+index 1dabfbc..ffceae6 100644
+--- a/movement/movement.h
++++ b/movement/movement.h
+@@ -312,4 +312,6 @@ void movement_play_alarm_beeps(uint8_t rounds, BuzzerNote alarm_note);
+
+ uint8_t movement_claim_backup_register(void);
+
++void set_time(void);
++
+ #endif // MOVEMENT_H_
diff --git a/main/dont_set_time_if_no_debugger.patch b/main/dont_set_time_if_no_debugger.patch
new file mode 100644
index 0000000..f38a49a
--- /dev/null
+++ b/main/dont_set_time_if_no_debugger.patch
@@ -0,0 +1,564 @@
+diff --git a/make.mk b/make.mk
+index 5610c1f..3cc2d6f 100644
+--- a/make.mk
++++ b/make.mk
+@@ -124,6 +124,7 @@ SRCS += \
+ $(TOP)/watch-library/hardware/watch/watch_deepsleep.c \
+ $(TOP)/watch-library/hardware/watch/watch_private.c \
+ $(TOP)/watch-library/hardware/watch/watch_private_cdc.c \
++ $(TOP)/watch-library/hardware/watch/m0FaultDispatch.c \
+ $(TOP)/watch-library/hardware/watch/watch.c \
+ $(TOP)/watch-library/hardware/hal/src/hal_atomic.c \
+ $(TOP)/watch-library/hardware/hal/src/hal_delay.c \
+diff --git a/movement/set_time.c b/movement/set_time.c
+index 27cea25..0cea3d8 100644
+--- a/movement/set_time.c
++++ b/movement/set_time.c
+@@ -7,11 +7,27 @@
+ #include "watch.h"
+ #include "movement.h"
+ #include "watch_utility.h"
++#include "m0FaultDispatch.h"
+
+ #define SYS_TIME 0x11
+
+ extern movement_state_t movement_state;
+
++void
++faultHandlerWithExcFrame (struct CortexExcFrame *exc, uint32_t reason,
++ uint32_t addr, struct CortexPushedRegs *hiRegs)
++{
++
++ if (reason == EXC_m0_CAUSE_BKPT_HIT)
++ {
++ exc->pc += 4;
++ exc->r0_r3[0] = 0;
++ return;
++ }
++
++ while (1);
++}
++
+ static inline int32_t
+ get_tz_offset (movement_settings_t * settings)
+ {
+@@ -32,6 +48,9 @@ void __attribute__((used)) set_time (void)
+
+ t = sys_time ();
+
++ if (!t)
++ return;
++
+ watch_date_time wdt = watch_utility_date_time_from_unix_time (t,
+ get_tz_offset
+ (&movement_state.
+diff --git a/watch-library/hardware/watch/m0FaultDispatch.c b/watch-library/hardware/watch/m0FaultDispatch.c
+new file mode 100644
+index 0000000..da00b73
+--- /dev/null
++++ b/watch-library/hardware/watch/m0FaultDispatch.c
+@@ -0,0 +1,460 @@
++#include "m0FaultDispatch.h"
++#include <stdbool.h>
++
++#define STR2(x) #x
++#define STR(x) STR2(x)
++
++
++
++static uint32_t analyzeInstr16PrvUtilGetReg(const struct CortexExcFrame* frm, struct CortexPushedRegs *moreRegs, uint32_t regNo)
++{
++ switch (regNo) {
++ case 0: return frm->r0;
++ case 1: return frm->r1;
++ case 2: return frm->r2;
++ case 3: return frm->r3;
++ case 4: //fallthrough
++ case 5: //fallthrough
++ case 6: //fallthrough
++ case 7: return moreRegs->regs4_7[regNo - 4];
++ case 8: //fallthrough
++ case 9: //fallthrough
++ case 10: //fallthrough
++ case 11: return moreRegs->regs8_11[regNo - 8];
++ case 12: return frm->r12;
++ case 13: return (uintptr_t)(frm + 1);
++ case 14: return frm->lr;
++ case 15: return frm->pc + 4;
++ default: return 0xffffffff;
++ }
++}
++
++static bool load8(uint32_t addr, uint32_t *dstP)
++{
++ uint32_t tmp;
++ bool ret;
++
++ asm volatile(
++ " mov %0, #1 \n\t" //special formulation for our fault handler
++ " ldrb %1, [%2] \n\t"
++ " b 1f \n\t"
++ " mov %0, #0 \n\t"
++ "1: \n\t"
++ " str %1, [%3] \n\t"
++ :"=&l"(ret), "=&l"(tmp)
++ :"l"(addr), "l"(dstP)
++ :"cc", "memory");
++
++ return ret;
++}
++
++static bool store8(uint32_t addr, uint32_t val)
++{
++ bool ret;
++
++ asm volatile(
++ " mov %0, #1 \n\t" //special formulation for our fault handler
++ " strb %2, [%1] \n\t"
++ " b 1f \n\t"
++ " mov %0, #0 \n\t"
++ "1: \n\t"
++ :"=&l"(ret)
++ :"l"(addr), "l"(val)
++ :"cc", "memory");
++
++ return ret;
++}
++
++static bool load16(uint32_t addr, uint32_t *dstP)
++{
++ uint32_t tmp;
++ bool ret;
++
++ asm volatile(
++ " mov %0, #1 \n\t" //special formulation for our fault handler
++ " ldrh %1, [%2] \n\t"
++ " b 1f \n\t"
++ " mov %0, #0 \n\t"
++ "1: \n\t"
++ " str %1, [%3] \n\t"
++ :"=&l"(ret), "=&l"(tmp)
++ :"l"(addr), "l"(dstP)
++ :"cc", "memory");
++
++ return ret;
++}
++
++static bool store16(uint32_t addr, uint32_t val)
++{
++ bool ret;
++
++ asm volatile(
++ " mov %0, #1 \n\t" //special formulation for our fault handler
++ " strh %2, [%1] \n\t"
++ " b 1f \n\t"
++ " mov %0, #0 \n\t"
++ "1: \n\t"
++ :"=&l"(ret)
++ :"l"(addr), "l"(val)
++ :"cc", "memory");
++
++ return ret;
++}
++
++static bool load32(uint32_t addr, uint32_t *dstP)
++{
++ uint32_t tmp;
++ bool ret;
++
++ asm volatile(
++ " mov %0, #1 \n\t" //special formulation for our fault handler
++ " ldr %1, [%2] \n\t"
++ " b 1f \n\t"
++ " mov %0, #0 \n\t"
++ "1: \n\t"
++ " str %1, [%3] \n\t"
++ :"=&l"(ret), "=&l"(tmp)
++ :"l"(addr), "l"(dstP)
++ :"cc", "memory");
++
++ return ret;
++}
++
++static bool store32(uint32_t addr, uint32_t val)
++{
++ bool ret;
++
++ asm volatile(
++ " mov %0, #1 \n\t" //special formulation for our fault handler
++ " str %2, [%1] \n\t"
++ " b 1f \n\t"
++ " mov %0, #0 \n\t"
++ "1: \n\t"
++ :"=&l"(ret)
++ :"l"(addr), "l"(val)
++ :"cc", "memory");
++
++ return ret;
++}
++
++//we also get a pointer to the hiregs since we need to reset them before we call faultHandlerWithExcFrame()
++// we get them in pushedRegs
++void __attribute__((used)) analyzeInstr16(struct CortexExcFrame* frm, uint16_t instr, struct CortexPushedRegs *pushedRegs)
++{
++ static const uint8_t popCntTab[] = {0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4}; //in a nibble
++ static bool (*const accessR[])(uint32_t addr, uint32_t *dstP) = {load8, load16, load32};
++ static bool (*const accessW[])(uint32_t addr, uint32_t val) = {store8, store16, store32};
++ uint_fast8_t excCause = EXC_m0_CAUSE_UNCLASSIFIABLE, accessSzLog = 0;
++ uint32_t addr, val, excExtraData = 0, ofst = 0, base = 0, i;
++ bool testStore = false;
++
++ switch (instr >> 11) {
++ case 0b01000:
++ if ((instr & 0x05c0) == 0x0500) //cmp.hi with both lo regs
++ excCause = EXC_m0_CAUSE_UNDEFINSTR16;
++ if ((instr >> 8) == 0x47) { //BX or BLX
++
++ if (instr & 7) //SBZ bits not Z
++ excCause = EXC_m0_CAUSE_UNDEFINSTR16;
++ else if ((instr & 0x78) == 0x78) //PC
++ excCause = EXC_m0_CAUSE_UNDEFINSTR16;
++ }
++ break;
++
++ case 0b01001: //load from literal pool (alignment guaranteed by instr)
++ addr = ((frm->pc + 4) &~ 3) + ((instr & 0xff) << 2);
++ if (!load32(addr, &val)) {
++
++ excCause = EXC_m0_CAUSE_MEM_READ_ACCESS_FAIL;
++ excExtraData = addr;
++ }
++ break;
++
++ case 0b01010: //load/store register
++ case 0b01011:
++ switch ((instr >> 9) & 0x07) {
++ case 0b000: //STR
++ testStore = true;
++ //fallthrough
++ case 0b100: //LDR
++ accessSzLog = 2;
++ break;
++ case 0b001: //STRH
++ testStore = true;
++ //fallthrough
++ case 0b101: //LDRH
++ case 0b111: //LDRSH
++ accessSzLog = 1;
++ break;
++ case 0b010: //STRB
++ testStore = true;
++ //fallthrough
++ case 0b110: //LDRB
++ case 0b011: //LDRSB
++ accessSzLog = 0;
++ break;
++ }
++ ofst = analyzeInstr16PrvUtilGetReg(frm, pushedRegs, (instr >> 6) & 0x07);
++ goto load_store_check;
++
++ case 0b01100: //str imm
++ testStore = true;
++ //fallthrough
++ case 0b01101: //ldr imm
++ accessSzLog = 2;
++ goto load_store_check_get_imm;
++
++ case 0b01110: //strb imm
++ testStore = true;
++ //fallthrough
++ case 0b01111: //ldrb imm
++ accessSzLog = 0;
++ goto load_store_check_get_imm;
++
++ case 0b10000: //strh imm
++ testStore = true;
++ //fallthrough
++
++ case 0b10001: //ldrh imm
++ accessSzLog = 1;
++ goto load_store_check_get_imm;
++
++load_store_check_get_imm:
++ ofst = ((instr >> 6) & 0x1f) << accessSzLog;
++
++load_store_check:
++ base = analyzeInstr16PrvUtilGetReg(frm, pushedRegs, (instr >> 3) & 0x07);
++
++load_store_check_have_base:
++ addr = base + ofst;
++
++ if (addr << (32 - accessSzLog)) {
++
++ excCause = EXC_m0_CAUSE_DATA_UNALIGNED;
++ excExtraData = addr;
++ }
++ else if (!accessR[accessSzLog](addr, &val)) {
++
++ excCause = EXC_m0_CAUSE_MEM_READ_ACCESS_FAIL;
++ excExtraData = addr;
++ }
++ else if (testStore && !accessW[accessSzLog](addr, val)) {
++
++ excCause = EXC_m0_CAUSE_MEM_WRITE_ACCESS_FAIL;
++ excExtraData = addr;
++ }
++ //if we have not faulted by now, we do not know why this load/store faulted
++ break;
++
++ case 0b10010: //sp-based store
++ testStore = true;
++ //fallthrough
++
++ case 0b10011: //sp-based load
++ ofst = (instr & 0xff) * 4;
++ accessSzLog = 3;
++ base = analyzeInstr16PrvUtilGetReg(frm, pushedRegs, 13);
++ goto load_store_check_have_base;
++
++ case 0b10111:
++ if ((instr & 0x0700) == 0x0600)
++ excCause = EXC_m0_CAUSE_BKPT_HIT;
++ //push/pop are here too, but if they fail, we'd fail to stash and not ever get here
++ break;
++
++ case 0b11000: //STMIA
++ testStore = true;
++ //fallthrough
++ case 0b11001: //LDMIA
++ base = analyzeInstr16PrvUtilGetReg(frm, pushedRegs, (instr >> 8) & 0x07);
++ if (base & 3) { //unaligned base
++ excExtraData = base;
++ excCause = EXC_m0_CAUSE_DATA_UNALIGNED;
++ }
++ else if (!(instr & 0xff)) //LDM/STM with empty reg set
++ excCause = EXC_m0_CAUSE_UNDEFINSTR16;
++ else {
++ i = popCntTab[instr & 0x0f] + popCntTab[(instr >> 4) & 0x0f];
++
++ do {
++
++ addr = base;
++ base += 4;
++
++ if (!load32(addr, &val)) {
++
++ excCause = EXC_m0_CAUSE_MEM_READ_ACCESS_FAIL;
++ excExtraData = addr;
++ }
++ else if (testStore && !store32(addr, val)) {
++
++ excCause = EXC_m0_CAUSE_MEM_WRITE_ACCESS_FAIL;
++ excExtraData = addr;
++ }
++ else
++ continue;
++
++ break; //one fault found is enough
++
++ } while (--i);
++ //if we have not faulted by now, we do not know why this ldm/stm faulted
++ }
++ break;
++
++ case 0b11011:
++ if ((instr & 0x0700) == 0x0600) //UDF
++ excCause = EXC_m0_CAUSE_UNDEFINSTR16;
++ break;
++ }
++
++ faultHandlerWithExcFrame(frm, excCause, excExtraData, pushedRegs);
++}
++
++void __attribute__((used,naked)) iHardFault_Handler(void)
++{
++ asm volatile(
++
++ //grab the appropriate SP
++ " mov r0, lr \n\t"
++ " lsr r0, #3 \n\t"
++ " bcs 1f \n\t"
++ " mrs r0, msp \n\t"
++ " b 2f \n\t"
++ "1: \n\t"
++ " mrs r0, psp \n\t"
++ "2: \n\t"
++
++ //check for ARM mode
++ " ldr r1, [r0, #4 * 7] \n\t" //load pushed flags
++ " mov r3, #1 \n\t"
++ " lsl r3, #24 \n\t" //T flag
++ " tst r1, r3 \n\t" //check for T bit
++ " bne not_arm_mode \n\t" //if it is set, further testing to be done here
++ "is_arm_mode: \n\t"
++ " movs r1, #" STR(EXC_m0_CAUSE_BAD_CPU_MODE) " \n\t"
++ " b call_handler_no_pushed_regs \n\t"
++
++ //check if re-entry
++ "not_arm_mode: \n\t"
++ " ldr r2, [r0, #4 * 7] \n\t"
++ " lsl r2, #32 - 6 \n\t"
++ " lsr r2, #32 - 6 \n\t"
++ " cmp r2, #4 \n\t"
++ " bne not_reentry \n\t"
++
++ //is re-entry from our own code only (should only be memory access issues)
++ //our memory accesses are crafted specially and we detect that
++ //we also assume here that Pc in our exclusive mode is valid
++ " ldr r3, [r0, #4 * 6] \n\t" //exc.PC
++ " ldrh r1, [r3, #2] \n\t" //should be a "B . +4" = 0xe000
++ " mov r2, #0xe0 \n\t"
++ " lsl r2, #8 \n\t"
++ " cmp r1, r2 \n\t"
++ " bne bug_in_classifier \n\t"
++ " ldrh r2, [r3, #4] \n\t" //should be a "MOV R?, #??" = 0x46f6
++ " lsr r2, #11 \n\t"
++ " cmp r2, #0x04 \n\t"
++ " bne bug_in_classifier \n\t"
++
++ //re-entry from our own code - skip the load/store and the next instruction
++ " add r3, #4 \n\t"
++ " str r3, [r0, #4 * 6] \n\t" //exc.PC
++ " bx lr \n\t"
++
++ //re-entry from our code but not a specially-instrumented load/store instr
++ "bug_in_classifier: \n\t"
++ " mov r1, #" STR(EXC_m0_CAUSE_CLASSIFIER_ERROR) " \n\t"
++ " b call_handler_no_pushed_regs \n\t"
++
++ "access_fail_plus_4: \n\t"
++ " add r2, #2 \n\t"
++ "access_fail_plus_2: \n\t"
++ " add r2, #2 \n\t"
++ "access_fail: \n\t" //expects address in r2
++ " mov r1, #" STR(EXC_m0_CAUSE_MEM_READ_ACCESS_FAIL) "\n\t"
++ "call_handler_no_pushed_regs: \n\t"
++ " push {r4-r7, lr} \n\t"
++ " mov r4, r8 \n\t"
++ " mov r5, r9 \n\t"
++ " mov r6, r10 \n\t"
++ " mov r7, r11 \n\t"
++ " push {r4-r7} \n\t"
++ " mov r3, sp \n\t"
++ " bl faultHandlerWithExcFrame \n\t"
++ " pop {r4-r7} \n\t"
++ " mov r8, r4 \n\t"
++ " mov r9, r5 \n\t"
++ " mov r10, r6 \n\t"
++ " mov r11, r7 \n\t"
++ " pop {r4-r7, pc} \n\t"
++
++ //not re-entry, r3 still has T bit
++ "not_reentry: \n\t"
++ //further checks will take place in another context - go there now
++ " ldr r2, [r0, #4 * 6] \n\t" //exc.PC
++ " adr r1, check_more_in_custom_mode \n\t" //stashed PC
++ " add r3, r3, #4 \n\t" //desired SR
++ " push {r1, r3} \n\t"
++ " mov r1, r12 \n\t" //stashed r12
++ " push {r1, lr} \n\t" //and stashed lr
++ " push {r0-r3} \n\t" //stashed r0..r3
++ " mov r2, #0x0e \n\t"
++ " mvn r2, r2 \n\t" //get an lr to go to (handler mode, main stack) -> 0xfffffff1
++ " bx r2 \n\t"
++
++ //check more in a safer space (r0 = exc; r2 = exc->pc; lr & sp set for direct return to original exc cause)
++
++ ".balign 4 \n\t"
++ "check_more_in_custom_mode: \n\t"
++ " cmp r0, r0 \n\t" //set Z
++ " ldrh r1, [r2] \n\t" //load instr, on failure, branch is skipped too
++ " b 1f \n\t"
++ " mov r1, #1 \n\t" //clear Z. only executed on access failure
++ "1: \n\t" //Z flag is clear on failure, due to the mov above, which is skipped on a succesful read
++ " bne access_fail \n\t"
++ " lsr r3, r1, #11 \n\t"
++ " cmp r3, #0x1C \n\t"
++ " bls instr_is_16bits_long \n\t"
++
++ //let's read the second half of a 32-bit instr
++ "instr_is_32_bits_long: \n\t"
++ " mov r12, r2 \n\t" //save exc.pc
++ " cmp r0, r0 \n\t" //set Z
++ " ldrh r2, [r2, #2] \n\t" //load instr part 2, on failure, branch is skipped too
++ " b 1f \n\t"
++ " mov r1, #1 \n\t" //clear Z. only executed on access failure
++ "1: \n\t" //Z flag is clear on failure, due to the mov above, which is skipped on a succesful read
++ " bne access_fail_plus_2 \n\t"
++
++ //32-bit instr. C-M0 has none that are valid AND can cause an exception, so this is definitely an UNDEF INSTR case
++ " mov r1, #" STR(EXC_m0_CAUSE_UNDEFINSTR32) " \n\t"
++ " b call_handler_no_pushed_regs \n\t"
++
++ //it was a 16 bit instr. instr is in r1, pc is in r2
++ "instr_is_16bits_long: \n\t"
++ //more triage will be done in C (r0 = excFrame, r1 = instr16, r2 = pc)
++ " push {r4-r7, lr} \n\t"
++ " mov r4, r8 \n\t"
++ " mov r5, r9 \n\t"
++ " mov r6, r10 \n\t"
++ " mov r7, r11 \n\t"
++ " push {r4-r7} \n\t"
++ " mov r2, sp \n\t"
++ " bl analyzeInstr16 \n\t"
++ " pop {r4-r7} \n\t"
++ " mov r8, r4 \n\t"
++ " mov r9, r5 \n\t"
++ " mov r10, r6 \n\t"
++ " mov r11, r7 \n\t"
++ " pop {r4-r7, pc} \n\t"
++ ".ltorg \n\t"
++ :
++ :
++ : "cc", "memory", "r0", "r1", "r2", "r3", "r12" //yes gcc needs this list...
++ );
++}
++
++
++
++
++
+diff --git a/watch-library/shared/watch/m0FaultDispatch.h b/watch-library/shared/watch/m0FaultDispatch.h
+new file mode 100644
+index 0000000..207f5d4
+--- /dev/null
++++ b/watch-library/shared/watch/m0FaultDispatch.h
+@@ -0,0 +1,38 @@
++#ifndef _M0_FAULT_DISPATCH_
++#define _M0_FAULT_DISPATCH_
++
++#include <stdint.h>
++
++
++#define EXC_m0_CAUSE_MEM_READ_ACCESS_FAIL 1 //address provided
++#define EXC_m0_CAUSE_MEM_WRITE_ACCESS_FAIL 2 //address provided
++#define EXC_m0_CAUSE_BAD_CPU_MODE 3 //arm mode entered
++#define EXC_m0_CAUSE_DATA_UNALIGNED 4 //addres provided
++#define EXC_m0_CAUSE_UNDEFINSTR16 5
++#define EXC_m0_CAUSE_UNDEFINSTR32 6
++#define EXC_m0_CAUSE_BKPT_HIT 7
++#define EXC_m0_CAUSE_CLASSIFIER_ERROR 8 //classifier itself crashed
++#define EXC_m0_CAUSE_UNCLASSIFIABLE 9 //classification failed
++
++struct CortexExcFrame {
++ union {
++ struct {
++ uint32_t r0, r1, r2, r3;
++ };
++ uint32_t r0_r3[3];
++ };
++ uint32_t r12, lr, pc, sr;
++};
++
++struct CortexPushedRegs { //when we push regs, we push them in this order. here for unification
++ uint32_t regs8_11[4];
++ uint32_t regs4_7[4];
++};
++
++//fault handling code, cause is EXC_m0_CAUSE_, extraData is usually an address. both unused on C-M3
++void faultHandlerWithExcFrame(struct CortexExcFrame *exc, uint32_t cause, uint32_t extraData, struct CortexPushedRegs *pushedRegs);
++
++
++
++
++#endif
diff --git a/main/fix-serial.patch b/main/fix-serial.patch
index 80b11c5..4c06ffa 100644
--- a/main/fix-serial.patch
+++ b/main/fix-serial.patch
@@ -1,97 +1,64 @@
diff --git a/movement/filesystem.c b/movement/filesystem.c
-index 97e3545..d3c2f83 100644
+index 9df0a8d..ac117af 100644
--- a/movement/filesystem.c
+++ b/movement/filesystem.c
-@@ -275,6 +275,10 @@ void filesystem_process_command(char *line) {
- filesystem_append_file(filename, "\n", 1);
- }
- free(text);
-+ } else if (strcmp(command, "format") == 0) {
-+ lfs_unmount(&lfs);
-+ lfs_format(&lfs, &cfg);
-+ lfs_mount(&lfs, &cfg);
- } else {
- printf("%s: command not found\n", command);
- }
-diff --git a/movement/movement.c b/movement/movement.c
-index 825f130..c7066f7 100644
---- a/movement/movement.c
-+++ b/movement/movement.c
-@@ -547,7 +547,29 @@ bool app_loop(void) {
- tx = "";
- });
- #else
-- read(0, line, 256);
-+#if 0
-+ read(0, &line[ll], sizeof(line)-ll);
-+#else
-+ // JMM yuck just yuck, we do our best to patch this mess up
-+ {
-+ static char buf[sizeof(line) - 1];
-+ static int bl; //again really signed?
-+ int red;
+@@ -280,3 +280,13 @@ int filesystem_cmd_echo(int argc, char *argv[]) {
+ return 0;
+ }
+
++int filesystem_cmd_format(int argc, char *argv[]) {
++ (void) argc;
++ (void) argv;
+
-+ red = read(0, &buf[bl], sizeof(buf) - 1);
++ lfs_unmount(&lfs);
++ lfs_format(&lfs, &cfg);
++ lfs_mount(&lfs, &cfg);
+
-+ if (red > 0) {
-+ write(0, &buf[bl], red);
-+ bl += red;
-+ if (buf[bl-1] == '\r') {
-+ for (red = 0; red < bl; ++red)
-+ line[red] = buf[red] == '\r' ? '\n' : buf[red];
-+ //memcpy(line,buf,bl-1);
-+ bl = 0;
-+ }
-+ }
-+ }
-+#endif
- #endif
- if (strlen(line)) filesystem_process_command(line);
- }
-diff --git a/watch-library/hardware/watch/watch_private.c b/watch-library/hardware/watch/watch_private.c
-index cd607b8..5081fdb 100644
---- a/watch-library/hardware/watch/watch_private.c
-+++ b/watch-library/hardware/watch/watch_private.c
-@@ -246,9 +246,18 @@ void _watch_enable_usb(void) {
- // this function ends up getting called by printf to log stuff to the USB console.
- int _write(int file, char *ptr, int len) {
- (void)file;
-+ int i; //it's 2023 boys and girls you can use size_t and ssize_t
- if (hri_usbdevice_get_CTRLA_ENABLE_bit(USB)) {
-- tud_cdc_n_write(0, (void const*)ptr, len);
-- tud_cdc_n_write_flush(0);
-+ //tud_cdc_n_write(0, (void const*)ptr, len);
-+ for (i = 0; i < len; ++i)
-+ {
-+ if (ptr[i]=='\n') {
-+ tud_cdc_n_write(0, (void const*)"\r\n", 2);
-+ } else {
-+ tud_cdc_n_write(0, (void const*)&ptr[i], 1);
-+ }
-+ tud_cdc_n_write_flush(0);
-+ }
- return len;
- }
++ return 0;
++}
+diff --git a/movement/filesystem.h b/movement/filesystem.h
+index fa3d9d1..ee13cba 100644
+--- a/movement/filesystem.h
++++ b/movement/filesystem.h
+@@ -101,5 +101,6 @@ int filesystem_cmd_cat(int argc, char *argv[]);
+ int filesystem_cmd_df(int argc, char *argv[]);
+ int filesystem_cmd_rm(int argc, char *argv[]);
+ int filesystem_cmd_echo(int argc, char *argv[]);
++int filesystem_cmd_format(int argc, char *argv[]);
-@@ -262,6 +271,8 @@ int _read(int file, char *ptr, int len) {
- int actual_length = strlen(buf);
- if (actual_length) {
- memcpy(ptr, buf, min(len, actual_length));
-+ //JMM yuckity yuck yuck
-+ buf[0] = 0;
- return actual_length;
- }
- return 0;
-diff --git a/watch-library/shared/watch/watch.h b/watch-library/shared/watch/watch.h
-index 790f9a1..b280ae6 100644
---- a/watch-library/shared/watch/watch.h
-+++ b/watch-library/shared/watch/watch.h
-@@ -95,5 +95,6 @@ void watch_reset_to_bootloader(void);
- * @return The number of bytes read, or zero if no bytes were read.
- */
- int read(int file, char *ptr, int len);
-+int write(int file, char *ptr, int len);
-
--#endif /* WATCH_H_ */
-\ No newline at end of file
-+#endif /* WATCH_H_ */
+ #endif // FILESYSTEM_H_
+diff --git a/movement/shell_cmd_list.c b/movement/shell_cmd_list.c
+index 0ea08a5..a5d2d01 100644
+--- a/movement/shell_cmd_list.c
++++ b/movement/shell_cmd_list.c
+@@ -92,6 +92,13 @@ shell_command_t g_shell_commands[] = {
+ .max_args = 3,
+ .cb = filesystem_cmd_echo,
+ },
++ {
++ .name = "format",
++ .help = "usage: format",
++ .min_args = 0,
++ .max_args = 0,
++ .cb = filesystem_cmd_format,
++ },
+ {
+ .name = "stress",
+ .help = "test CDC write; usage: stress [LEN] [DELAY_MS]",
+diff --git a/watch-library/hardware/watch/watch_private_cdc.c b/watch-library/hardware/watch/watch_private_cdc.c
+index a961b5e..31e354d 100644
+--- a/watch-library/hardware/watch/watch_private_cdc.c
++++ b/watch-library/hardware/watch/watch_private_cdc.c
+@@ -145,7 +145,11 @@ static void prv_handle_writes(void) {
+ prv_handle_reads();
+ }
+ if (tud_cdc_write_available()) {
+- tud_cdc_write(&s_write_buf[idx], 1);
++ if (s_write_buf[idx]=='\n') {
++ tud_cdc_write("\r\n", 2);
++ } else {
++ tud_cdc_write(&s_write_buf[idx], 1);
++ }
+ }
+ s_write_buf[idx] = 0;
+ s_write_buf_len--;
diff --git a/main/metric.patch b/main/metric.patch
index cbf0287..0ff3dc7 100644
--- a/main/metric.patch
+++ b/main/metric.patch
@@ -11,394 +11,26 @@ index 878b4a4..5c2cee7 100644
moon_phase_face,
sunrise_sunset_face,
diff --git a/movement/make/Makefile b/movement/make/Makefile
-index db08929..e131bbb 100644
+index 576822c..cf8850a 100644
--- a/movement/make/Makefile
+++ b/movement/make/Makefile
-@@ -120,6 +120,7 @@ SRCS += \
- ../watch_faces/complication/flashlight_face.c \
- ../watch_faces/clock/decimal_time_face.c \
- ../watch_faces/clock/wyoscan_face.c \
+@@ -131,6 +131,7 @@ SRCS += \
+ ../watch_faces/clock/minute_repeater_decimal_face.c \
+ ../watch_faces/complication/tuning_tones_face.c \
+ ../watch_faces/complication/kitchen_conversions_face.c \
+ ../watch_faces/complication/metric_face.c \
# New watch faces go above this line.
# Leave this line at the bottom of the file; it has all the targets for making your project.
diff --git a/movement/movement_faces.h b/movement/movement_faces.h
-index ff34c06..94316ce 100644
+index 3557110..17fe809 100644
--- a/movement/movement_faces.h
+++ b/movement/movement_faces.h
-@@ -95,6 +95,7 @@
- #include "flashlight_face.h"
- #include "decimal_time_face.h"
- #include "wyoscan_face.h"
+@@ -104,6 +104,7 @@
+ #include "minute_repeater_decimal_face.h"
+ #include "tuning_tones_face.h"
+ #include "kitchen_conversions_face.h"
+#include "metric_face.h"
// New includes go above this line.
#endif // MOVEMENT_FACES_H_
-diff --git a/movement/watch_faces/complication/equinox.c b/movement/watch_faces/complication/equinox.c
-new file mode 100644
-index 0000000..e8d862e
---- /dev/null
-+++ b/movement/watch_faces/complication/equinox.c
-@@ -0,0 +1,101 @@
-+#define D2R(a) ((a)*(M_PI/180.))
-+
-+
-+
-+
-+// shamelessly stolen from Meeus Astronmical Algorithms Chapter 27 table 27.B
-+// chapter 25 is probably more tractable, but this is easier to code up
-+
-+# if 0
-+static double mean_vernal_equinox (unsigned year)
-+{
-+ double y = year;
-+ y -= 2000;
-+ y *= 0.001;
-+ return 2451623.80984 + 365242.37404 * y + 0.05169 * (y * y) - 0.00411 * (y * y * y) - 0.00057 * (y * y * y * y);
-+}
-+
-+static double mean_summer_solstice (unsigned year)
-+{
-+ double y = year;
-+ y -= 2000;
-+ y *= 0.001;
-+ return 2451716.56767 + 365241.62603 * y + 0.00325 * (y * y) + 0.00888 * (y * y * y) - 0.00030 * (y * y * y * y);
-+}
-+#endif
-+
-+static double mean_autumnal_equinox (unsigned year)
-+{
-+ double y = year;
-+ y -= 2000;
-+ y *= 0.001;
-+ return 2451810.21715 + 365242.01767 * y - 0.11575 * (y * y) + 0.00337 * (y * y * y) + 0.00078 * (y * y * y * y);
-+}
-+
-+#if 0
-+static double mean_winter_solstice (unsigned year)
-+{
-+ double y = year;
-+ y -= 2000;
-+ y *= 0.001;
-+ return 2451900.05952 + 365242.74049 * y - 0.06223 * (y * y) - 0.00823 * (y * y * y) + 0.00032 * (y * y * y * y);
-+}
-+#endif
-+
-+static double orbital_periodic_terms (double t)
-+{
-+#define N 24
-+ const double A[N] = {485, 203, 199, 182, 156, 136, 77, 74, 70, 58, 52, 50, 45,
-+ 44, 29, 18, 17, 16, 14, 12, 12, 12, 9, 8
-+ };
-+ const double B[N] = {D2R (324.96), D2R (337.23), D2R (342.08), D2R (27.85),
-+ D2R (73.14), D2R (171.52), D2R (222.54), D2R (296.72),
-+ D2R (243.58), D2R (119.81), D2R (297.17), D2R (21.02),
-+ D2R (247.54), D2R (325.15), D2R (60.93), D2R (155.12),
-+ D2R (288.79), D2R (198.04), D2R (199.76), D2R (95.39),
-+ D2R (287.11), D2R (320.81), D2R (227.73), D2R (15.45)
-+ };
-+ const double C[N] = {D2R (1934.136), D2R (32964.467), D2R (20.186),
-+ D2R (445267.112), D2R (45036.886), D2R (22518.443),
-+ D2R (65928.934), D2R (3034.906), D2R (9037.513),
-+ D2R (33718.147), D2R (150.678), D2R (2281.226),
-+ D2R (29929.562), D2R (31555.956), D2R (4443.417),
-+ D2R (67555.328), D2R (4562.452), D2R (62894.029),
-+ D2R (31436.921), D2R (14577.848), D2R (31931.756),
-+ D2R (34777.259), D2R (1222.114), D2R (16859.074)
-+ };
-+ double s = 0;
-+ unsigned i;
-+
-+ for (i = 0; i < N; ++i)
-+ s += A[i] * cos (B[i] + (C[i] * t));
-+
-+ return s;
-+}
-+
-+
-+static double mean_to_real (double j0)
-+{
-+
-+ double t = (j0 - 2451545.) / 36525.;
-+ double w = D2R ((35999.373 * t) - 2.47);
-+ double dl = 1 + 0.0334 * cos (w) + 0.0007 * cos (2. * w);
-+ double s = orbital_periodic_terms (t);
-+
-+#if 0
-+ printf ("j0=%.6f\r\n", j0);
-+ printf ("t=%.6f\r\n", t);
-+ printf ("w=%.6f\r\n", w);
-+ printf ("dl=%.6f\r\n", dl);
-+ printf ("s=%.6f\r\n", s);
-+#endif
-+
-+ return j0 + ((0.00001 * s) / dl);
-+}
-+
-+static double autumnal_equinox (unsigned y)
-+{
-+ return mean_to_real (mean_autumnal_equinox (y));
-+ // return mean_to_real (mean_summer_solstice (y));
-+}
-+
-diff --git a/movement/watch_faces/complication/metric_face.c b/movement/watch_faces/complication/metric_face.c
-new file mode 100644
-index 0000000..9eaafbf
---- /dev/null
-+++ b/movement/watch_faces/complication/metric_face.c
-@@ -0,0 +1,198 @@
-+#include <stdlib.h>
-+#include <string.h>
-+#include <math.h>
-+#include "watch.h"
-+#include "watch_utility.h"
-+#include "vsop87a_micro.h" // smaller size, less accurate
-+#include "vsop87a_milli.h"
-+#include "astrolib.h"
-+#include "metric_face.h"
-+
-+#define RATIO 8
-+
-+#include "equinox.c"
-+
-+static const char *days[10] =
-+ { "Pr", "Du", "Tr", "Qa", "Qi", "SH", "Sp", "Oc", "No", "De" };
-+
-+
-+static uint32_t
-+jd_to_timestamp (double tjd)
-+{
-+ tjd -= 2440587.5;
-+ tjd *= 86400.;
-+ return (uint32_t) floor (tjd);
-+}
-+
-+static uint32_t
-+timestamp_start_of_year (int y)
-+{
-+ double julian_equinox = autumnal_equinox (y);
-+ julian_equinox += (2.3372305555 / 360); /* Offset of paris meridian from greenwich meridian */
-+ return jd_to_timestamp (julian_equinox);
-+}
-+
-+static int
-+days_since_equinox (watch_date_time date_time)
-+{
-+ uint32_t now = watch_utility_date_time_to_unix_time (date_time, 0);
-+ uint32_t start_of_year =
-+ timestamp_start_of_year (date_time.unit.year + 2020);
-+
-+ start_of_year/=86400;
-+ start_of_year*=86400;
-+
-+ if (start_of_year > now)
-+ start_of_year =
-+ timestamp_start_of_year (date_time.unit.year + 2020 - 1);
-+
-+ now -= start_of_year;
-+
-+ now /= 86400;
-+
-+ return (int) now;
-+}
-+
-+static void
-+calculate_metric_date (metric_state_t * state, watch_date_time date_time)
-+{
-+ int dse = days_since_equinox (date_time);
-+
-+ state->m_day = (dse % 30) + 1;
-+ state->m_wday = (dse % 10);
-+ state->c_day = date_time.unit.day;
-+
-+}
-+
-+
-+static uint32_t
-+date_time_to_ds (watch_date_time dt)
-+{
-+ uint32_t ret;
-+ ret = dt.unit.hour;
-+ ret *= 60;
-+ ret += dt.unit.minute;
-+ ret *= 60;
-+ ret += dt.unit.second;
-+ return ret;
-+}
-+
-+static void
-+_metric_face_sync (movement_settings_t * settings, metric_state_t * state)
-+{
-+ watch_date_time date_time = watch_rtc_get_date_time ();
-+
-+ (void) settings;
-+
-+ if (date_time.unit.day != state->c_day)
-+ calculate_metric_date (state, date_time);
-+
-+ state->dt = date_time_to_ds (date_time) * RATIO;
-+ state->resync_at = state->dt * (3600 * RATIO);
-+ if (state->resync_at > (86400 * RATIO))
-+ state->resync_at = (86400 * RATIO);
-+
-+}
-+
-+static void
-+_metric_face_update (movement_event_t event, movement_settings_t * settings,
-+ metric_state_t * state, int show_secs)
-+{
-+ uint64_t v;
-+ char buf[14];
-+
-+ (void) event;
-+
-+ if (state->dt >= state->resync_at)
-+ _metric_face_sync (settings, state);
-+
-+ v = state->dt;
-+ v *= (uint64_t) 100000;
-+ v /= (uint64_t) (RATIO * 86400);
-+
-+
-+#if 0
-+ {
-+ watch_date_time date_time = watch_rtc_get_date_time ();
-+ printf ("%d %d\n", state->dt, date_time_to_ds (date_time) * RATIO);
-+ }
-+#endif
-+
-+
-+ sprintf (buf, "%s%2d %05d", days[state->m_wday], (int) state->m_day,
-+ (int) v);
-+
-+ if (!show_secs)
-+ {
-+ buf[8] = ' ';
-+ buf[9] = ' ';
-+ }
-+
-+ watch_display_string (buf, 0);
-+}
-+
-+void
-+metric_face_setup (movement_settings_t * settings, uint8_t watch_face_index,
-+ void **context_ptr)
-+{
-+ (void) settings;
-+ (void) watch_face_index;
-+ if (*context_ptr == NULL)
-+ {
-+ *context_ptr = malloc (sizeof (metric_state_t));
-+ memset (*context_ptr, 0, sizeof (metric_state_t));
-+ }
-+}
-+
-+void
-+metric_face_activate (movement_settings_t * settings, void *context)
-+{
-+ metric_state_t *state = (metric_state_t *) context;
-+ (void) settings;
-+ movement_request_tick_frequency (RATIO);
-+ state->resync_at = 0;
-+}
-+
-+bool
-+metric_face_loop (movement_event_t event, movement_settings_t * settings,
-+ void *context)
-+{
-+ (void) settings;
-+ metric_state_t *state = (metric_state_t *) context;
-+
-+ switch (event.event_type)
-+ {
-+ case EVENT_ACTIVATE:
-+ watch_set_colon ();
-+ /*fall through */
-+ case EVENT_TICK:
-+ state->dt++;
-+ _metric_face_update (event, settings, state, 1);
-+ break;
-+ case EVENT_LOW_ENERGY_UPDATE:
-+ state->dt += RATIO * 60;
-+ _metric_face_update (event, settings, state, 0);
-+ break;
-+ case EVENT_ALARM_BUTTON_UP:
-+ break;
-+ case EVENT_ALARM_LONG_PRESS:
-+ break;
-+ case EVENT_TIMEOUT:
-+ state->resync_at = 0;
-+ break;
-+ default:
-+ movement_default_loop_handler (event, settings);
-+ break;
-+ }
-+
-+ return true;
-+}
-+
-+void
-+metric_face_resign (movement_settings_t * settings, void *context)
-+{
-+ (void) settings;
-+ metric_state_t *state = (metric_state_t *) context;
-+ state->resync_at = 0;
-+ state->c_day = 0;
-+}
-diff --git a/movement/watch_faces/complication/metric_face.h b/movement/watch_faces/complication/metric_face.h
-new file mode 100644
-index 0000000..1968e38
---- /dev/null
-+++ b/movement/watch_faces/complication/metric_face.h
-@@ -0,0 +1,51 @@
-+/*
-+ * MIT License
-+ *
-+ * Copyright (c) 2022 Joey Castillo
-+ *
-+ * Permission is hereby granted, free of charge, to any person obtaining a copy
-+ * of this software and associated documentation files (the "Software"), to deal
-+ * in the Software without restriction, including without limitation the rights
-+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-+ * copies of the Software, and to permit persons to whom the Software is
-+ * furnished to do so, subject to the following conditions:
-+ *
-+ * The above copyright notice and this permission notice shall be included in all
-+ * copies or substantial portions of the Software.
-+ *
-+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-+ * SOFTWARE.
-+ */
-+
-+#ifndef METRIC_FACE_H_
-+#define METRIC_FACE_H_
-+
-+#include "movement.h"
-+
-+typedef struct {
-+ uint32_t dt;
-+ uint32_t resync_at;
-+ uint32_t m_day;
-+ uint32_t m_wday;
-+ uint32_t c_day;
-+} metric_state_t;
-+
-+void metric_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr);
-+void metric_face_activate(movement_settings_t *settings, void *context);
-+bool metric_face_loop(movement_event_t event, movement_settings_t *settings, void *context);
-+void metric_face_resign(movement_settings_t *settings, void *context);
-+
-+#define metric_face ((const watch_face_t){ \
-+ metric_face_setup, \
-+ metric_face_activate, \
-+ metric_face_loop, \
-+ metric_face_resign, \
-+ NULL, \
-+})
-+
-+#endif // METRIC_FACE_H_
diff --git a/main/series b/main/series
index e0d931c..aa3c0cd 100644
--- a/main/series
+++ b/main/series
@@ -5,4 +5,7 @@ fix-serial.patch
metric.patch
siderial.patch
moon.patch
+debug-compile.patch
+set-time.patch
+dont_set_time_if_no_debugger.patch
endstop
diff --git a/main/set-time.patch b/main/set-time.patch
new file mode 100644
index 0000000..c7d479c
--- /dev/null
+++ b/main/set-time.patch
@@ -0,0 +1,83 @@
+diff --git a/make.mk b/make.mk
+index ee40100..5610c1f 100644
+--- a/make.mk
++++ b/make.mk
+@@ -64,7 +64,7 @@ LDFLAGS += -mcpu=cortex-m0plus -mthumb
+ LDFLAGS += -Wl,--gc-sections
+ LDFLAGS += -Wl,--script=$(TOP)/watch-library/hardware/linker/saml22j18.ld
+ LDFLAGS += -Wl,--print-memory-usage
+-LDFLAGS += -g
++LDFLAGS += -g -u set_time
+
+ LIBS += -lm
+
+diff --git a/movement/make/Makefile b/movement/make/Makefile
+index a336b85..5fa1322 100644
+--- a/movement/make/Makefile
++++ b/movement/make/Makefile
+@@ -51,6 +51,7 @@ SRCS += \
+ ../../littlefs/lfs.c \
+ ../../littlefs/lfs_util.c \
+ ../movement.c \
++ ../set_time.c \
+ ../filesystem.c \
+ ../shell.c \
+ ../shell_cmd_list.c \
+diff --git a/movement/movement.c b/movement/movement.c
+index 230dbe5..8abd6e7 100644
+--- a/movement/movement.c
++++ b/movement/movement.c
+@@ -412,6 +412,7 @@ void app_wake_from_backup(void) {
+
+ void app_setup(void) {
+ watch_store_backup_data(movement_state.settings.reg, 0);
++ set_time();
+
+ static bool is_first_launch = true;
+
+diff --git a/movement/set_time.c b/movement/set_time.c
+new file mode 100644
+index 0000000..27cea25
+--- /dev/null
++++ b/movement/set_time.c
+@@ -0,0 +1,40 @@
++#include <stdio.h>
++#include <string.h>
++#include <limits.h>
++#include <string.h>
++#include <stdlib.h>
++#include <stdio.h>
++#include "watch.h"
++#include "movement.h"
++#include "watch_utility.h"
++
++#define SYS_TIME 0x11
++
++extern movement_state_t movement_state;
++
++static inline int32_t
++get_tz_offset (movement_settings_t * settings)
++{
++ return movement_timezone_offsets[settings->bit.time_zone] * 60;
++}
++
++static int32_t
++sys_time (void)
++{
++ int32_t value = SYS_TIME;
++ asm ("mov r0,%0; bkpt $0xab; mov %0,r0": "+r" (value): :"r0");
++ return value;
++}
++
++void __attribute__((used)) set_time (void)
++{
++ int32_t t;
++
++ t = sys_time ();
++
++ watch_date_time wdt = watch_utility_date_time_from_unix_time (t,
++ get_tz_offset
++ (&movement_state.
++ settings));
++ watch_rtc_set_date_time (wdt);
++}