]> bicyclesonthemoon.info Git - klavirko/ui/commitdiff
Final version as of 23.1.2022 main v1.0
authorb <rowerynaksiezycu@gmail.com>
Sun, 23 Jan 2022 20:58:00 +0000 (21:58 +0100)
committerb <rowerynaksiezycu@gmail.com>
Sun, 23 Jan 2022 20:58:00 +0000 (21:58 +0100)
35 files changed:
.gitignore [new file with mode: 0644]
LinkerSubCommand.tmp [new file with mode: 0644]
aix.c [new file with mode: 0644]
aix.h [new file with mode: 0644]
ctrl.c [new file with mode: 0644]
ctrl.h [new file with mode: 0644]
debug.c [new file with mode: 0644]
debug.h [new file with mode: 0644]
font16.png [new file with mode: 0644]
font16.xcf [new file with mode: 0644]
font8.png [new file with mode: 0644]
font8.xcf [new file with mode: 0644]
fs.c [new file with mode: 0644]
fs.h [new file with mode: 0644]
gui.c [new file with mode: 0644]
gui.h [new file with mode: 0644]
img2fnt.c [new file with mode: 0644]
interrupt_handlers.c [new file with mode: 0644]
interrupt_handlers.h [new file with mode: 0644]
iodefine.h [new file with mode: 0644]
klavirko-ui.h [new file with mode: 0644]
lcd.c [new file with mode: 0644]
lcd.h [new file with mode: 0644]
license.txt [new file with mode: 0644]
main.c [new file with mode: 0644]
main.h [new file with mode: 0644]
makefile [new file with mode: 0644]
makefile_orig [new file with mode: 0644]
memory_template.ld [new file with mode: 0644]
metalinker.pl [new file with mode: 0755]
reset_program.asm [new file with mode: 0644]
sinus.c [new file with mode: 0644]
vector_table.c [new file with mode: 0644]
wave.c [new file with mode: 0644]
wave.h [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..8a72c9e
--- /dev/null
@@ -0,0 +1,12 @@
+*.o
+*.lst
+*.hex
+*.mot
+*.x
+*.map
+img2fnt
+font.h
+sinus
+sinus.h
+memory.ld
+size.tmp
diff --git a/LinkerSubCommand.tmp b/LinkerSubCommand.tmp
new file mode 100644 (file)
index 0000000..0fb6e33
--- /dev/null
@@ -0,0 +1 @@
+"reset_program.o" "interrupt_handlers.o" "vector_table.o" "aix.o" "main.o" "lcd.o" "gui.o" "wave.o" "ctrl.o" "fs.o" "debug.o"
\ No newline at end of file
diff --git a/aix.c b/aix.c
new file mode 100644 (file)
index 0000000..c2e9542
--- /dev/null
+++ b/aix.c
@@ -0,0 +1,261 @@
+/* ANALOG INPUT EXPANDER */
+/*
+Copyright 2021 Balthasar Szczepański
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include "klavirko-ui.h"
+#include "aix.h"
+#include "debug.h"
+#include "wave.h"
+#include "gui.h"
+
+         uint16_t aix_data[N_AIX_DATA] = AIX_DATA_INIT;
+volatile uint8_t  aix_buffer[N_AIX_BUFFER];
+volatile uint8_t  aix_buffer_r = 0;
+volatile uint8_t  aix_buffer_w = 0;
+         uint8_t  aix_check;       // frame checksum
+         uint8_t  aix_i = 0;       // frame byte index
+         uint8_t  aix_sel = 0;     // setting index
+         uint8_t  aix_sw_old = 0;  // old switch state
+         uint8_t  aix_sw_new = 0;  // new switch state
+         uint8_t  aix_sw_count = 0;// switch state debouncer
+         uint16_t aix_val = 0;     // setting value
+volatile uint16_t aix_timeout = 0; // timeout for no AIX communication
+
+
+inline void aix_watchdog (void) // monitor if AIX communication still working
+{
+       if (aix_timeout < AIX_TIMEOUT)
+       {
+               ++aix_timeout;
+               if(aix_timeout == AIX_TIMEOUT)
+               {
+                       debug_string("AIX TIMEOUT!\r\n", 1);
+                       gui_trigger_error(GUI_ERRF_AIX);
+               }
+       }
+}
+
+inline void int_aix_rx (void) // received byte from AIX
+{
+       uint8_t data;
+       
+       data = AIX_RX__SDR; // get byte
+       
+       if((aix_buffer_w + 1 == aix_buffer_r) || (aix_buffer_w + 1 == aix_buffer_r + N_AIX_BUFFER)) // buffer overflow
+               {}
+       else
+       {
+               aix_buffer[aix_buffer_w] = data;
+               // advance the pointer
+               if (aix_buffer_w >= N_AIX_BUFFER - 1)
+                       aix_buffer_w = 0;
+               else
+                       ++aix_buffer_w;
+       }
+}
+
+inline void int_aix_err (void) // received error from AIX
+{
+       volatile uint8_t data; //just to make sure a read is performed
+       uint16_t err;
+       
+       // not interested which error occurred and why
+       // just clear and continue
+       // frames have checksums anyway
+       
+       data = AIX_RX__SDR;
+       err = AIX_RX__SSR;
+       AIX_RX__SIR = err;
+}
+
+inline void setup_aix (void)
+{
+       PER0 |= 0b00000100; //enable serial array unit 0
+       
+       SPS0 = (SPS0 & 0xfff0) | 0x0001; //CK00 = fclk/2 = 16MHz
+       
+       AIX_TX__SMR  = 0b0000000000100011; //CK00, int. on buf empty
+       AIX_RX__SMR  = 0b0000000100100010; //CK00, ...
+       
+       AIX_TX__SCR  = 0b1000000010010111; //8n1, LSB
+       AIX_RX__SCR  = 0b0100010010010111;
+       
+       AIX_TX__SDR  = 0x8c00l; //16MHz / 138 = 115942.029bps
+       AIX_RX__SDR  = 0x8c00l;
+       
+       SOL0 &= ~0x0001;
+       SO0 |= 0b0000001100000011;
+       
+       AIX_TX__SOE = 1;
+       
+       AIX_TX__PM  = 0;
+       AIX_TX__POM = 0;
+       AIX_TX__PU  = 0; 
+       AIX_TX      = 1;
+       
+       AIX_RX__POM = 0;
+       AIX_RX__PIM = 0;
+       AIX_RX__PM  = 1;
+       AIX_RX__PU  = 0;
+       AIX_RX      = 1;
+       
+       AIX_TX__SS = 1;
+       AIX_RX__SS = 1;
+       
+       AIX_RX__MK = 0;
+       AIX_RX__MKE = 0;
+}
+
+void handle_aix (void)
+{
+       uint8_t data;
+       int16_t diff;
+       
+       if (aix_buffer_r != aix_buffer_w) // data waiting in buffer
+       {
+               // get data; advance pointer
+               data = aix_buffer[aix_buffer_r];
+               if(aix_buffer_r >= N_AIX_BUFFER - 1)
+                       aix_buffer_r = 0;
+               else
+                       ++aix_buffer_r;
+               
+               if (data == '\n') //end of frame
+               {
+                       if( //frame is valid:
+                               aix_check == 0 && // correct checksum
+                               aix_i != 0 && // frame not empty
+                               (((1<<aix_sel) & AIX_UNUSED)==0) && // not for an unused setting
+                               aix_val <= AIX_DATA_MAX // value in range
+                       ){
+                               aix_timeout = 0; // clear the 'watchdog'
+                               
+                               // has value changed enough?
+                               diff = (int16_t)(aix_val - aix_data[aix_sel]);
+                               if ((diff >= AIX_MIN_DIFF) || (diff <= 0 - AIX_MIN_DIFF))
+                               {
+                                       if (aix_data[aix_sel] == AIX_DATA_DEFAULT) // first read ever
+                                               aix_data[aix_sel] = aix_val;
+                                       else
+                                               aix_data[aix_sel] = (aix_val + aix_data[aix_sel])>>1; // low pass approach
+                                       
+                                       
+                                       if((1<<aix_sel)&AIX_ADSR) // envelope to change
+                                               update_adsr();
+                                       else if((1<<aix_sel)&AIX_WAVE) //waveform to change
+                                               update_wave();
+                                       else if(aix_sel == AIX_CONTRAST) // contrast to change
+                                               lcd_contrast(aix_val);
+                                       else if(aix_sel == AIX_BRIGHTNESS) // brightness to change
+                                               lcd_brightness(aix_val);
+                               }
+                               // debounce the waveform selector switch
+                               if (aix_sw_new != aix_sw_old)
+                                       aix_sw_count = 0;
+                               else if (aix_sw_count < AIX_SW_DEBOUNCE)
+                               {
+                                       ++aix_sw_count;
+                                       if (aix_sw_count == AIX_SW_DEBOUNCE)
+                                       {
+                                               if (aix_sw_new != aix_data[AIX_SW])
+                                               {
+                                                       // debounce completed, update the waveform
+                                                       aix_data[AIX_SW] = aix_sw_new;
+                                                       update_wave();
+                                               }
+                                       }
+                               }
+                               aix_sw_old = aix_sw_new;
+                       }
+                       // reset state
+                       aix_i = 0;
+                       aix_check = 0;
+               }
+               else
+               {
+                       if (data >= '0' && data <= '9')
+                               data -= '0';
+                       else if (data >= 'A' && data <= 'F')
+                               data -= 'A' - 10;
+                       else if (data >= 'a' && data <= 'f')
+                               data -= 'a' - 10;
+                       else if (data >= 'G' && data <= 'V')
+                               data -= 'G';
+                       else if (data >= 'g' && data <= 'v')
+                               data -= 'g';
+                       else
+                               data = 0;
+                       
+                       if (aix_i & 0x1) // high half byte
+                               data <<= 4;
+                       
+                       aix_check += data; // update checksum
+                       
+                       switch (aix_i) // which byte?
+                       {
+                       case 0: // setting index / command ID
+                               aix_sel = data;
+                               break;
+                       case 1: // value bits 7-4
+                               aix_val = 0;
+                       case 2: // value bits 3-0
+                               aix_val |= data;
+                               break;
+                       case 3: // switch state
+                               aix_sw_new = data;
+                               break;
+                       case 4: // value bits 9-8
+                               *(((uint8_t*)&aix_val)+1) |= data;
+                               break;
+                       default:
+                               break;
+                       }
+                       
+                       ++aix_i; // next byte
+               }
+       }
+}
+
+void debug_aix (const uint8_t blocking) // print AIX state
+{
+       debug_string("AIX: ", blocking);
+       debug_hex(aix_data[AIX_BRIGHTNESS], blocking, 3, 1);
+       debug_byte(' ',blocking);
+       debug_hex(aix_data[AIX_CONTRAST], blocking, 3, 1);
+       debug_string(" | ",blocking);
+       debug_hex(aix_data[AIX_A], blocking, 3, 1);
+       debug_byte(' ',blocking);
+       debug_hex(aix_data[AIX_D], blocking, 3, 1);
+       debug_byte(' ',blocking);
+       debug_hex(aix_data[AIX_S], blocking, 3, 1);
+       debug_byte(' ',blocking);
+       debug_hex(aix_data[AIX_R], blocking, 3, 1);
+       debug_string(" | ",blocking);
+       debug_hex(aix_data[AIX_V1], blocking, 3, 1);
+       debug_byte(' ',blocking);
+       debug_hex(aix_data[AIX_V2], blocking, 3, 1);
+       debug_byte(' ',blocking);
+       debug_hex(aix_data[AIX_V3], blocking, 3, 1);
+       debug_byte(' ',blocking);
+       debug_hex(aix_data[AIX_V4], blocking, 3, 1);
+       debug_string(" | ",blocking);
+       debug_hex(aix_data[AIX_T1], blocking, 3, 1);
+       debug_byte(' ',blocking);
+       debug_hex(aix_data[AIX_T2], blocking, 3, 1);
+       debug_byte(' ',blocking);
+       debug_hex(aix_data[AIX_T3], blocking, 3, 1);
+       debug_byte(' ',blocking);
+       debug_hex(aix_data[AIX_T4], blocking, 3, 1);
+       debug_string(" | ",blocking);
+       debug_hex(aix_data[AIX_SW], blocking, 3, 1);
+       debug_string("\r\n",blocking);
+}
diff --git a/aix.h b/aix.h
new file mode 100644 (file)
index 0000000..fda74e3
--- /dev/null
+++ b/aix.h
@@ -0,0 +1,96 @@
+/* ANALOG INPUT EXPANDER */
+/*
+Copyright 2021 Balthasar Szczepański
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include <stdint.h>
+#include "klavirko-ui.h"
+
+/*
+UI layout:
+
+  F
+ T 0 |o######| E D  | C B A 9
+  V             1 2 |  3 4 5 6
+
+*/
+
+// index' of all settings
+
+#define AIX_BRIGHTNESS 0xF
+#define AIX_CONTRAST   0x0
+
+#define AIX_A 0xE
+#define AIX_D 0xD
+#define AIX_S 0x1
+#define AIX_R 0x2
+
+#define AIX_V1 0xC
+#define AIX_V2 0xB
+#define AIX_V3 0xA
+#define AIX_V4 0x9
+
+#define AIX_T1 0x3
+#define AIX_T2 0x4
+#define AIX_T3 0x5
+#define AIX_T4 0x6
+
+#define AIX_UNUSED1 0x07
+#define AIX_UNUSED2 0x08
+
+#define AIX_UNUSED (\
+       (1 << AIX_UNUSED1)|\
+       (1 << AIX_UNUSED2)\
+)
+
+#define AIX_SW 0x10
+
+#define AIX_ADSR (\
+       (1 << AIX_A)|\
+       (1 << AIX_D)|\
+       (1 << AIX_S)|\
+       (1 << AIX_R)\
+)
+#define AIX_WAVE (\
+       (1 << AIX_V1)|\
+       (1 << AIX_V2)|\
+       (1 << AIX_V3)|\
+       (1 << AIX_V4)|\
+       (1 << AIX_T1)|\
+       (1 << AIX_T2)|\
+       (1 << AIX_T3)|\
+       (1 << AIX_T4)\
+)
+
+#define N_AIX_DATA       17
+#define AIX_DATA_DEFAULT 0x0fff
+#define AIX_DATA_MAX     0x03ff
+#define AIX_DATA_INIT \
+{ \
+       AIX_DATA_DEFAULT, AIX_DATA_DEFAULT, AIX_DATA_DEFAULT, AIX_DATA_DEFAULT, \
+       AIX_DATA_DEFAULT, AIX_DATA_DEFAULT, AIX_DATA_DEFAULT, AIX_DATA_DEFAULT, \
+       AIX_DATA_DEFAULT, AIX_DATA_DEFAULT, AIX_DATA_DEFAULT, AIX_DATA_DEFAULT, \
+       AIX_DATA_DEFAULT, AIX_DATA_DEFAULT, AIX_DATA_DEFAULT, AIX_DATA_DEFAULT, \
+       AIX_DATA_DEFAULT \
+}
+#define N_AIX_BUFFER    10
+#define AIX_MIN_DIFF    8
+#define AIX_TIMEOUT     1190
+#define AIX_SW_DEBOUNCE 60
+
+inline void aix_watchdog(void) LOWTEXT;    // monitor if AIX communication still working
+inline void int_aix_rx (void) LOWTEXT_INT; // received byte from AIX
+inline void int_aix_err(void) LOWTEXT_INT; // received error from AIX
+inline void setup_aix(void);
+       void handle_aix(void);
+       void debug_aix (const uint8_t blocking); // print AIX state
+
+extern uint16_t aix_data[N_AIX_DATA];
diff --git a/ctrl.c b/ctrl.c
new file mode 100644 (file)
index 0000000..41fc908
--- /dev/null
+++ b/ctrl.c
@@ -0,0 +1,500 @@
+/* COMMUNICATING WITH MAIN CONTROLLER */
+/*
+Copyright 2021 Balthasar Szczepański
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include <string.h>
+#include "klavirko-ui.h"
+#include "ctrl.h"
+#include "wave.h"
+#include "debug.h"
+#include "gui.h"
+
+         uint8_t  ctrl_databuffer[N_CTRL_DATABUFFER];
+volatile uint8_t  ctrl_rxbuffer[N_CTRL_RXBUFFER];
+volatile uint8_t  ctrl_rxbuffer_r = 0;
+volatile uint8_t  ctrl_rxbuffer_w = 0;
+         uint8_t  ctrl_txbuffer[N_CTRL_TXBUFFER];
+         uint8_t  ctrl_txbuffer_r = 0;
+         uint8_t  ctrl_txbuffer_w = 0;
+volatile uint8_t  ctrl_error = 0;
+
+         uint8_t  ctrl_state = CTRL_WAIT;
+         uint8_t  ctrl_retry = 0;
+volatile uint16_t ctrl_timeout = 0;
+
+         uint16_t ctrl_request = 0;
+         uint16_t ctrl_finished = 0;
+         uint16_t ctrl_failed = 0;
+         uint16_t ctrl_action = 0;
+         uint8_t  ctrl_cmd = CMD_NOP;
+         uint8_t  ctrl_resp = 0;
+         uint8_t  ctrl_checksum = 0;
+         uint8_t  ctrl_halfbyte = 0;
+         int16_t  ctrl_index = 0;
+         int16_t  ctrl_limit = 0;
+
+         uint8_t  midi_id = 0;
+         uint8_t  midi_id_out = 16;
+         uint8_t  midi_pedal_en = 1;
+
+
+inline void ctrl_watchdog(void)
+{
+       ++ctrl_timeout;
+}
+
+inline void int_ctrl_rx(void) // received byte from CTRL
+{
+       uint8_t data;
+       
+       data = CTRL_RX__SDR; // get byte
+       
+       if((ctrl_rxbuffer_w + 1 == ctrl_rxbuffer_r) || (ctrl_rxbuffer_w + 1 == ctrl_rxbuffer_r + N_CTRL_RXBUFFER)) // buffer overflow
+               ctrl_error |= ERR_OVERFLOW;
+       else
+       {
+               ctrl_rxbuffer[ctrl_rxbuffer_w] = data;
+               // advance the pointer
+               if (ctrl_rxbuffer_w >= N_CTRL_RXBUFFER - 1)
+                       ctrl_rxbuffer_w = 0;
+               else
+                       ++ctrl_rxbuffer_w;
+       }
+}
+
+inline void int_ctrl_err(void) // received error from CTRL
+{
+       volatile uint8_t data; //just to make sure a read is performed
+       uint16_t err;
+       
+       // not interested which error occurred and why
+       // just clear and continue
+       // frames have checksums anyway
+       
+       data = CTRL_RX__SDR;
+       err = CTRL_RX__SSR;
+       CTRL_RX__SIR = err;
+       
+       ctrl_error |= ERR_UART;
+}
+
+inline void setup_ctrl (void)
+{
+       PER0 |= 0b00000100; //enable serial array unit 0
+       
+       SPS0 = (SPS0 & 0xff0f) | 0x0010; //CK01 = fclk/2 = 16MHz
+       
+       CTRL_TX__SMR = 0b1000000000100011; //CK01, int. on buf empty
+       CTRL_RX__SMR = 0b1000000100100010; //CK01, ...
+       
+       CTRL_TX__SCR = 0b1000000010010111; //8n1, LSB
+       CTRL_RX__SCR = 0b0100010010010111;
+       
+       CTRL_TX__SDR = 0x8c00l; //16MHz / 138 = 115942.029bps
+       CTRL_RX__SDR = 0x8c00l;
+       
+       SOL0 &= ~0x0004; //nonreversed
+       SO0  |= 0b0000110000001100;
+       
+       CTRL_TX__SOE = 1;
+       
+       CTRL_TX__POM = 0;
+       CTRL_TX__PMC = 0;
+       CTRL_TX__PM  = 0;
+       CTRL_TX__PU  = 0;
+       CTRL_TX      = 1;
+       
+       CTRL_RX__POM = 0;
+       CTRL_RX__PIM = 0;
+       CTRL_RX__PMC = 0;
+       CTRL_RX__PM  = 1;
+       CTRL_RX__PU  = 0;
+       CTRL_RX      = 1;
+       
+       CTRL_TX__SS = 1;
+       CTRL_RX__SS = 1;
+       
+       CTRL_RX__MK = 0;
+       CTRL_RX__MKE = 0;
+}
+
+inline void init_ctrl (void)
+{
+       ctrl_request |= FLAGS_INIT_CTRL;
+       
+       while (
+               ((ctrl_finished & FLAGS_INIT_CTRL) != FLAGS_INIT_CTRL) && // still unfinished requests
+               (ctrl_failed == 0) // none of them failed
+       ){
+               handle_ctrl();
+               handle_debug();
+       }
+}
+
+void handle_ctrl (void)
+{
+       uint8_t io;
+       // no 'else's to allow fall throughs
+       
+       if (ctrl_state == CTRL_WAIT && ctrl_request != 0) // new request to handle
+       {
+               // check which request
+               if(ctrl_request & FLAG_GET_TIMING)
+               {
+                       ctrl_action = FLAG_GET_TIMING;
+                       ctrl_cmd = CMD_GET_TIMING;
+               }
+               else if(ctrl_request & FLAG_GET_MAX_TUNING)
+               {
+                       ctrl_action = FLAG_GET_MAX_TUNING;
+                       ctrl_cmd = CMD_GET_MAX_TUNING;
+               }
+               else if(ctrl_request & FLAG_SET_TUNING)
+               {
+                       ctrl_action = FLAG_SET_TUNING;
+                       ctrl_cmd = CMD_SET_TUNING;
+               }
+               else if(ctrl_request & FLAG_SET_ADSR)
+               {
+                       ctrl_action = FLAG_SET_ADSR;
+                       ctrl_cmd = CMD_SET_ADSR;
+               }
+               else if(ctrl_request & FLAG_SET_SAMPLE)
+               {
+                       ctrl_action = FLAG_SET_SAMPLE;
+                       ctrl_cmd = CMD_SET_SAMPLE;
+               }
+               else if(ctrl_request & FLAG_GET_MIDI_CFG)
+               {
+                       ctrl_action = FLAG_GET_MIDI_CFG;
+                       ctrl_cmd = CMD_GET_MIDI_CFG;
+               }
+               else if(ctrl_request & FLAG_SET_MIDI_CFG)
+               {
+                       ctrl_action = FLAG_SET_MIDI_CFG;
+                       ctrl_cmd = CMD_SET_MIDI_CFG;
+               }
+               else // sorry, invalid
+               {
+                       ctrl_action = 0;
+                       ctrl_cmd = CMD_NOP;
+               }
+               
+               if (ctrl_action) // OK, stuff to do
+               {
+                       ctrl_state = CTRL_PRE_EXEC;
+                       ctrl_retry = 0;
+                       ctrl_error = 0;
+                       ctrl_request &= ~ctrl_action;
+                       ctrl_finished &= ~ctrl_action;
+                       ctrl_failed &= ~ctrl_action;
+               }
+               else // back to waiting
+               {
+                       ctrl_state = CTRL_WAIT;
+                       ctrl_request = 0;
+               }
+       }
+       
+       if (ctrl_state == CTRL_PRE_EXEC) // execute action before sending command
+       {
+               ctrl_error = 0;
+               switch(ctrl_cmd)
+               {
+               case CMD_SET_SAMPLE:
+                       memcpy(ctrl_databuffer, sample, N_SAMPLE);
+                       ctrl_limit = N_SAMPLE;
+                       break;
+               case CMD_SET_ADSR:
+                       *((uint32_t*)ctrl_databuffer) = adsr_A;
+                       *((uint32_t*)(ctrl_databuffer+4)) = adsr_D;
+                       *((uint32_t*)(ctrl_databuffer+8)) = adsr_S;
+                       *((uint32_t*)(ctrl_databuffer+12)) = adsr_R;
+                       ctrl_limit = 4*4;
+                       break;
+               case CMD_SET_TUNING:
+                       *((uint16_t*)ctrl_databuffer) = tuning;
+                       *((int8_t*)(ctrl_databuffer+2)) = transp;
+                       ctrl_limit = 3;
+                       break;
+               case CMD_SET_MIDI_CFG:
+                       ctrl_databuffer[0] = midi_id;
+                       ctrl_databuffer[1] = midi_id_out;
+                       ctrl_databuffer[2] = midi_pedal_en;
+                       ctrl_limit = 3;
+                       break;
+               default:
+                       ctrl_limit = 0;
+                       break;
+               }
+               ctrl_state = CTRL_SEND;
+               ctrl_checksum = 0;
+               ctrl_index = -1;
+       }
+       
+       while (ctrl_state == CTRL_SEND) // send the command
+       {
+               if((ctrl_txbuffer_w + 1 == ctrl_txbuffer_r) || (ctrl_txbuffer_w + 1 == ctrl_txbuffer_r + N_CTRL_TXBUFFER)) // no place in buffer
+                       break;
+               
+               if (ctrl_index < 0) // start of frame, command id
+               {
+                       io = ctrl_cmd;
+                       ctrl_checksum -= ctrl_cmd;
+                       ctrl_index = 0;
+                       ctrl_halfbyte = 0;
+               }
+               else if (ctrl_index > ctrl_limit) // end of frame
+               {
+                       io = '\n';
+                       ctrl_state = CTRL_SWITCH; // GOTO next state
+                       ctrl_rxbuffer_r = ctrl_rxbuffer_w;
+               }
+               else // data byte
+               {
+                       if (ctrl_index == ctrl_limit) // last byte, the checksum
+                               io = ctrl_checksum;
+                       else
+                               io = ctrl_databuffer[ctrl_index];
+                       if (ctrl_halfbyte == 0)
+                       {
+                               io >>= 4;
+                               ctrl_halfbyte = 1;
+                       }
+                       else
+                       {
+                               ctrl_checksum -= io;
+                               io &= 0x0f;
+                               ctrl_halfbyte = 0;
+                               ++ctrl_index;
+                       }
+                       if (io > 9)
+                               io += ('a' - 10);
+                       else
+                               io += '0';
+               }
+               
+               // put to buffer, adnvance the pointer
+               ctrl_txbuffer[ctrl_txbuffer_w] = io;
+               if (ctrl_txbuffer_w >= N_CTRL_TXBUFFER - 1)
+                       ctrl_txbuffer_w = 0;
+               else
+                       ++ctrl_txbuffer_w;
+       }
+       
+       if (ctrl_txbuffer_r != ctrl_txbuffer_w) // data in output buffer
+       {
+               if(!(CTRL_TX__SSR & 0b01000000)) // uart is ready
+               {
+                       CTRL_TX__IF = 0;
+                       CTRL_TX__SDR = ctrl_txbuffer[ctrl_txbuffer_r]; // send the byte
+                       
+                       // advance the pointer
+                       if (ctrl_txbuffer_r >= N_CTRL_RXBUFFER - 1)
+                               ctrl_txbuffer_r = 0;
+                       else
+                               ++ctrl_txbuffer_r;
+               }
+       }
+       
+       if (ctrl_state == CTRL_SWITCH) // switch from send to receive; recalculate frame length
+       {
+               ctrl_index = 0;
+               ctrl_halfbyte = 0;
+               ctrl_timeout = 0;
+               // ctrl_error = 0;
+               
+               switch(ctrl_cmd)
+               {
+               case CMD_GET_MAX_TUNING:
+                       ctrl_limit = 2;
+                       break;
+               case CMD_GET_TIMING:
+                       ctrl_limit = 2 * 4;
+                       break;
+               case CMD_GET_MIDI_CFG:
+                       ctrl_limit = 3;
+                       break;
+               default:
+                       ctrl_limit = 0;
+                       break;
+               }
+               
+               ctrl_state = CTRL_RECEIVE;
+               ctrl_resp = CMD_NOP;
+       }
+       
+       while (ctrl_rxbuffer_r != ctrl_rxbuffer_w) // data in input buffer
+       {
+               // get the byte, advance the pointer
+               io = ctrl_rxbuffer[ctrl_rxbuffer_r];
+               if(ctrl_rxbuffer_r >= N_CTRL_RXBUFFER -1 )
+                       ctrl_rxbuffer_r = 0;
+               else
+                       ++ctrl_rxbuffer_r;
+               
+               if (io == CMD_ERR) // received a complain from CTRL
+               {
+                       // drop any action, immediately switch to listen
+                       ctrl_state = CTRL_RECEIVE;
+                       ctrl_txbuffer_r = ctrl_txbuffer_w;
+               }
+               
+               if (ctrl_state == CTRL_RECEIVE) // actually expecting to receive
+               {
+                       if (io == '\n') // end of frame
+                       {
+                               if (ctrl_cmd != CMD_NOP)
+                               {
+                                       if (ctrl_resp != ctrl_cmd) // response to the same id?
+                                               ctrl_error |= ERR_MISMATCH;
+                                       if (ctrl_checksum != 0) // checksum correct?
+                                               ctrl_error |= ERR_CHECKSUM;
+                                       if (ctrl_index != ctrl_limit + 1) // length correct?
+                                               ctrl_error |= ERR_LENGTH;
+                                       ctrl_state = CTRL_POST_EXEC; // GOTO next state
+                               }
+                       }
+                       else if (io <= ' ') // whitespace to ignore
+                       {
+                               
+                       }
+                       else if ( // data
+                               (io >= '0' && io <= '9')||
+                               (io >= 'A' && io <= 'F')||
+                               (io >= 'a' && io <= 'f')
+                       ){
+                               if (ctrl_index <= ctrl_limit)
+                               {
+                                       if (io >= 'a')
+                                               io -= ('a'-10);
+                                       else if (io >= 'A')
+                                               io -= ('A'-10);
+                                       else
+                                               io -= '0';
+                                       
+                                       if (ctrl_halfbyte)
+                                       {
+                                               ctrl_checksum += io;
+                                               if (ctrl_index < ctrl_limit)
+                                                       ctrl_databuffer[ctrl_index] |= io;
+                                               ++ctrl_index;
+                                               ctrl_halfbyte = 0;
+                                       }
+                                       else
+                                       {
+                                               io <<= 4;
+                                               ctrl_checksum += io;
+                                               if (ctrl_index < ctrl_limit)
+                                                       ctrl_databuffer[ctrl_index] = io;
+                                               ctrl_halfbyte = 1;
+                                       }
+                               }
+                               else
+                                       ctrl_index = ctrl_limit + 2;
+                       }
+                       else //response index
+                       {
+                               ctrl_resp = io;
+                               ctrl_checksum = io;
+                               ctrl_index = 0;
+                               ctrl_halfbyte = 0;
+                               
+                       }
+               }
+       }
+       
+       if (ctrl_state == CTRL_RECEIVE) // still expecting to receive
+       {
+               if (ctrl_timeout >= CTRL_TIMEOUT)
+                       ctrl_error |= ERR_TIMEOUT;
+               if (ctrl_error != 0)
+                       ctrl_state = CTRL_POST_EXEC;
+       }
+       
+       if (ctrl_state == CTRL_POST_EXEC) // execute action after receiving response
+       {
+               if (ctrl_error != 0) // there was error, handle this instead
+               {
+                       debug_string("CTRL_ERR: ", 1);
+                       debug_hex(ctrl_error, 0, 2, 1);
+                       debug_string(" !\r\n", 1);
+                       
+                       ++ctrl_retry;
+                       if(ctrl_retry >= CTRL_RETRY)
+                       {
+                               debug_string("CTRL_TIMEOUT!\r\n", 1);
+                               gui_trigger_error(GUI_ERRF_CTRL);
+                               ctrl_failed |= ctrl_action;
+                               ctrl_state = CTRL_WAIT; // give up
+                       }
+                       else
+                       {
+                               ctrl_state = CTRL_PRE_EXEC; // try again
+                       }
+               }
+               else
+               {
+                       switch (ctrl_cmd)
+                       {
+                       case CMD_GET_TIMING:
+                               set_timing(
+                                       *((uint32_t*)ctrl_databuffer),
+                                       *((uint32_t*)(ctrl_databuffer+4))
+                               );
+                               break;
+                       case CMD_GET_MAX_TUNING:
+                               set_max_tuning (*((uint16_t*)ctrl_databuffer));
+                               break;
+                       case CMD_GET_MIDI_CFG:
+                               midi_id = (ctrl_databuffer[0]<N_MIDI_ID) ? ctrl_databuffer[0] : N_MIDI_ID;
+                               midi_id_out = ctrl_databuffer[1] & MIDI_ID_MASK;
+                               midi_pedal_en = ctrl_databuffer[2];
+                               ctrl_finished |= FLAG_GET_MIDI_CFG;
+                               break;
+                       case CMD_SET_TUNING:
+                       case CMD_SET_ADSR:
+                       case CMD_SET_SAMPLE:
+                       case CMD_SET_MIDI_CFG:
+                               break;
+                       default:
+                               ctrl_action = 0;
+                               break;
+                       }
+                       ctrl_finished |= ctrl_action;
+                       ctrl_state = CTRL_WAIT; // done; wait for next
+                       ctrl_retry = 0;
+               }
+       }
+}
+
+//mark changed data to be (re)sent to CTRL
+
+inline void ctrl_update_wave (void)
+{
+       ctrl_request |= FLAG_SET_SAMPLE;
+}
+
+inline void ctrl_update_adsr (void)
+{
+       ctrl_request |= FLAG_SET_ADSR;
+}
+
+inline void ctrl_update_tuning (void)
+{
+       ctrl_request |= FLAG_SET_TUNING;
+}
+
+inline void ctrl_update_midi (void)
+{
+       ctrl_request |= FLAG_SET_MIDI_CFG;
+}
diff --git a/ctrl.h b/ctrl.h
new file mode 100644 (file)
index 0000000..a4fedab
--- /dev/null
+++ b/ctrl.h
@@ -0,0 +1,99 @@
+/* COMMUNICATING WITH MAIN CONTROLLER */
+/*
+Copyright 2021 Balthasar Szczepański
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include <stdint.h>
+#include "klavirko-ui.h"
+
+// commands
+#define CMD_NOP             0
+#define CMD_ERR             '!'
+#define CMD_GET_SAMPLE      'G'
+#define CMD_SET_SAMPLE      'g'
+#define CMD_GET_SAMPLE_BYTE 'H'
+#define CMD_SET_SAMPLE_BYTE 'h'
+#define CMD_GET_ADSR        'I'
+#define CMD_SET_ADSR        'i'
+#define CMD_GET_TUNING      'J'
+#define CMD_SET_TUNING      'j'
+#define CMD_GET_MAX_TUNING  'K'
+#define CMD_GET_TIMING      'L'
+#define CMD_GET_MIDI_CFG    'M'
+#define CMD_SET_MIDI_CFG    'm'
+
+// request/status flags
+#define FLAG_ERR             0x0001
+#define FLAG_GET_SAMPLE      0x0002
+#define FLAG_SET_SAMPLE      0x0004
+#define FLAG_GET_SAMPLE_BYTE 0x0008
+#define FLAG_SET_SAMPLE_BYTE 0x0010
+#define FLAG_GET_ADSR        0x0020
+#define FLAG_SET_ADSR        0x0040
+#define FLAG_GET_TUNING      0x0080
+#define FLAG_SET_TUNING      0x0100
+#define FLAG_GET_MAX_TUNING  0x0200
+#define FLAG_GET_TIMING      0x0400
+#define FLAG_GET_MIDI_CFG    0x0800
+#define FLAG_SET_MIDI_CFG    0x1000
+
+// which actions to request at start
+#define FLAGS_INIT_CTRL ( \
+       FLAG_GET_TIMING | \
+       FLAG_GET_MAX_TUNING | \
+       FLAG_GET_MIDI_CFG | \
+       FLAG_SET_TUNING \
+)
+
+// state machine
+
+#define CTRL_WAIT      0
+#define CTRL_PRE_EXEC  1
+#define CTRL_SEND      2
+#define CTRL_SWITCH    3
+#define CTRL_RECEIVE   4
+#define CTRL_POST_EXEC 5
+
+// error flags
+
+#define ERR_NO       0x00
+#define ERR_UART     0x01
+#define ERR_OVERFLOW 0x02
+#define ERR_CHECKSUM 0x04
+#define ERR_LENGTH   0x08
+#define ERR_MISMATCH 0x10
+#define ERR_TIMEOUT  0x20
+
+#define CTRL_TIMEOUT 110
+#define CTRL_RETRY    64
+
+#define N_CTRL_DATABUFFER 256 //at least as big as sample
+#define N_CTRL_RXBUFFER   32
+#define N_CTRL_TXBUFFER   32
+
+#define N_MIDI_ID      16
+#define MIDI_ID_MASK 0x0f
+
+extern uint8_t midi_id;
+extern uint8_t midi_id_out;
+extern uint8_t midi_pedal_en;
+
+inline void ctrl_watchdog (void) LOWTEXT;
+inline void int_ctrl_rx (void)  LOWTEXT_INT; // received byte from CTR
+inline void int_ctrl_err (void) LOWTEXT_INT; // received error from CTR
+inline void setup_ctrl (void);
+inline void init_ctrl (void);
+       void handle_ctrl (void);
+//mark changed data to be (re)sent to CTRL
+inline void ctrl_update_wave (void);
+inline void ctrl_update_adsr (void);
+inline void ctrl_update_tuning (void);
+inline void ctrl_update_midi (void);
diff --git a/debug.c b/debug.c
new file mode 100644 (file)
index 0000000..598271c
--- /dev/null
+++ b/debug.c
@@ -0,0 +1,115 @@
+/* DEBUG SERIAL OUTPUT */
+/*
+Copyright 2021 Balthasar Szczepański
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include "klavirko-ui.h"
+#include "debug.h"
+#include "main.h"
+
+uint8_t debug_buffer[N_DEBUG_BUFFER];
+uint8_t debug_buffer_r = 0;
+uint8_t debug_buffer_w = 0;
+
+//using AIX TX as debug output now
+void handle_debug(void)
+{
+       uint8_t byte;
+       
+       if(debug_buffer_r != debug_buffer_w) // data waiting in buffer
+       {
+               byte = debug_buffer[debug_buffer_r]; // get the byte
+               
+               if(!(AIX_TX__SSR & 0b01000000)) // uart is ready
+               {
+                       AIX_TX__IF = 0;
+                       AIX_TX__SDR = byte;
+               }
+               else
+                       return;
+               // advance the pointer
+               if(debug_buffer_r >= N_DEBUG_BUFFER - 1)
+                       debug_buffer_r = 0;
+               else
+                       ++debug_buffer_r;
+       }
+}
+
+void debug_byte ( // send a byte to debug output
+       const uint8_t value,
+       const uint8_t blocking
+)
+{
+       while((debug_buffer_w + 1 == debug_buffer_r) || (debug_buffer_w + 1 == debug_buffer_r + N_DEBUG_BUFFER))
+       {
+               if (blocking)
+                       handle_debug();
+               else
+                       return;
+       }
+       debug_buffer[debug_buffer_w] = value;
+       if(debug_buffer_w >= N_DEBUG_BUFFER - 1)
+               debug_buffer_w = 0;
+       else
+               ++debug_buffer_w;
+}
+
+void debug_string ( // send a string to debug output
+       const uint8_t * const text,
+       const uint8_t blocking
+)
+{
+       uint16_t i;
+       for(i=0; text[i] != '\0'; ++i)
+               debug_byte(text[i], blocking);
+}
+
+// todo: use make_dec/hex_string() to avoid code duplication ?
+
+void debug_hex ( // send hexadecimal number to debug output
+       const uint32_t value,
+       const uint8_t blocking,
+       const uint16_t min_digits,
+       const uint8_t uppercase
+)
+{
+       uint8_t x;
+       uint8_t text[9];
+       
+       x = make_hex_string(
+               text,
+               value,
+               min_digits,
+               8,
+               uppercase
+       );
+       text[x] = '\0';
+       debug_string(text, blocking);
+}
+
+void debug_dec ( // send decimal number to debug output
+       const uint32_t value,
+       const uint8_t blocking,
+       const uint16_t min_digits
+)
+{
+       uint8_t text[11];
+       uint8_t x;
+       
+       x = make_dec_string(
+               text,
+               value,
+               min_digits,
+               10
+       );
+       text[x] = '\0';
+       debug_string(text, blocking);
+}
diff --git a/debug.h b/debug.h
new file mode 100644 (file)
index 0000000..ceca855
--- /dev/null
+++ b/debug.h
@@ -0,0 +1,39 @@
+/* DEBUG SERIAL OUTPUT */
+/*
+Copyright 2021 Balthasar Szczepański
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include <stdint.h>
+
+//using TX of AIX as debug output
+
+#define N_DEBUG_BUFFER 256
+
+void handle_debug(void);
+void debug_byte ( // send a byte to debug output
+       const uint8_t value,
+       const uint8_t blocking
+);
+void debug_string ( // send a string to debug output
+       const uint8_t * const text,
+       const uint8_t blocking
+);
+void debug_hex ( // send hexadecimal number to debug output
+       const uint32_t value,
+       const uint8_t blocking,
+       const uint16_t min_digits,
+       const uint8_t uppercase
+);
+void debug_dec ( // send decimal number to debug output
+       const uint32_t value,
+       const uint8_t blocking,
+       const uint16_t min_digits
+);
diff --git a/font16.png b/font16.png
new file mode 100644 (file)
index 0000000..035d0b3
Binary files /dev/null and b/font16.png differ
diff --git a/font16.xcf b/font16.xcf
new file mode 100644 (file)
index 0000000..4111270
Binary files /dev/null and b/font16.xcf differ
diff --git a/font8.png b/font8.png
new file mode 100644 (file)
index 0000000..7cd4f40
Binary files /dev/null and b/font8.png differ
diff --git a/font8.xcf b/font8.xcf
new file mode 100644 (file)
index 0000000..980a6bb
Binary files /dev/null and b/font8.xcf differ
diff --git a/fs.c b/fs.c
new file mode 100644 (file)
index 0000000..1dbfa2d
--- /dev/null
+++ b/fs.c
@@ -0,0 +1,1377 @@
+/* STORAGE OF SETTINGS */
+/*
+Copyright 2021 Balthasar Szczepański
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include <string.h>
+#include "klavirko-ui.h"
+#include "fs.h"
+#include "debug.h"
+#include "main.h"
+#include "gui.h"
+#include "wave.h"
+
+int16_t fs_index0 = -1;
+int16_t fs_index1 = -1;
+int16_t fs_newindex0 = -1;
+int16_t fs_newindex1 = -1;
+int16_t fs_index = -1;
+int16_t fs_index_block = -1;
+int16_t fs_index_record = -1;
+int16_t fs_index_sector = -1;
+int16_t fs_index_fileid = -1;
+
+int16_t fs_page = -1;
+uint8_t fs_page_flags = 0;
+int16_t fs_page_sectors[N_FILES_ON_PAGE] = {[0 ... (N_FILES_ON_PAGE-1)] = -1};
+int16_t fs_page_fileids[N_FILES_ON_PAGE] = {[0 ... (N_FILES_ON_PAGE-1)] = -1};
+uint8_t fs_page_names[N_FILES_ON_PAGE][N_NAME+1] = {[0 ... (N_FILES_ON_PAGE-1)] = {[0 ... N_NAME] = 0}};
+int16_t fs_page_indexes[N_FILES_ON_PAGE] = {[0 ... (N_FILES_ON_PAGE-1)] = -1};
+int16_t fs_page_indexblocks[N_FILES_ON_PAGE] = {[0 ... (N_FILES_ON_PAGE-1)] = -1};
+int16_t fs_page_indexrecords[N_FILES_ON_PAGE] = {[0 ... (N_FILES_ON_PAGE-1)] = -1};
+
+volatile uint16_t fs_timeout;
+
+inline void fs_watchdog (void)
+{
+       ++fs_timeout;
+}
+
+
+/* SPI FLASH HANDLING */
+
+inline uint8_t fs_spi_1b (const uint8_t data) // send & receive single byte
+{
+       FS__SDR = data;
+       while (FS__SSR & 0x0040)
+               {}
+       return (uint8_t)(FS__SDR );
+}
+
+void fs_spi( // execute single command 
+       const uint8_t   opcode,
+       const uint16_t  address_bytes,
+       const uint16_t  dummy_bytes,
+       const uint16_t  data_bytes,
+       const uint32_t  address,
+       uint8_t * const read_data,
+       const uint8_t * const write_data,
+       const uint8_t   flag
+)
+{
+       uint16_t i;
+       uint8_t  io = 0;
+       
+       // clear errors
+       if (FS__SSR & 0b00000111)
+               FS__SIR = FS__SSR;
+       
+       // Chip Select & LED indicator
+       LED_U = 1;
+       FS_CS = 0;
+       
+       io = fs_spi_1b(opcode);
+       
+       for (i = address_bytes ? 2 : -1; i<3; --i) //assume 3 or 0 bytes
+       {
+               io = fs_spi_1b(*(((uint8_t *)(&address))+i));
+       }
+       
+       for (i=0; i<dummy_bytes; ++i)
+       {
+               io = fs_spi_1b(io);
+       }
+       
+       for (i=0; i<data_bytes; ++i)
+       {
+               if (flag & FS_WRITE_FLAG)
+                       io = write_data[i];
+               else
+                       io = 0;
+               io = fs_spi_1b(io);
+               if (flag & FS_READ_FLAG)
+                       read_data[i] = io;
+       }
+       
+       // Chip Select & LED indicator
+       FS_CS = 1;
+       LED_U = 0;
+}
+
+/* SPI FLASH COMMANDS */
+
+inline void fs_spi_read2 (
+       const uint32_t  address,
+       const uint16_t  n,
+       uint8_t * const data
+)
+{
+       fs_spi(FS_READ_2, 3, 2, n, address, data, FS_NULL, FS_READ_FLAG);
+}
+
+inline void fs_spi_read1 (
+       const uint32_t  address,
+       const uint16_t  n,
+       uint8_t * const data
+)
+{
+       fs_spi(FS_READ_1, 3, 1, n, address, data, FS_NULL, FS_READ_FLAG);
+}
+
+inline void fs_spi_read (
+       const uint32_t  address,
+       const uint16_t  n,
+       uint8_t * const data
+)
+{
+       fs_spi(FS_READ, 3, 0, n, address, data, FS_NULL, FS_READ_FLAG);
+}
+
+inline void fs_spi_erase_4k (const uint32_t address)
+{
+       fs_spi(FS_ERASE_4K, 3, 0, 0, address, FS_NULL, FS_NULL, 0);
+}
+
+inline void fs_spi_erase_32k (const uint32_t address)
+{
+       fs_spi(FS_ERASE_32K, 3, 0, 0, address, FS_NULL, FS_NULL, 0);
+}
+
+inline void fs_spi_erase_64k (const uint32_t address)
+{
+       fs_spi(FS_ERASE_64K, 3, 0, 0, address, FS_NULL, FS_NULL, 0);
+}
+
+inline void fs_spi_erase_chip (void)
+{
+       fs_spi(FS_ERASE_CHIP, 0, 0, 0, 0, FS_NULL, FS_NULL, 0);
+}
+
+inline void fs_spi_program (
+       const uint32_t  address,
+       const uint16_t  n,
+       const uint8_t * const data
+)
+{
+       fs_spi(FS_PROGRAM, 3, 0, n, address, FS_NULL, data, FS_WRITE_FLAG);
+}
+
+inline void fs_spi_write_enable (void)
+{
+       fs_spi(FS_WRITE_ENABLE, 0, 0, 0, 0, FS_NULL, FS_NULL, 0);
+}
+
+inline void fs_spi_write_disable (void)
+{
+       fs_spi(FS_WRITE_DISABLE, 0, 0, 0, 0, FS_NULL, FS_NULL, 0);
+}
+
+inline void fs_spi_protect_sector (const uint32_t address)
+{
+       fs_spi(FS_PROTECT_SECTOR, 3, 0, 0, address, FS_NULL, FS_NULL, 0);
+}
+
+inline void fs_spi_unprotect_sector (const uint32_t address)
+{
+       fs_spi(FS_UNPROTECT_SECTOR, 3, 0, 0, address, FS_NULL, FS_NULL, 0);
+}
+
+inline uint8_t fs_spi_read_sector_protection (const uint32_t address)
+{
+       uint8_t status;
+       fs_spi(FS_READ_SECTOR_PROTECTION, 3, 0, 1, address, &status, FS_NULL, FS_READ_FLAG);
+       return status;
+}
+
+inline uint16_t fs_spi_read_status (void)
+{
+       uint16_t status;
+       fs_spi(FS_READ_STATUS, 0, 0, 2, 0, (uint8_t*)&status, FS_NULL, FS_READ_FLAG);
+       return status;
+}
+
+inline void fs_spi_write_status_low (const uint8_t status)
+{
+       fs_spi(FS_WRITE_STATUS_1, 0, 0, 1, 0, FS_NULL, &status, FS_WRITE_FLAG);
+}
+
+inline void fs_spi_write_status_high (const uint8_t status)
+{
+       fs_spi(FS_WRITE_STATUS_2, 0, 0, 1, 0, FS_NULL, &status, FS_WRITE_FLAG);
+}
+
+inline void fs_spi_write_status (const uint16_t status)
+{
+       fs_spi_write_status_low((uint8_t) status);
+       fs_spi_write_status_high(*(((uint8_t*)(&status))+1));
+}
+
+inline void fs_spi_reset (void)
+{
+       uint8_t confirm = FS_RESET_CONFIRM;
+       fs_spi(FS_RESET, 0, 0, 1, 0, FS_NULL, &confirm, FS_WRITE_FLAG);
+}
+
+inline uint32_t fs_spi_read_id (void) // TODO: fix this if ever needed
+{
+       uint32_t id = 0;
+       fs_spi(FS_READ_ID, 0, 0, 3, 0, (uint8_t*)&id, FS_NULL, FS_READ_FLAG);
+       return id;
+}
+
+inline void fs_wait_ready (const uint16_t timeout)
+{
+       uint16_t status;
+       
+       fs_timeout = 0;
+       do
+       {
+               if (fs_timeout > timeout)
+               {
+                       fs_index = -1;
+                       fs_index0 = -1;
+                       fs_index1 = -1;
+                       debug_string("FS TIMEOUT!\r\n",1);
+                       gui_trigger_error(GUI_ERRF_FS);
+                       break;
+               }
+               status = fs_spi_read_status();
+       }while(status & FS_BSY);
+}
+
+/* support for filesystem operations */
+
+inline uint32_t fs_expand_address (
+       const uint8_t sector,
+       const uint8_t id,
+       const uint16_t offset
+)
+{
+       return
+               ((((uint32_t)sector)<<(FILEID_BITS+FILE_BITS)) |
+               (((uint32_t)id)<<FILE_BITS))
+               +offset;
+}
+
+inline uint32_t fs_expand_record_address (
+       const uint8_t index,
+       const uint8_t block,
+       const uint8_t record
+)
+{
+       uint8_t index_sector;
+       
+       if (index & NEW_INDEX)
+               index_sector = (index & 1) ? fs_newindex1 : fs_newindex0;
+       else
+               index_sector = (index & 1) ? fs_index1 : fs_index0;
+       
+       return fs_expand_address(
+               index_sector,
+               block,
+               ((uint16_t)record) * N_RECORD_BYTES + INDEX_OFFSET_RECORDS
+       );
+}
+
+inline uint8_t fs_is_sector_free (const uint8_t sector)
+{
+       uint32_t address;
+       uint8_t  data;
+       
+       address = fs_expand_address(sector, 0, FILE_OFFSET_FLAG);
+       fs_spi_read (address, 1, &data);
+       
+       return (data == 0xff) ? 0xff : 0x00;
+}
+
+inline uint8_t fs_is_file_free (
+       const uint8_t sector,
+       const uint8_t id
+)
+{
+       uint32_t address;
+       uint8_t  data;
+       
+       address = fs_expand_address(sector, 0, FILE_OFFSET_FLAG);
+       fs_spi_read (address, 1, &data);
+       
+       return data & (1<<id);
+}
+
+uint16_t fs_get_file_type (
+       const uint8_t sector,
+       const uint8_t id
+)
+{
+       uint32_t address;
+       uint16_t type = 0;
+       uint8_t  free;
+       
+       free = fs_is_file_free(sector, id);
+       address = fs_expand_address(sector, id, FILE_OFFSET_TYPE);
+       fs_spi_read (address, 1, (uint8_t*)&type);
+       
+       if(free)
+       {
+               if(type != FILE_FREE)
+                       type |= FILE_INVALID;
+       }
+       else
+       {
+               if(type == FILE_FREE)
+                       type |= FILE_INVALID;
+       }
+       return type;
+}
+
+inline uint16_t fs_get_index_validation (const uint8_t sector)
+{
+       uint16_t type;
+       
+       type = fs_get_file_type(sector, 0);
+       if ((type != FILE_INDEX_0)&&(type != FILE_INDEX_1))
+               return 0;
+       if ((fs_get_file_type(sector, 1)!=type))
+               return 0;
+       if ((fs_get_file_type(sector, 2)!=type))
+               return 0;
+       if ((fs_get_file_type(sector, 3)!=type))
+               return 0;
+       if ((fs_get_file_type(sector, 4)!=type))
+               return 0;
+       if ((fs_get_file_type(sector, 5)!=type))
+               return 0;
+       if ((fs_get_file_type(sector, 6)!=type))
+               return 0;
+       if ((fs_get_file_type(sector, 7)!=type))
+               return 0;
+       return type;
+}
+
+/* filesystem indexing */
+
+inline void fs_locate_index (void)
+{
+       int16_t i;
+       uint8_t found0 = 0;
+       uint8_t found1 = 0;
+       uint16_t type;
+       
+       for (i=0; i< N_SECTORS; ++i)
+       {
+               type = fs_get_index_validation(i);
+
+               if (type == FILE_INDEX_0)
+               {
+                       found0 = 1;
+                       fs_index0 = i;
+               }
+               else if (type == FILE_INDEX_1)
+               {
+                       found1 = 1;
+                       fs_index1 = i;
+               }
+
+               if (found0 && found1)
+                       return;
+       }
+       fs_index0 = -1;
+       fs_index1 = -1;
+}
+
+void fs_scan_index (void)
+{
+       uint8_t index, block, record;
+       uint8_t status, page, id, sector=fs_index0, fileid=N_FILES_IN_SECTOR-1;
+       uint8_t lastsector, lastfileid;
+       
+       for (index = 0; index<2; ++index)
+       {
+               for (block = 0; block < N_FILES_IN_SECTOR; ++block)
+               {
+                       for (record = 0; record < N_INDEX_RECORDS; ++record)
+                       {
+                               lastsector = sector;
+                               lastfileid = fileid;
+                               status = fs_read_record(
+                                       index, block, record,
+                                       &page, &id,
+                                       &sector, &fileid
+                               );
+                               if (status == RECORD_FREE)
+                                       goto fs_scan_index__after_scan;
+                       }
+               }
+       }
+       fs_scan_index__after_scan:
+       
+       fs_index = index;
+       fs_index_block = block;
+       fs_index_record = record;
+       fs_index_sector = lastsector;
+       fs_index_fileid = lastfileid;
+       fs_index_next_file();
+}
+
+inline void fs_index_next_record (void)
+{
+       if (fs_index_record < N_INDEX_RECORDS-1)
+               ++fs_index_record;
+       else
+       {
+               fs_index_record = 0;
+               if (fs_index_block < N_FILES_IN_SECTOR-1)
+                       ++fs_index_block;
+               else
+               {
+                       fs_index_block = 0;
+                       if(fs_index < 1)
+                               ++fs_index;
+                       else
+                       {
+                               fs_index = -1;
+                               fs_index0 = -1;
+                               fs_index1 = -1;
+                               debug_string("FS OVERFLOW!\r\n", 1);
+                               gui_trigger_error(GUI_ERRF_FS);
+                       }
+               }
+       }
+}
+
+inline void fs_index_next_sector (void)
+{
+       fs_index_fileid = 0;
+       if (fs_index_sector < N_SECTORS - 1)
+               ++fs_index_sector;
+       else
+               fs_index_sector = 0;
+}
+
+inline void fs_index_next_file (void)
+{
+       if (fs_index_fileid < N_FILES_IN_SECTOR-1)
+               ++fs_index_fileid;
+       else
+               fs_index_next_sector();
+}
+
+/* filesystem operations, low level */
+
+uint8_t  fs_create_file (
+       const uint8_t sector,
+       const uint8_t id,
+       const uint8_t type
+)
+{
+       uint32_t sector_address;
+       uint32_t file_id_address;
+       uint8_t  flag;
+       
+       // debug_string("create file ",1);
+       // debug_hex(type,1,2,0);
+       // debug_byte(' ',1);
+       // debug_hex(sector,1,2,0);
+       // debug_byte(':',1);
+       // debug_hex(id,1,1,0);
+       // debug_string("\r\n",1);
+       
+       if (!fs_is_file_free(sector, id))
+               return 0;
+       
+       sector_address  = fs_expand_address(sector, 0, FILE_OFFSET_FLAG);
+       file_id_address = fs_expand_address(sector, id, FILE_OFFSET_TYPE);
+       flag = ~(1<<id);
+       
+       // debug_string("sector_addr: ",1);
+       // debug_hex(sector_address,1,8,1);
+       // debug_string(" flag: ",1);
+       // debug_hex(flag,1,2,0);
+       // debug_string("\r\nfile_id_addr: ",1);
+       // debug_hex(file_id_address,1,8,1);
+       // debug_string(" type: ",1);
+       // debug_hex(type,1,2,0);
+       // debug_string("\r\n",1);
+       
+       fs_spi_write_enable();
+       fs_spi_program(sector_address, 1, &flag);
+       fs_wait_ready(TIMEOUT_PROGRAM);
+       fs_spi_write_enable();
+       fs_spi_program(file_id_address, 1, &type);
+       fs_wait_ready(TIMEOUT_PROGRAM);
+       
+       return 0xff;
+}
+
+uint8_t  fs_delete_file (
+       const uint8_t sector,
+       const uint8_t id
+)
+{
+       uint32_t sector_address;
+       uint32_t file_id_address;
+       uint8_t  flag;
+       uint8_t  type = FILE_DELETED;
+       
+       sector_address  = fs_expand_address(sector, 0, FILE_OFFSET_FLAG);
+       file_id_address = fs_expand_address(sector, id, FILE_OFFSET_TYPE);
+       flag = ~(1<<id);
+       
+       // debug_string("delete file ",1);
+       // debug_hex(type,1,2,0);
+       // debug_byte(' ',1);
+       // debug_hex(sector,1,2,0);
+       // debug_byte(':',1);
+       // debug_hex(id,1,1,0);
+       // debug_string("\r\nsector_addr: ",1);
+       // debug_hex(sector_address,1,8,1);
+       // debug_string(" flag: ",1);
+       // debug_hex(flag,1,2,0);
+       // debug_string("\r\nfile_id_addr: ",1);
+       // debug_hex(file_id_address,1,8,1);
+       // debug_string(" type: ",1);
+       // debug_hex(type,1,2,0);
+       // debug_string("\r\n",1);
+       
+       fs_spi_write_enable();
+       fs_spi_program(sector_address, 1, &flag);
+       fs_wait_ready(TIMEOUT_PROGRAM);
+       fs_spi_write_enable();
+       fs_spi_program(file_id_address, 1, &type);
+       fs_wait_ready(TIMEOUT_PROGRAM);
+       
+       return 0xff;
+}
+
+uint8_t  fs_duplicate_file (
+       const uint8_t src_sector,
+       const uint8_t src_id,
+       const uint8_t dst_sector,
+       const uint8_t dst_id
+)
+{
+       uint32_t src_address_0, src_address_1;
+       uint32_t dst_address_0, dst_address_1;
+       uint32_t dst_sector_address;
+       uint8_t  data[256];
+       
+       // debug_string("duplicate file ",1);
+       // debug_byte(' ',1);
+       // debug_hex(src_sector,1,2,0);
+       // debug_byte(':',1);
+       // debug_hex(src_id,1,1,0);
+       // debug_byte(' ',1);
+       // debug_hex(dst_sector,1,2,0);
+       // debug_byte(':',1);
+       // debug_hex(dst_id,1,1,0);
+       // debug_string("\r\n",1);
+       
+       if (!fs_is_file_free(dst_sector, dst_id))
+               return 0;
+       
+       src_address_0 = fs_expand_address(src_sector, src_id, 0);
+       src_address_1 = src_address_0 + 256;
+       dst_address_0 = fs_expand_address(dst_sector, dst_id, 0);
+       dst_address_1 = dst_address_0 + 256;
+       dst_sector_address = fs_expand_address(dst_sector, 0, FILE_OFFSET_FLAG);
+       
+       // debug_string("src_addr: ",1);
+       // debug_hex(src_address_0,1,8,1);
+       // debug_byte(' ',1);
+       // debug_hex(src_address_1,1,8,1);
+       // debug_string("\r\ndst_addr: ",1);
+       // debug_hex(dst_address_0,1,8,1);
+       // debug_byte(' ',1);
+       // debug_hex(dst_address_1,1,8,1);
+       // debug_string("\r\ndst_sect_addr: ",1);
+       // debug_hex(dst_sector_address,1,8,1);
+       // debug_string("\r\n",1);
+       
+       fs_spi_read(src_address_0, 256, data);
+       data[FILE_OFFSET_FLAG] = ~ (1<<dst_id);
+       fs_spi_write_enable();
+       fs_spi_program(dst_sector_address, 1, data+FILE_OFFSET_FLAG);
+       fs_wait_ready(TIMEOUT_PROGRAM);
+       fs_spi_write_enable();
+       fs_spi_program(dst_address_0, 256, data);
+       fs_wait_ready(TIMEOUT_PROGRAM);
+       fs_spi_read(src_address_1, 256, data);
+       fs_spi_write_enable();
+       fs_spi_program(dst_address_1, 256, data);
+       fs_wait_ready(TIMEOUT_PROGRAM);
+       fs_spi_write_disable();
+       
+       return 0xff;
+}
+
+inline void fs_erase_sector(const uint8_t sector)
+{
+       uint32_t address;
+       
+       address = fs_expand_address(sector, 0, 0);
+       
+       // debug_string("erase sector ",1);
+       // debug_hex(sector,1,2,0);
+       // debug_string("\r\n",1);
+       
+       fs_spi_write_enable();
+       fs_spi_erase_4k(address);
+       fs_wait_ready(TIMEOUT_ERASE);
+}
+
+void  fs_format (void)
+{
+       uint16_t status;
+       
+       fs_spi_write_enable ();
+       fs_spi_erase_chip();
+       fs_wait_ready(TIMEOUT_FORMAT);
+       
+       if(fs_index0 < 0)
+       {
+               fs_index0 = 0;
+               fs_index1 = 1;
+       }
+       else
+       {
+               ++fs_index0;
+               if (fs_index0 >= N_SECTORS - 1)
+                       fs_index0 = 0;
+               fs_index1 = fs_index0 + 1;
+       }
+       
+       fs_create_file(fs_index0, 0, FILE_INDEX_0);
+       fs_create_file(fs_index0, 1, FILE_INDEX_0);
+       fs_create_file(fs_index0, 2, FILE_INDEX_0);
+       fs_create_file(fs_index0, 3, FILE_INDEX_0);
+       fs_create_file(fs_index0, 4, FILE_INDEX_0);
+       fs_create_file(fs_index0, 5, FILE_INDEX_0);
+       fs_create_file(fs_index0, 6, FILE_INDEX_0);
+       fs_create_file(fs_index0, 7, FILE_INDEX_0);
+       
+       fs_create_file(fs_index1, 0, FILE_INDEX_1);
+       fs_create_file(fs_index1, 1, FILE_INDEX_1);
+       fs_create_file(fs_index1, 2, FILE_INDEX_1);
+       fs_create_file(fs_index1, 3, FILE_INDEX_1);
+       fs_create_file(fs_index1, 4, FILE_INDEX_1);
+       fs_create_file(fs_index1, 5, FILE_INDEX_1);
+       fs_create_file(fs_index1, 6, FILE_INDEX_1);
+       fs_create_file(fs_index1, 7, FILE_INDEX_1);
+}
+
+uint8_t fs_read_record (
+       const uint8_t   index,
+       const uint8_t   block,
+       const uint8_t   record,
+       uint8_t * const page,
+       uint8_t * const id,
+       uint8_t * const sector,
+       uint8_t * const fileid
+)
+{
+       uint8_t  status;
+       uint32_t address;
+       uint8_t  data[N_RECORD_BYTES];
+       
+       address = fs_expand_record_address(index, block, record);
+       
+       fs_spi_read(address, N_RECORD_BYTES, data);
+       
+       status = data[RECORD_OFFSET_ID] & RECORD_FLAGS;
+       if (status == RECORD_ACTIVE)
+       {
+               *page  = data[RECORD_OFFSET_PAGE];
+               *id    =(data[RECORD_OFFSET_ID] & PAGEID_MASK) >> PAGEID_SHIFT;
+               *sector= data[RECORD_OFFSET_SECTOR];
+               *fileid=(data[RECORD_OFFSET_ID] & SECTRID_MASK) >> SECTRID_SHIFT;
+       }
+       return status;
+}
+
+void fs_write_record (
+       const uint8_t index,
+       const uint8_t block,
+       const uint8_t record,
+       const uint8_t page,
+       const uint8_t id,
+       const uint8_t sector,
+       const uint8_t fileid
+)
+{
+       uint32_t address;
+       uint8_t  data[N_RECORD_BYTES];
+       
+       // debug_string("write record ",1);
+       // debug_hex(index,1,1,0);
+       // debug_byte(':',1);
+       // debug_hex(block,1,1,0);
+       // debug_byte(':',1);
+       // debug_hex(record,1,2,0);
+       // debug_byte(' ',1);
+       // debug_hex(page,1,2,0);
+       // debug_byte(':',1);
+       // debug_hex(id,1,1,0);
+       // debug_byte(' ',1);
+       // debug_hex(sector,1,2,0);
+       // debug_byte(':',1);
+       // debug_hex(fileid,1,1,0);
+       // debug_string("\r\n",1);
+       
+       address = fs_expand_record_address(index, block, record);
+       
+       data [RECORD_OFFSET_ID] =
+               ((id << PAGEID_SHIFT) & PAGEID_MASK) |
+               ((fileid << SECTRID_SHIFT) & SECTRID_MASK) |
+               (RECORD_FLAGS & RECORD_ACTIVE);
+       data [RECORD_OFFSET_SECTOR] = sector;
+       data [RECORD_OFFSET_PAGE] = page;
+       
+       // debug_string("record_addr: ",1);
+       // debug_hex(address,1,8,1);
+       // debug_string(" data: ",1);
+       // debug_hex(data[0],1,2,0);
+       // debug_byte(' ',1);
+       // debug_hex(data[1],1,2,0);
+       // debug_byte(' ',1);
+       // debug_hex(data[2],1,2,0);
+       // debug_string("\r\n",1);
+       
+       fs_spi_write_enable();
+       fs_spi_program(address, N_RECORD_BYTES, data);
+       fs_wait_ready(TIMEOUT_PROGRAM);
+}
+
+void fs_delete_record (
+       const uint8_t index,
+       const uint8_t block,
+       const uint8_t record
+)
+{
+       uint32_t address;
+       uint8_t  data[N_RECORD_BYTES] = {[0 ... (N_RECORD_BYTES-1)] = RECORD_DELETED};
+       
+       // debug_string("delete record ",1);
+       // debug_hex(index,1,1,0);
+       // debug_byte(':',1);
+       // debug_hex(block,1,1,0);
+       // debug_byte(':',1);
+       // debug_hex(record,1,2,0);
+       // debug_string("\r\n",1);
+       
+       address = fs_expand_record_address(index, block, record);
+       
+       // debug_string("record_addr: ",1);
+       // debug_hex(address,1,8,1);
+       // debug_string(" data: ",1);
+       // debug_hex(data[0],1,2,0);
+       // debug_byte(' ',1);
+       // debug_hex(data[1],1,2,0);
+       // debug_byte(' ',1);
+       // debug_hex(data[2],1,2,0);
+       // debug_string("\r\n",1);
+       
+       fs_spi_write_enable();
+       fs_spi_program(address, N_RECORD_BYTES, data);
+       fs_wait_ready(TIMEOUT_PROGRAM);
+}
+
+/* filesystem operations, high level */
+
+void fs_get_file_name (
+       const uint8_t   sector,
+       const uint8_t   id,
+       uint8_t * const name
+)
+{
+       uint32_t address;
+       
+       address = fs_expand_address(sector, id, FILE_OFFSET_NAME);
+       fs_spi_read(address, N_NAME, name);
+}
+
+void fs_load_page (const uint8_t new_page)
+{
+       uint8_t index, block, record;
+       uint8_t status, page, id, sector, fileid;
+       
+       // debug_string("FS LOAD PAGE\r\n",1);
+       
+       if (new_page == fs_page)
+       {
+               // debug_string("no change\r\n",1);
+               return;
+       }
+       
+       fs_page = -1;
+       fs_page_flags = 0;
+       for (id = 0; id < N_FILES_ON_PAGE; ++id)
+               fs_page_names[id][0] = '\0';
+       
+       if ((fs_index0 < 0) || (fs_index1 < 0))
+       {
+               // debug_string("no index\r\n",1);
+               return;
+       }
+       
+       for (index = 0; index<2; ++index)
+       {
+               for (block = 0; block < N_FILES_IN_SECTOR; ++block)
+               {
+                       for (record = 0; record < N_INDEX_RECORDS; ++record)
+                       {
+                               // debug_string("i=",1);
+                               // debug_dec(index,1,1);
+                               // debug_string(" b=",1);
+                               // debug_dec(block,1,1);
+                               // debug_string(" r=",1);
+                               // debug_dec(record,1,3);
+                               // debug_string(" : ",1);
+                               
+                               status = fs_read_record(
+                                       index, block, record,
+                                       &page, &id,
+                                       &sector, &fileid
+                               );
+                               // debug_hex(status,1,2,0);
+                               // debug_string("\r\n",1);
+                               
+                               if (status == RECORD_FREE)
+                                       goto fs_load_page__after_scan;
+                               if (status != RECORD_ACTIVE)
+                                       continue;
+                               if (page == new_page)
+                               {
+                                       fs_page_indexes[id] = index;
+                                       fs_page_indexblocks[id] = block;
+                                       fs_page_indexrecords[id] = record;
+                                       fs_page_sectors[id] = sector;
+                                       fs_page_fileids[id] = fileid;
+                                       fs_get_file_name(sector, fileid, fs_page_names[id]);
+                                       fs_page_flags |= 1<<id;
+                                       if (fs_page_flags == PAGE_FLAGS)
+                                               goto fs_load_page__after_scan;
+                               }
+                       }
+               }
+       }
+       fs_load_page__after_scan:
+       // for (id = 0; id < FILES_ON_PAGE; ++i)
+       // {
+               // if (!(fs_page_flags & (1<<id)))
+                       // fs_page_names[id][0] = '\0';
+       // }
+       fs_page = new_page;
+}
+
+void  fs_store_voice (
+       const uint8_t  id,
+       const uint8_t * const name,
+       const uint32_t A,
+       const uint32_t D,
+       const uint32_t S,
+       const uint32_t R,
+       const uint8_t * const wave
+)
+{
+       uint8_t overwrite;
+       int16_t i;
+       uint32_t address;
+       uint8_t data[FILE_OFFSET_NAME - FILE_OFFSET_START];
+       uint8_t opt_sector;
+       
+       if ((fs_index0 < 0) || (fs_index1 < 0))
+               return;
+       
+       overwrite =  fs_page_flags & (1<<id);
+       
+       for (i=0; ;++i)
+       {
+               if (i >= (N_SECTORS * N_FILES_IN_SECTOR))
+               {
+                       fs_index = -1;
+                       fs_index0 = -1;
+                       fs_index1 = -1;
+                       debug_string("FS FULL!\r\n", 1);
+                       gui_trigger_error(GUI_ERRF_FS);
+                       return;
+               }
+               if (fs_create_file(fs_index_sector, fs_index_fileid, FILE_NORMAL))
+                       break;
+               fs_index_next_file();
+       }
+       address = fs_expand_address(fs_index_sector, fs_index_fileid, 0);
+       
+       data[FILE_OFFSET_PAGE - FILE_OFFSET_START] = fs_page;
+       data[FILE_OFFSET_ID - FILE_OFFSET_START] = id;
+       *((uint32_t*)&(data[FILE_OFFSET_ADSR + FILE_OFFSET_ADSR_A - FILE_OFFSET_START])) = A;
+       *((uint32_t*)&(data[FILE_OFFSET_ADSR + FILE_OFFSET_ADSR_D - FILE_OFFSET_START])) = D;
+       *((uint32_t*)&(data[FILE_OFFSET_ADSR + FILE_OFFSET_ADSR_S - FILE_OFFSET_START])) = S;
+       *((uint32_t*)&(data[FILE_OFFSET_ADSR + FILE_OFFSET_ADSR_R - FILE_OFFSET_START])) = R;
+       
+       fs_spi_write_enable();
+       fs_spi_program(address + FILE_OFFSET_START, FILE_OFFSET_NAME - FILE_OFFSET_START, data);
+       fs_wait_ready(TIMEOUT_PROGRAM);
+       
+       fs_spi_write_enable();
+       fs_spi_program(address + FILE_OFFSET_NAME, N_NAME, name);
+       fs_wait_ready(TIMEOUT_PROGRAM);
+       
+       fs_spi_write_enable();
+       fs_spi_program(address + FILE_OFFSET_WAVE, N_SAMPLE, wave);
+       fs_wait_ready(TIMEOUT_PROGRAM);
+       
+       
+       if (overwrite)
+       {
+               fs_delete_record(
+                       fs_page_indexes[id],
+                       fs_page_indexblocks[id],
+                       fs_page_indexrecords[id]
+               );
+               fs_delete_file(
+                       fs_page_sectors[id],
+                       fs_page_fileids[id]
+               );
+               opt_sector = fs_page_sectors[id];
+       }
+       
+       fs_write_record(
+               fs_index,
+               fs_index_block,
+               fs_index_record,
+               fs_page,
+               id,
+               fs_index_sector,
+               fs_index_fileid
+       );
+       
+       fs_page_flags |= 1<<id;
+       fs_page_indexes[id] = fs_index;
+       fs_page_indexblocks[id] = fs_index_block;
+       fs_page_indexrecords[id] = fs_index_record;
+       fs_page_sectors[id] = fs_index_sector;
+       fs_page_fileids[id] = fs_index_fileid;
+       memcpy(fs_page_names[id],name, N_NAME);
+       
+       fs_index_next_file();
+       fs_index_next_record();
+       
+       if (overwrite)
+               fs_optimise_sector(opt_sector);
+       
+       fs_optimise_index();
+}
+
+uint8_t  fs_load_voice (
+       const uint8_t    id,
+       uint32_t * const A,
+       uint32_t * const D,
+       uint32_t * const S,
+       uint32_t * const R,
+       uint8_t  * const wave
+)
+{
+       int16_t i;
+       uint32_t address;
+       uint8_t data[FILE_OFFSET_NAME - FILE_OFFSET_ADSR];
+       
+       if ((fs_index0 < 0) || (fs_index1 < 0))
+               return 0;
+       
+       if (!(fs_page_flags & (1<<id)))
+               return 0;
+       
+       address = fs_expand_address(fs_page_sectors[id], fs_page_fileids[id], 0);
+       
+       fs_spi_read(address + FILE_OFFSET_ADSR, FILE_OFFSET_NAME - FILE_OFFSET_ADSR, data);
+       
+       *A = *((uint32_t*)&(data[FILE_OFFSET_ADSR_A]));
+       *D = *((uint32_t*)&(data[FILE_OFFSET_ADSR_D]));
+       *S = *((uint32_t*)&(data[FILE_OFFSET_ADSR_S]));
+       *R = *((uint32_t*)&(data[FILE_OFFSET_ADSR_R]));
+       
+       fs_spi_read(address + FILE_OFFSET_WAVE, N_SAMPLE, wave);
+       
+       return 0xff;
+}
+
+void fs_optimise_sector (const uint8_t opt_sector)
+{
+       uint8_t  found = 0;
+       uint8_t  deleted = 0;
+       uint8_t  index, block, record;
+       uint8_t  status, page, id, sector, fileid;
+       uint16_t type, i;
+       
+       uint8_t  pages[N_FILES_IN_SECTOR];
+       uint8_t  ids[N_FILES_IN_SECTOR];
+       uint8_t  types[N_FILES_IN_SECTOR];
+       uint8_t  indexes[N_FILES_IN_SECTOR];
+       uint8_t  indexblocks[N_FILES_IN_SECTOR];
+       uint8_t  indexrecords[N_FILES_IN_SECTOR];
+       
+       uint32_t address;
+       
+       // debug_string("optimise_sector ", 1);
+       // debug_hex(opt_sector, 1, 2, 0);
+       // debug_string("\r\n",1);
+       
+       for (fileid = 0; fileid < N_FILES_IN_SECTOR; ++fileid)
+       {
+               type = fs_get_file_type(opt_sector, fileid);
+               // debug_hex(fileid, 1,1,0);
+               // debug_byte(':',1);
+               // debug_hex(type, 1,2,0);
+               if (type != FILE_NORMAL)
+               {
+                       types[fileid] = FILE_DELETED;
+                       found |= (1<<fileid);
+                       if(type != FILE_FREE)
+                       {
+                               ++deleted;
+                               // debug_byte('+',1);
+                       }
+               }
+               else
+                       types[fileid] = type;
+               // debug_byte(' ',1);
+       }
+       
+       // debug_string("\r\n deleted=",1);
+       // debug_hex(deleted,1,1,0);
+       // debug_string("\r\n",1);
+       
+       if (deleted <= N_MAX_DELETED)
+               return;
+       
+       if (fs_page >= 0)
+       {
+               for (id = 0; id < N_FILES_ON_PAGE; ++id)
+               {
+                       if (fs_page_sectors[id] == opt_sector)
+                       {
+                               fileid = fs_page_fileids[id];
+                               found |= (1<<fileid);
+                               pages[fileid] = fs_page;
+                               ids[fileid] = id;
+                               indexes[fileid] = fs_page_indexes[id];
+                               indexblocks[fileid] = fs_page_indexblocks[id];
+                               indexrecords[fileid] = fs_page_indexrecords[id];
+                       }
+               }
+       }
+       
+       if (found == SECTOR_FLAGS)
+               goto fs_optimise_sector__after_scan;
+       
+       for (index = 0; index < 2; ++index)
+       {
+               for (block = 0; block < N_FILES_IN_SECTOR; ++block)
+               {
+                       for (record = 0; record < N_INDEX_RECORDS; ++record)
+                       {
+                               status = fs_read_record(
+                                       index, block, record,
+                                       &page, &id,
+                                       &sector, &fileid
+                               );
+                               if (status == RECORD_FREE)
+                                       goto fs_optimise_sector__after_scan;
+                               else if ((status == RECORD_ACTIVE) && (sector == opt_sector))
+                               {
+                                       found |= (1<<fileid);
+                                       pages[fileid] = page;
+                                       ids[fileid] = id;
+                                       indexes[fileid] = index;
+                                       indexblocks[fileid] = block;
+                                       indexrecords[fileid] = record;
+                                       if (found == SECTOR_FLAGS)
+                                               goto fs_optimise_sector__after_scan;
+                               }
+                       }
+               }
+       }
+       fs_optimise_sector__after_scan:
+       
+       // debug_string("found=",1);
+       // debug_hex(found,1,2,0);
+       // debug_string("\r\n",1);
+       
+       if (opt_sector == fs_index_sector)
+               fs_index_next_sector();
+       
+       for (fileid = 0; fileid < N_FILES_IN_SECTOR; ++fileid)
+       {
+               if ((found & (1<<fileid)) && (types[fileid]==FILE_NORMAL))
+               {
+                       for (i = 0; ; ++i)
+                       {
+                               if (i >= (N_SECTORS * N_FILES_IN_SECTOR))
+                               {
+                                       fs_index = -1;
+                                       fs_index0 = -1;
+                                       fs_index1 = -1;
+                                       debug_string("FS FULL!\r\n", 1);
+                                       gui_trigger_error(GUI_ERRF_FS);
+                                       return;
+                               }
+                               if (fs_duplicate_file(opt_sector, fileid, fs_index_sector, fs_index_fileid))
+                                       break;
+                               fs_index_next_file();
+                       }
+                       fs_write_record(
+                               fs_index, fs_index_block, fs_index_record,
+                               pages[fileid], ids[fileid],
+                               fs_index_sector, fs_index_fileid
+                       );
+                       fs_delete_record(indexes[fileid], indexblocks[fileid], indexrecords[fileid]);
+                       if (pages[fileid] == fs_page)
+                       {
+                               id = ids[fileid];
+                               fs_page_indexes[id] = fs_index;
+                               fs_page_indexblocks[id] = fs_index_block;
+                               fs_page_indexrecords[id] = fs_index_record;
+                               fs_page_sectors[id] = fs_index_sector;
+                               fs_page_fileids[id] = fs_index_fileid;
+                       }
+                       fs_index_next_record();
+                       fs_index_next_file();
+               }
+       }
+       
+       fs_erase_sector(opt_sector);
+}
+
+void fs_optimise_index (void)
+{
+       uint16_t i;
+       uint8_t index=0, block=0, record=0;
+       uint8_t newindex=0, newblock=0, newrecord=0;
+       uint8_t status, page, id, sector, fileid;
+       
+       if (!(
+               (fs_index == 1) &&
+               (fs_index_block == N_FILES_IN_SECTOR-1) &&
+               (fs_index_record >= N_INDEX_RECORDS - N_INDEX_MARGIN)
+       ))
+               return;
+       if ((fs_index0 < 0) || (fs_index1 < 0))
+               return;
+       
+       // debug_string("optimise_index\r\n", 1);
+       
+       fs_newindex0 = -1;
+       fs_newindex1 = -1;
+       sector = fs_index_sector;
+       
+       for (i = 0; i< N_SECTORS ; ++i)
+       {
+               if (fs_is_sector_free(sector))
+               {
+                       if (fs_newindex0 <0)
+                               fs_newindex0 = sector;
+                       else
+                       {
+                               fs_newindex1 = sector;
+                               break;
+                       }
+               }
+               if (sector >= N_SECTORS-1)
+                       sector = 0;
+               else
+                       ++sector;
+       }
+       if (fs_newindex1 < 0)
+       {
+               fs_index = -1;
+               fs_index0 = -1;
+               fs_index1 = -1;
+               debug_string("FS FULL!\r\n", 1);
+               gui_trigger_error(GUI_ERRF_FS);
+               return;
+       }
+       
+       for (id =0; id<N_FILES_IN_SECTOR; ++id)
+       {
+               fs_create_file(fs_newindex0, id, FILE_INDEX_0);
+               fs_create_file(fs_newindex1, id, FILE_INDEX_1);
+       }
+       
+       for (index=0; index<2; ++i)
+       {
+               for (block=0; block<N_FILES_IN_SECTOR; ++block)
+               {
+                       for (record=0; record<N_INDEX_RECORDS; ++record)
+                       {
+                               status = fs_read_record(
+                                       index, block, record,
+                                       &page, &id,
+                                       &sector, &fileid
+                               );
+                               if (status == RECORD_FREE)
+                                       goto fs_optimise_index__after_scan;
+                               if (status != RECORD_ACTIVE)
+                                       continue;
+                               fs_write_record(
+                                       newindex | NEW_INDEX, newblock, newrecord,
+                                       page, id,
+                                       sector, fileid
+                               );
+                               //todo: extract to sub?
+                               if (newrecord < N_INDEX_RECORDS-1)
+                                       ++newrecord;
+                               else
+                               {
+                                       newrecord=0;
+                                       if(newblock < N_FILES_IN_SECTOR)
+                                               ++newblock;
+                                       else
+                                       {
+                                               newblock = 0;
+                                               ++newindex;
+                                       }
+                               }
+                       }
+               }
+       }
+       fs_optimise_index__after_scan:
+       
+       fs_erase_sector(fs_index0);
+       fs_erase_sector(fs_index1);
+       
+       fs_index0 = fs_newindex0;
+       fs_index1 = fs_newindex1;
+       fs_index = newindex;
+       fs_index_block = newblock;
+       fs_index_record = newrecord;
+}
+
+inline void setup_fs (void)
+{
+       FS_HOLD = 1;
+       FS_HOLD__PM = 0;
+       FS_HOLD = 1;
+       
+       FS_WP = 1;
+       FS_WP__PM = 0;
+       FS_WP = 1;
+       
+       FS_CS = 1;
+       FS_CS__PM = 0;
+       FS_CS__PU = 0;
+       FS_CS = 1;
+       
+       LED_U = 0;
+       LED_U__PM = 0;
+       
+       PER0 |= 0b00001000; //enable serial array unit 1
+       
+       SPS1 = (SPS1 & 0xff0f) | 0x0000; //CK01 = fclk/1 = 32MHz
+       
+       FS__SMR = 0b1000000000100000; //CK01, int. on tx end
+       FS__SCR = 0b1111000000000111; //T/R enable, mode 0, MSB first, 8 bit
+       FS__SDR = 0x0400; //32MHz / 6 = 5+1/3 MHz
+       
+       SO1 &= ~0b0000001000000010; //ck, so = 0 (mode^!)
+       
+       FS__SOE = 1;
+       
+       FS_SCK__PM = 0;
+       FS_SCK__PU = 0;
+       FS_SCK = 1;
+       
+       FS_MOSI__PM = 0;
+       FS_MOSI__PU = 0;
+       FS_MOSI = 1;
+       
+       FS_MISO__PM = 1;
+       FS_MISO__POM = 0;
+       FS_MISO__PU = 0;
+       
+       FS__SS = 1;
+}
+
+inline void init_fs (void)
+{
+       uint8_t index, block, record;
+       uint8_t status, page, id, sector, fileid;
+       
+       fs_spi_write_disable ();
+       
+       fs_locate_index();
+       if((fs_index0<0)||(fs_index1<0))
+       {
+               fs_index = -1;
+               debug_string("FS INVALID!\r\n", 1);
+               gui_trigger_error(GUI_ERRF_FS);
+               return;
+       }
+       fs_scan_index();
+       
+       debug_string("INIT_FS\r\nindex0=",1);
+       debug_dec(fs_index0,1,1);
+       debug_string("\r\nindex1=",1);
+       debug_dec(fs_index1,1,1);
+       debug_string("\r\nindex=",1);
+       debug_dec(fs_index,1,1);
+       debug_string("\r\nblock=",1);
+       debug_dec(fs_index_block,1,1);
+       debug_string("\r\nrecord=",1);
+       debug_dec(fs_index_record,1,1);
+       debug_string("\r\nsector=",1);
+       debug_dec(fs_index_sector,1,1);
+       debug_string("\r\nfileid=",1);
+       debug_dec(fs_index_fileid,1,1);
+       debug_string("\r\n",1);
+       
+       // debug_sector(fs_index0);
+       // debug_sector(fs_index1);
+}
+
+void  debug_file (
+       const uint8_t sector,
+       const uint8_t id
+)
+{
+       uint32_t address;
+       uint16_t i, j;
+       uint8_t  data[16];
+       
+       address = fs_expand_address(sector,id,0);
+       
+       debug_string ("file ",1);
+       debug_hex(sector,1,2,0);
+       debug_byte(':',1);
+       debug_hex(id,1,1,0);
+       debug_string("\r\n",1);
+       debug_hex(address,1,5,1);
+       debug_string("\r\n",1);
+       
+       for(i=0; i<FILE_OFFSET_NEXT; i+=16, address+=16)
+       {
+               fs_spi_read(address,16,data);
+               for(j=0; j<16; ++j)
+               {
+                       if(j)
+                               debug_byte(' ',1);
+                       debug_hex(data[j],1,2,0);
+               }
+               debug_string("\r\n",1);
+       }
+}
+
+void  debug_sector (const uint8_t sector)
+{
+       uint8_t id;
+       
+       for(id = 0; id < N_FILES_IN_SECTOR; ++id)
+               debug_file(sector,id);
+}
diff --git a/fs.h b/fs.h
new file mode 100644 (file)
index 0000000..0a1871f
--- /dev/null
+++ b/fs.h
@@ -0,0 +1,515 @@
+/* STORAGE OF SETTINGS */
+/*
+Copyright 2021 Balthasar Szczepański
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include <stdint.h>
+#include "klavirko-ui.h"
+
+// Our SPI flash memory is AT25DF081A-SSH-T
+
+// SPI flash opcodes
+
+#define FS_READ_2    0x1B
+#define FS_READ_1    0x0B
+#define FS_READ      0x03
+#define FS_READ_DUAL 0x3B
+
+#define FS_ERASE_4K   0x20
+#define FS_ERASE_32K  0x52
+#define FS_ERASE_64K  0xD8
+#define FS_ERASE_CHIP 0x60
+// #define FS_ERASE_CHIP 0xC7
+
+#define FS_PROGRAM      0x02
+#define FS_PROGRAM_DUAL 0xA2
+
+#define FS_WRITE_ENABLE           0x06
+#define FS_WRITE_DISABLE          0x04
+#define FS_PROTECT_SECTOR         0x36
+#define FS_UNPROTECT_SECTOR       0x39
+#define FS_READ_SECTOR_PROTECTION 0x3C
+
+#define FS_SECTOR_LOCKDOWN        0x33
+#define FS_FREEZE_SECTOR_LOCKDOWN 0x34
+#define FS_READ_SECTOR_LOCKDOWN   0x35
+#define FS_PROGRAM_OTP_SECURITY   0x9B
+#define FS_READ_OTP_SECURITY      0x77
+
+#define FS_READ_STATUS    0x05
+#define FS_WRITE_STATUS_1 0x01
+#define FS_WRITE_STATUS_2 0x31
+
+#define FS_RESET         0xF0
+#define FS_RESET_CONFIRM 0xD0
+#define FS_READ_ID       0x9F
+#define FS_POWER_DOWN    0xB9
+#define FS_POWER_RESUME  0xAB
+
+#define FS_READ_FLAG  0x01
+#define FS_WRITE_FLAG 0x02
+
+// SPI flash status bits
+
+#define FS_BSY  0x0001 // Busy Status
+#define FS_RDY  FS_BSY // Ready Status (reversed)
+#define FS_WEL  0x0002 // Write Enable Latch Status
+#define FS_SWP  0x000C // Software Protection Status
+#define FS_WPP  0x0010 // Write Protect Pin Status
+#define FS_EPE  0x0020 // Erase/Program Error
+#define FS_SPRL 0x0080 // Sector Protection Registers Locked
+#define FS_BSY2 0x0100 // Busy Status
+#define FS_RDY2 FS_BSY2// Ready Status (reversed)
+#define FS_SLE  0x0800 // Sector Lockdown Enabled
+#define FS_RSTE 0x1000 // Reset Enabled
+
+#define FS_NULL ((uint8_t *)0x00100000) //address outside memory map
+
+/*
+       max wait times according to datasheet:
+       
+       program page 3 ms
+       erase 4K   200 ms
+       erase chip  28 s
+       
+       we will use higher timeouts
+*/
+
+#define TIMEOUT_PROGRAM  100
+#define TIMEOUT_ERASE   1000
+#define TIMEOUT_FORMAT 60000
+
+/*
+FILESYSTEM:
+1MB
+256 sectors
+
++-------+-----------+
+| 00000 |           |
+|       | SECTOR 0  |
+| 00FFF |           |
++-------+-----------+
+| 01000 |           |
+|       | SECTOR 1  |
+| 01FFF |           |
++-------+-----------+
+|      ...          | 
++-------+-----------+
+| FF000 |           |
+|       | SECTOR 255|
+| FFFFF |           |
++-------+-----------+
+
+SECTOR:
+4kB
+8 files
+
++-----+-----+----+
+| 000 |FLAGS|    |
++-----+-----+    |
+| 001 |          |
+|     |  FILE 0  |
+| 1FF |          |
++-----+--------+-+
+| 200 |RESERVED| |
++-----+--------+ |
+| 201 |          |
+|     |  FILE 1  |
+| 3FF |          |
++-----+----------+
+|    ...         |
++-----+--------+-+
+| E00 |RESERVED| |
++-----+--------+ |
+| F01 |          |
+|     |  FILE 7  |
+| FFF |          |
++-----+----------+
+
+FLAGS:
+76543210
+1 - free
+0 - used
+
+FILE:
+512 bytes
+
++-----+---------+
+| 000 |RESERVED |
++-----+---------+
+| 001 |FILE TYPE|
++-----+---------+
+| 002 |         |
+|     |  DATA   |
+| 1FF |         |
++-----+---------+
+
+VOICE CONFIG FILE:
+
++-----+---------+
+| 000 |RESERVED |
++-----+---------+
+| 001 |FILE TYPE|
++-----+---------+
+| 002 |  PAGE   |
++-----+---------+
+| 003 |   ID    |
++-----+---------+
+| 004 |         |
+|     |    A    |
+| 007 |         |
++-----+---------+
+| 008 |         |
+|     |    D    |
+| 00B |         |
++-----+---------+
+| 00C |         |
+|     |    S    |
+| 00F |         |
++-----+---------+
+| 010 |         |
+|     |    R    |
+| 013 |         |
++-----+---------+
+| 014 |         |
+|     |  NAME   |
+| 02F |         |
++-----+---------+
+| 030 |         |
+|     |RESERVED |
+| 0FF |         |
++-----+---------+
+| 100 |         |
+|     |WAVEFORM |
+| 1FF |         |
++-----+---------+
+
+INDEX FILE:
+
++-----+----------+
+| 000 | RESERVED |
++-----+----------+
+| 001 |FILE TYPE |
++-----+----------+
+| 002 |          |
+|     | RECORD 0 |
+| 004 |          |
++-----+----------+
+| 005 |          |
+|     | RECORD 1 |
+| 007 |          |
++-----+----------+
+|    ...         |
++-----+----------+
+| 1fd |          |
+|     |RECORD 169|
+| 1FF |          |
++-----+----------+
+
+INDEX LOGICALLY:
+
++---------+---------+------------+
+|SECTOR 0 | BLOCK 0 | RECORD 0   |
+|         |         | ...        |
+|         |(1 file) | RECORD 169 |
+|         +---------+------------+ 
+|         | BLOCK 1 | RECORD 170 |
+|         |         | ...        |
+|         |(1 file) | RECORD 339 |
+|         +---------+------------+ 
+|         |        ...           |
+|         +---------+------------+ 
+|         | BLOCK 7 | RECORD 1190|
+|         |         | ...        |
+|         |(1 file) | RECORD 1359|
++---------+---------+------------+ 
+|SECTOR 1 | BLOCK 8 | RECORD 1360|
+|         |         | ...        |
+|         |(1 file) | RECORD 1529|
+|         +---------+------------+ 
+|         |        ...           |
+|         +---------+------------+ 
+|         | BLOCK 15| RECORD 2550|
+|         |         | ...        |
+|         |(1 file) | RECORD 2719|
++---------+---------+------------+ 
+
+INDEX RECORD:
+
+    | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
++---+---+---+---+---+---+---+---+---+
+| 0 | A |ID ON PAGE | B |ID IN SECTR|
++---+---+-----------+---+-----------+
+| 1 |            SECTOR             |
++---+-------------------------------+
+| 2 |             PAGE              |
++---+-------------------------------+
+
+AB:
+11 - record free
+01 - record active
+00 - record deleted
+
+*/
+
+#define N_SECTORS       256
+#define N_PAGES         256
+#define N_FILES_IN_SECTOR 8
+#define N_FILES_ON_PAGE   6
+#define N_INDEX_RECORDS 170
+#define N_RECORD_BYTES    3
+
+#define N_NAME 28
+
+#define N_INDEX_MARGIN 24
+#define N_MAX_DELETED   2
+
+/* This is a tradeoff between efficiency of memory space usage
+and frequency of defragmentation.
+With bigger value we're erasing and relocating less often,
+which extends the life of the flash memorym
+but we are wasting more space for deleted files.
+With the value of 2, even in the worst case, where each sector
+contains 2 deleted files we still have place for (8-2)*256=1536 files,
+which is exactly as much as we need.
+We can be 100% sure that we will not run out of space.
+We erase a sector 3 times less often than we delete files.
+We erase a sector when it has 3/8 of it's area deleted.
+With higher values it would still be statistically very unlikely
+but not impossible. To make it impossible I would have to do some
+checking of how much space is still free vs how much should be free
+for the remaining settings and react dynamically.
+But I prefer to stick with the simple spolution which is good enough.
+
+ETA: In the WORST case we will still miss 2 + 1 sectors
+(index + relocation place) = 24 settings.
+EXTREMELY unlikely,
+but I might still need to come up with something clever.
+Wait for it.
+
+ETAA: How about limit the number of pages to 250,
+which gives 1500 settings only?
+*/
+
+#define RECORD_OFFSET_ID     0
+#define RECORD_OFFSET_SECTOR 1
+#define RECORD_OFFSET_PAGE   2
+
+#define INDEX_OFFSET_RECORDS 2
+
+#define NEW_INDEX 0b10000000
+
+#define FILE_OFFSET_FLAG   0x000
+#define FILE_OFFSET_TYPE   0x001
+#define FILE_OFFSET_START  0x002
+#define FILE_OFFSET_PAGE   0x002
+#define FILE_OFFSET_ID     0x003
+#define FILE_OFFSET_ADSR   0x004
+#define FILE_OFFSET_ADSR_A   0x0
+#define FILE_OFFSET_ADSR_D   0x4
+#define FILE_OFFSET_ADSR_S   0x8
+#define FILE_OFFSET_ADSR_R   0xC
+#define FILE_OFFSET_NAME   0x014
+#define FILE_OFFSET_UNUSED 0x030
+#define FILE_OFFSET_WAVE   0x100
+#define FILE_OFFSET_NEXT   0x200
+
+#define PAGEID_SHIFT  4
+#define PAGEID_MASK   0b01110000
+#define SECTRID_SHIFT 0
+#define SECTRID_MASK  0b00000111
+
+#define FILE_FREE     0b11111111
+#define FILE_RESERVED 0b11111110
+#define FILE_INDEX_0  0b01010100
+#define FILE_INDEX_1  0b10101010
+#define FILE_NORMAL   0b00111110
+#define FILE_DELETED  0b00000000
+#define FILE_INVALID  0xff00
+
+#define RECORD_FLAGS   0b10001000
+#define RECORD_FREE    0b10001000
+#define RECORD_ACTIVE  0b00001000
+#define RECORD_DELETED 0b00000000
+
+#define PAGE_FLAGS   0b00111111
+#define SECTOR_FLAGS 0b11111111
+
+#define FILE_BITS   9
+#define FILEID_BITS 3
+#define SECTOR_BITS 8
+
+extern int16_t fs_page;
+extern uint8_t fs_page_flags;
+extern int16_t fs_page_sectors[N_FILES_ON_PAGE];
+extern int16_t fs_page_fileids[N_FILES_ON_PAGE];
+extern uint8_t fs_page_names[N_FILES_ON_PAGE][N_NAME+1];
+
+inline void fs_watchdog (void) LOWTEXT;
+
+// SPI flash handling
+inline uint8_t fs_spi_1b (const uint8_t data); // send & receive single byte
+void fs_spi( // execute single command 
+       const uint8_t   opcode,
+       const uint16_t  address_bytes,
+       const uint16_t  dummy_bytes,
+       const uint16_t  data_bytes,
+       const uint32_t  address,
+       uint8_t * const read_data,
+       const uint8_t * const write_data,
+       const uint8_t   flag
+);
+
+// SPI flash commands
+inline void     fs_spi_read2 (
+       const uint32_t  address,
+       const uint16_t  n,
+       uint8_t * const data
+);
+inline void     fs_spi_read1 (
+       const uint32_t  address,
+       const uint16_t  n,
+       uint8_t * const data
+);
+inline void     fs_spi_read (
+       const uint32_t  address,
+       const uint16_t  n,
+       uint8_t * const data
+);
+inline void     fs_spi_erase_4k (const uint32_t address);
+inline void     fs_spi_erase_32k (const uint32_t address);
+inline void     fs_spi_erase_64k (const uint32_t address);
+inline void     fs_spi_erase_chip (void);
+inline void     fs_spi_program (
+       const uint32_t  address,
+       const uint16_t  n,
+       const uint8_t * const data
+);
+inline void     fs_spi_write_enable (void);
+inline void     fs_spi_write_disable (void);
+inline void     fs_spi_protect_sector (uint32_t address);
+inline void     fs_spi_unprotect_sector (const uint32_t address);
+inline uint8_t  fs_spi_read_sector_protection (const uint32_t address);
+inline uint16_t fs_spi_read_status (void);
+inline void     fs_spi_write_status_low (const uint8_t status);
+inline void     fs_spi_write_status_high (const uint8_t status);
+inline void     fs_spi_write_status (const uint16_t status);
+inline void     fs_spi_reset (void);
+inline uint32_t fs_spi_read_id (void);
+
+inline void fs_wait_ready (const uint16_t timeout);
+
+// support for filesystem operations
+inline uint32_t fs_expand_address (
+       const uint8_t sector,
+       const uint8_t id,
+       const uint16_t offset
+);
+inline uint32_t fs_expand_record_address (
+       const uint8_t index,
+       const uint8_t block,
+       const uint8_t record
+);
+inline uint8_t  fs_is_sector_free (const uint8_t sector);
+inline uint8_t  fs_is_file_free (
+       const uint8_t sector,
+       const uint8_t id
+);
+uint16_t        fs_get_file_type (
+       const uint8_t sector,
+       const uint8_t id
+);
+inline uint16_t fs_get_index_validation (const uint8_t sector);
+
+// filesystem indexing
+inline void fs_locate_index (void);
+inline void fs_scan_index (void);
+inline void fs_index_next_record (void);
+inline void fs_index_next_sector (void);
+inline void fs_index_next_file (void);
+
+// filesystem operations, low level
+uint8_t     fs_create_file (
+       const uint8_t sector,
+       const uint8_t id,
+       const uint8_t type
+);
+uint8_t     fs_delete_file (
+       const uint8_t sector,
+       const uint8_t id
+);
+uint8_t     fs_duplicate_file (
+       const uint8_t src_sector,
+       const uint8_t src_id,
+       const uint8_t dst_sector,
+       const uint8_t dst_id
+);
+inline void fs_erase_sector (const uint8_t sector);
+void        fs_format (void);
+uint8_t     fs_read_record (
+       const uint8_t   index,
+       const uint8_t   block,
+       const uint8_t   record,
+       uint8_t * const page,
+       uint8_t * const id,
+       uint8_t * const sector,
+       uint8_t * const fileid
+);
+void        fs_write_record (
+       const uint8_t index,
+       const uint8_t block,
+       const uint8_t record,
+       const uint8_t page,
+       const uint8_t id,
+       const uint8_t sector,
+       const uint8_t fileid
+);
+void        fs_delete_record (
+       uint8_t index,
+       uint8_t block,
+       uint8_t record
+);
+
+// filesystem operations, high level
+void    fs_get_file_name(
+       const uint8_t   sector,
+       const uint8_t   id,
+       uint8_t * const name
+);
+void    fs_load_page (const uint8_t new_page);
+void    fs_store_voice (
+       const uint8_t  id,
+       const uint8_t * const name,
+       const uint32_t A,
+       const uint32_t D,
+       const uint32_t S,
+       const uint32_t R,
+       const uint8_t * const wave
+);
+uint8_t fs_load_voice (
+       const uint8_t    id,
+       uint32_t * const A,
+       uint32_t * const D,
+       uint32_t * const S,
+       uint32_t * const R,
+       uint8_t  * const wave
+);
+void    fs_optimise_sector (const uint8_t opt_sector);
+void    fs_optimise_index (void);
+
+// general
+inline void setup_fs (void);
+inline void init_fs (void);
+void        debug_file (
+       const uint8_t sector,
+       const uint8_t id
+);
+void        debug_sector (const uint8_t sector);
diff --git a/gui.c b/gui.c
new file mode 100644 (file)
index 0000000..1d837e7
--- /dev/null
+++ b/gui.c
@@ -0,0 +1,1315 @@
+/* GUI */
+/*
+Copyright 2021, 2022 Balthasar Szczepański
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include "klavirko-ui.h"
+#include "gui.h"
+#include "debug.h"
+#include "wave.h"
+#include "lcd.h"
+#include "main.h"
+#include "fs.h"
+#include "ctrl.h"
+
+volatile uint8_t  button = 0;
+volatile uint8_t  button_old = 0;
+volatile uint8_t  button_new = 0;
+volatile uint16_t button_count = 0;
+volatile uint16_t button_hold_count = 0;
+volatile uint16_t button_y_count = 0;
+
+volatile uint8_t  jog = 0;
+volatile uint8_t  jog_old = 0;
+volatile uint8_t  jog_new = 0;
+
+volatile uint16_t gui_event[N_GUI_EVENT];
+volatile uint8_t  gui_event_r = 0;
+volatile uint8_t  gui_event_w = 0;
+
+         uint8_t  redraw_adsr = 0;
+         uint8_t  redraw_wave = 0;
+         uint8_t  wave_shape[N_SAMPLE];
+         uint8_t  adsr_shape[N_SAMPLE];
+
+         uint8_t  gui_state = GUI_INIT;
+         uint8_t  gui_return_state = GUI_WAVE;
+         uint8_t  gui_errors = 0;
+         uint8_t  gui_accepted_errors = GUI_ERRF_ALL;
+
+         uint8_t  gui_page=0;
+         uint8_t  gui_name[N_NAME+1] = {[0 ... (N_NAME)] = '\0'};
+
+         uint8_t  gui_name_cursor;
+         uint8_t  gui_name_pos;
+         uint8_t  gui_name_maxcursor;
+const    uint8_t *gui_name_list;
+const    uint8_t  gui_list_ABC[N_ABC+1] = LIST_ABC;
+const    uint8_t  gui_list_abc[N_abc+1] = LIST_abc;
+const    uint8_t  gui_list_123[N_123+1] = LIST_123;
+         uint8_t  gui_ignore_event = 0;
+
+         uint8_t  text_tuning[GUI_BUTTONTEXT_TUNING_N] = GUI_BUTTONTEXT_TUNING;
+         uint8_t  text_transpose[GUI_BUTTONTEXT_TRANSPOSE_N] = GUI_BUTTONTEXT_TRANSPOSE;
+         uint8_t  text_midi_in[GUI_BUTTONTEXT_MIDI_IN_N] = GUI_BUTTONTEXT_MIDI_IN;
+         uint8_t  text_midi_out[GUI_BUTTONTEXT_MIDI_OUT_N] = GUI_BUTTONTEXT_MIDI_OUT;
+         uint8_t  text_select[GUI_TITLE_SELECT_N] = GUI_TITLE_SELECT;
+         uint8_t  text_save[GUI_TITLE_SAVE_N] = GUI_TITLE_SAVE;
+
+/* event handling */
+
+inline void add_gui_event(const uint16_t event)
+{
+       if((gui_event_w + 1 == gui_event_r) || (gui_event_w + 1 == gui_event_r + N_GUI_EVENT)) // event buffer is full
+               return;
+       else
+       {
+               // add event, advance pointer
+               gui_event[gui_event_w] = event;
+               if (gui_event_w >= N_GUI_EVENT - 1)
+                       gui_event_w = 0;
+               else
+                       ++gui_event_w;
+       }
+}
+
+inline void reject_gui_events (void)
+{
+       gui_event_r = gui_event_w;
+}
+
+inline void gui_trigger_error (const uint8_t error)
+{
+       gui_errors |= (error & gui_accepted_errors);
+}
+
+inline void gui_ignore_error (const uint8_t error)
+{
+       gui_accepted_errors &= ~error;
+       gui_errors &= ~error;
+}
+
+/* handle changes */
+
+inline void gui_update_adsr (void)
+{
+       redraw_adsr = 1;
+}
+
+inline void gui_update_wave (void)
+{
+       redraw_wave = 1;
+}
+
+inline void gui_lock_wave (void)
+{
+       LED_V = 1;
+       lock_wave();
+}
+
+inline void gui_unlock_wave (void)
+{
+       unlock_wave();
+       update_wave();
+       LED_V = 0;
+}
+
+inline void gui_lock_adsr (void)
+{
+       LED_W = 1;
+       lock_adsr();
+}
+
+inline void gui_unlock_adsr (void)
+{
+       unlock_adsr();
+       update_adsr();
+       LED_W = 0;
+}
+
+inline void gui_switch_wave (void)
+{
+       if (wave_locked)
+               gui_unlock_wave();
+       else
+               gui_lock_wave();
+}
+
+inline void gui_switch_adsr (void)
+{
+       if (adsr_locked)
+               gui_unlock_adsr();
+       else
+               gui_lock_adsr();
+}
+
+/* handling inputs */
+
+inline void int_jog (void) // interrupt from rotary dial
+{
+       jog_old = jog_new;
+       jog_new = GET_JOG();
+       
+       if(!(jog_new & 0x1)) //full position
+       {
+               if(jog_new != jog) //new position, not return
+               {
+                       jog = jog_new;
+                       add_gui_event(EVENT_JOG | ((jog_new - jog_old) & 0x3));
+               }
+       }
+}
+
+inline void int_button_abcdef(void) // interrupt from button ADC
+{
+       uint8_t adc;
+       uint8_t bits;
+       uint8_t press;
+       uint8_t release;
+       
+       // get value
+       adc = ADCRH;
+       
+       // decode to bits, 3 buttons state
+       if (adc <=BUTTON_THR_0)
+               bits = BUTTON_A | BUTTON_B | BUTTON_C;
+       else if (adc <= BUTTON_THR_1)
+               bits = BUTTON_B | BUTTON_C;
+       else if (adc <= BUTTON_THR_2)
+               bits = BUTTON_A | BUTTON_C;
+       else if (adc <= BUTTON_THR_3)
+               bits = BUTTON_C;
+       else if (adc <= BUTTON_THR_4)
+               bits = BUTTON_A | BUTTON_B;
+       else if (adc <= BUTTON_THR_5)
+               bits = BUTTON_B;
+       else if (adc <= BUTTON_THR_6)
+               bits = BUTTON_A;
+       else 
+               bits = 0;
+       
+       button_old = button_new;
+       
+       if (ADS == BUTTON_ABC__ADS) // buttons ABC were read
+       {
+               ADS = BUTTON_DEF__ADS; // select DEF for next time
+               button_new = (button_new & (~BUTTON_ABC)) | bits; // update ABC state
+               
+               // also check button X now
+               if (BUTTON_X__P)
+                       button_new |= BUTTON_X;
+               else
+                       button_new &= ~BUTTON_X;
+       }
+       else // buttond DEF were read
+       {
+               ADS = BUTTON_ABC__ADS; // select ABC for next time
+               button_new = (button_new & (~BUTTON_DEF)) | (bits<<3); // update DEF state
+               
+               // also check button Y now: how many changes were counted?
+               if (button_y_count < 3)
+                       button_new |= BUTTON_Y;
+               else
+                       button_new &= ~BUTTON_Y;
+               button_y_count = 0;
+       }
+       
+       // do button debouncing for pressing and releasing
+       if (button_new == button_old)
+       {
+               if (button_count < BUTTON_DEBOUNCE)
+               {
+                       ++button_count;
+                       if (button_count == BUTTON_DEBOUNCE)
+                       {
+                               button_hold_count = button_count;
+                               press = button_new & (~button);
+                               release = button & (~button_new);
+                               button = button_new;
+                               if (press)
+                                       add_gui_event(EVENT_PRESS | press);
+                               if (release)
+                                       add_gui_event(EVENT_RELEASE | release);
+                       }
+               }
+       }
+       else
+               button_count = 0;
+       
+       // do button debouncing for long hold
+       if (button_hold_count < BUTTON_HOLD)
+       {
+               ++button_hold_count;
+         if (button_hold_count == BUTTON_HOLD)
+               {
+                       if(button)
+                               add_gui_event(EVENT_HOLD | button);
+               }
+       }
+}
+
+inline void int_button_y (void) // interrupt from button Y - square wave input
+{
+       ++button_y_count;
+}
+
+/* general setup */
+
+inline void setup_gui(void)
+{
+       PER0 |= //enable:
+               0b10000000| //interval timer
+               0b00100000; //ADC
+       
+       /* * * SETUP LED V,W * * */
+       
+       LED_V__PM = 0;
+       LED_V = 0;
+       
+       LED_W = 0;
+       LED_W__PM = 0;
+       
+       /* * * SETUP BUTTON Y * * */
+       
+       BUTTON_Y__PM = 1;
+       BUTTON_Y__PU = 0;
+       
+       BUTTON_Y__KRM = 1; //enable key int. detect
+       BUTTON_Y__MK = 0;  //enable int.
+       
+       /* * * SETUP SELECTOR * * */
+       
+       JOG_A__PU = 0;
+       JOG_A__POM = 0;
+       JOG_A__PM = 1;
+       
+       JOG_B__PU = 0;
+       JOG_B__PM = 1;
+       
+       //enable both edges
+       JOG_A__EGP = 1;
+       JOG_A__EGN = 1;
+       JOG_B__EGP = 1;
+       JOG_B__EGN = 1;
+       
+       //enable int.
+       JOG_A__MK = 0;
+       JOG_B__MK = 0;
+       
+       //initial state
+       jog = jog_new = jog_old = GET_JOG();
+       
+       /* * * SETUP INTERVAL TIMER * * */
+       // to use for ADC
+       OSMC = 0b00010000;
+       ITMC = 0x800e; //start, f = 15kHz / 15 = 1kHz;
+       // MK1h_bit.no2 = 0; //enable timer interrupt NOT NEEDED
+       
+       /* * * SETUP BUTTON ADC * * */
+       
+       //pins
+       BUTTON_ABC__PM = 1;
+       BUTTON_DEF__PM = 1;
+       LCD_CONTRAST__PM = 1;
+       ADPC = 0x05;
+       
+       ADM0 = 0b00000000; //mode... 38us...
+       ADM1 = 0b10100011; //oneshot trigger is interval timer
+       ADM2 = 0b00000000; //ref: vdd vss, 10bit.
+       ADS = BUTTON_ABC__ADS; //select channel
+       
+       ADM0 |= 0b00000001; //start ADCE
+       NOP_1us();
+       ADM0 |= 0b10000000; //start ADCS
+       
+       MK1H_bit.no0 = 0; //enable AD int.
+}
+
+inline void init_gui(void)
+{
+       gui_state = GUI_INIT;
+       //TODO
+}
+
+/* state machine */
+
+// WAVE: waveform & envelope live display
+
+inline void gui_gotostate_wave (void)
+{
+       gui_state = GUI_WAVE;
+       gui_update_adsr();
+       gui_update_wave();
+       reject_gui_events();
+}
+
+inline void gui_event_wave (const uint16_t event)
+{
+       if((event & EVENT_BITS) == EVENT_RELEASE)
+       {
+               if (event & BUTTON_X)
+               {
+                       gui_gotostate_options();
+               }
+               else if (event & BUTTON_Y)
+               {
+                       gui_gotostate_select();
+               }
+               else if (event & (BUTTON_A | BUTTON_B))
+               {
+                       gui_switch_wave();
+               }
+               else if (event & (BUTTON_C | BUTTON_D | BUTTON_E ))
+               {
+                       gui_switch_adsr();
+               }
+               else if (event & BUTTON_F)
+               {
+                       if(adsr_locked || wave_locked)
+                       {
+                               gui_unlock_wave();
+                               gui_unlock_adsr();
+                       }
+                       else
+                       {
+                               gui_lock_wave();
+                               gui_lock_adsr();
+                       }
+               }
+       }
+       else if((event & EVENT_BITS) == EVENT_HOLD)
+       {
+               if (event & BUTTON_Y)
+                       gui_gotostate_name(1);
+       }
+}
+
+inline void gui_exec_wave (void) // redraw if needed
+{
+       if (redraw_wave)
+       {
+               redraw_wave = 0;
+               draw_wave();
+       }
+       if (redraw_adsr)
+       {
+               redraw_adsr = 0;
+               draw_adsr();
+       }
+}
+
+// SELECT: voice settings selection
+
+inline void gui_gotostate_select (void)
+{
+       gui_state = GUI_SELECT;
+       draw_select();
+       reject_gui_events();
+}
+
+inline void gui_event_select (const uint16_t event)
+{
+       // uint8_t wave, adsr;
+       uint8_t id;
+       
+       if((event & EVENT_BITS) == EVENT_RELEASE)
+       {
+               if (event & (BUTTON_A | BUTTON_B | BUTTON_C | BUTTON_D | BUTTON_E | BUTTON_F))
+               {
+                       if (event & BUTTON_A)
+                               id = 0;
+                       else if (event & BUTTON_B)
+                               id = 1;
+                       else if (event & BUTTON_C)
+                               id = 2;
+                       else if (event & BUTTON_D)
+                               id = 3;
+                       else if (event & BUTTON_E)
+                               id = 4;
+                       else 
+                               id = 5;
+                       
+                       // adsr = adsr_locked;
+                       // wave = wave_locked;
+                       
+                       gui_lock_adsr();
+                       gui_lock_wave();
+                       
+                       if (fs_load_voice(
+                               id,
+                               &adsr_A,
+                               &adsr_D,
+                               &adsr_S,
+                               &adsr_R,
+                               sample)
+                       ){
+                               ctrl_update_wave();
+                               ctrl_update_adsr();
+                               gui_update_adsr();
+                               gui_update_wave();
+                       }
+                       else
+                       {
+                               // if(!adsr)
+                                       gui_unlock_adsr();
+                               // if(!wave)
+                                       gui_unlock_wave();
+                       }
+               }
+               else if (event & BUTTON_X)
+                       gui_gotostate_options();
+               else if (event & BUTTON_Y)
+                       gui_gotostate_wave();
+       }
+       else if((event & EVENT_BITS) == EVENT_HOLD)
+       {
+               if (event & BUTTON_Y)
+                       gui_gotostate_name(1);
+       }
+       else if((event & EVENT_BITS) == EVENT_JOG)
+       {
+               if ((event & ~EVENT_BITS) == JOG_PLUS)
+                       ++gui_page;
+               else
+                       --gui_page;
+               draw_select();
+               reject_gui_events();
+       }
+}
+
+// NAME: voice setting name input
+
+inline void gui_gotostate_name (const uint8_t new)
+{
+       if (new) // entering new name, not returning from cancelled location choice
+       {
+               gui_return_state = gui_state;
+               gui_name_pos = 0;
+       }
+       gui_state = GUI_NAME;
+       
+       gui_name_list = gui_list_ABC;
+       gui_name_maxcursor = N_ABC-1;
+       gui_name_cursor = 0;
+       if (gui_name_pos < N_NAME)
+       {
+               gui_name[gui_name_pos] = (main_timer&512)?'_':' ';
+               gui_name[gui_name_pos+1] = '\0';
+       }
+       else
+               gui_name[gui_name_cursor] = '\0';
+       lcd_draw_name_menu (
+               GUI_TITLE_NAME,
+               gui_name_list,
+               gui_name_cursor,
+               gui_name,
+               GUI_BUTTONTEXT_DEL,
+               GUI_BUTTONTEXT_SPACE,
+               GUI_BUTTONTEXT_ABC,
+               GUI_BUTTONTEXT_abc,
+               GUI_BUTTONTEXT_123,
+               GUI_BUTTONTEXT_OK
+       );
+       reject_gui_events();
+       gui_ignore_event = new;
+}
+
+inline void gui_event_name (const uint16_t event)
+{
+       uint8_t oldcursor;
+       
+       if((event & EVENT_BITS) == EVENT_RELEASE)
+       {
+               if (event & BUTTON_X)
+               {
+                       if (gui_return_state == GUI_SELECT)
+                               gui_gotostate_select();
+                       else
+                               gui_gotostate_wave();
+               }
+               else if (event & (BUTTON_A | BUTTON_B | BUTTON_Y))
+               {
+                       if (event & BUTTON_A)
+                       {
+                               if(gui_name_pos >0)
+                                       --gui_name_pos;
+                       }
+                       else if (event & BUTTON_B)
+                       {
+                               if(gui_name_pos < N_NAME)
+                               {
+                                       gui_name[gui_name_pos] = ' ';
+                                       ++gui_name_pos;
+                               }
+                       }
+                       else if (event & BUTTON_Y)
+                       {
+                               if (gui_ignore_event)
+                                       gui_ignore_event = 0;
+                               else if(gui_name_pos < N_NAME)
+                               {
+                                       gui_name[gui_name_pos] = gui_name_list[gui_name_cursor];
+                                       ++gui_name_pos;
+                               }
+                       }
+                       if (gui_name_pos < N_NAME)
+                       {       
+                               gui_name[gui_name_pos] = (main_timer&512)?'_':' ';
+                               gui_name[gui_name_pos+1] = '\0';
+                       }
+                       else
+                               gui_name[gui_name_pos] = '\0';
+                       lcd_update_button(-1,gui_name);
+               }
+               else if (event & ( BUTTON_C | BUTTON_D | BUTTON_E ))
+               {
+                       if (event & BUTTON_C)
+                       {
+                               gui_name_list = gui_list_ABC;
+                               gui_name_maxcursor = N_ABC-1;
+                       }
+                       else if (event & BUTTON_D)
+                       {
+                               gui_name_list = gui_list_abc;
+                               gui_name_maxcursor = N_abc-1;
+                       }
+                       else if (event & BUTTON_E)
+                       {
+                               gui_name_list = gui_list_123;
+                               gui_name_maxcursor = N_123-1;
+                       }
+                       lcd_draw_name_list(gui_name_list);
+                       if (gui_name_cursor > gui_name_maxcursor)
+                       {
+                               lcd_erase_name_cursor(gui_name_cursor);
+                               gui_name_cursor -= (gui_name_maxcursor + 1);
+                               lcd_draw_name_cursor(gui_name_cursor);
+                       }
+               }
+               else if (event & BUTTON_F)
+               {
+                       if (gui_name_pos != 0)
+                       {
+                               gui_name[gui_name_pos] = '\0';
+                               gui_gotostate_select_save();
+                       }
+               }
+       }
+       else if((event & EVENT_BITS) == EVENT_JOG)
+       {
+               oldcursor = gui_name_cursor;
+               if ((event & ~EVENT_BITS) == JOG_PLUS)
+               {
+                       if (gui_name_cursor < gui_name_maxcursor)
+                               ++gui_name_cursor;
+                       else
+                               gui_name_cursor = 0;
+               }
+               else
+               {
+                       if (gui_name_cursor != 0)
+                               --gui_name_cursor;
+                       else
+                               gui_name_cursor = gui_name_maxcursor;
+               }
+               lcd_draw_name_cursor(gui_name_cursor);
+               lcd_erase_name_cursor(oldcursor);
+       }
+}
+
+inline void gui_exec_name (void) // do cursor blinking
+{
+       uint8_t cursor;
+       
+       cursor = (main_timer & 512) ? '_' : ' ';
+       if ((gui_name_pos < N_NAME) && (gui_name[gui_name_pos] != cursor))
+       {
+               gui_name[gui_name_pos] = cursor;
+               lcd_update_button(-1, gui_name);
+       }
+}
+
+// SELECT_SAVE: voice setting save location selection
+
+inline void gui_gotostate_select_save (void)
+{
+       // gui_return_state = gui_state;
+       gui_state = GUI_SELECT_SAVE;
+       draw_select_save();
+       reject_gui_events();
+}
+
+inline void gui_event_select_save (const uint16_t event)
+{
+       uint8_t id;
+       
+       if((event & EVENT_BITS) == EVENT_RELEASE)
+       {
+               if (event & (BUTTON_A | BUTTON_B | BUTTON_C | BUTTON_D | BUTTON_E | BUTTON_F))
+               {
+                       if (event & BUTTON_A)
+                               id = 0;
+                       else if (event & BUTTON_B)
+                               id = 1;
+                       else if (event & BUTTON_C)
+                               id = 2;
+                       else if (event & BUTTON_D)
+                               id = 3;
+                       else if (event & BUTTON_E)
+                               id = 4;
+                       else 
+                               id = 5;
+                       
+                       fs_store_voice(
+                               id,
+                               gui_name,
+                               adsr_A,
+                               adsr_D,
+                               adsr_S,
+                               adsr_R,
+                               sample
+                       );
+                       
+                       if (gui_return_state == GUI_SELECT)
+                               gui_gotostate_select();
+                       else
+                               gui_gotostate_wave();
+               }
+               else if (event & BUTTON_X)
+               {
+                       gui_gotostate_name(0);
+               }
+       }
+       else if((event & EVENT_BITS) == EVENT_JOG)
+       {
+               if ((event & ~EVENT_BITS) == JOG_PLUS)
+                       ++gui_page;
+               else
+                       --gui_page;
+               draw_select_save();
+               reject_gui_events();
+       }
+}
+
+// OPTIONS: menu with some settings
+
+inline void gui_gotostate_options (void)
+{
+       make_text_tuning ();
+       make_text_transpose();
+       make_text_midi_in ();
+       make_text_midi_out ();
+       
+       gui_state = GUI_OPTIONS;
+       lcd_draw_menu (6,
+               GUI_TITLE_OPTIONS,"",
+               text_tuning,
+               text_transpose,
+               text_midi_in,
+               text_midi_out,
+               midi_pedal_en ? GUI_BUTTONTEXT_MIDI_PEDAL : GUI_BUTTONTEXT_MIDI_NOPEDAL,
+               GUI_BUTTONTEXT_RESET
+       );
+       reject_gui_events();
+}
+
+inline void gui_event_options (const uint16_t event)
+{
+       if((event & EVENT_BITS) == EVENT_RELEASE)
+       {
+               if (event & BUTTON_X)
+                       gui_gotostate_wave();
+               else if (event & BUTTON_Y)
+                       gui_gotostate_select();
+               else if (event & BUTTON_A)
+                       gui_gotostate_tuning();
+               else if (event & BUTTON_B)
+                       gui_gotostate_transpose();
+               else if (event & BUTTON_C)
+                       gui_gotostate_midi_id();
+               else if (event & BUTTON_D)
+                       gui_gotostate_midi_id_out();
+               else if (event & BUTTON_E)
+               {
+                       if (midi_pedal_en)
+                       {
+                               midi_pedal_en = 0;
+                               lcd_update_button(4, GUI_BUTTONTEXT_MIDI_NOPEDAL);
+                       }
+                       else
+                       {
+                               midi_pedal_en = 1;
+                               lcd_update_button(4, GUI_BUTTONTEXT_MIDI_PEDAL);
+                       }
+                       ctrl_update_midi();
+               }
+               else if (event & BUTTON_F)
+                       gui_gotostate_total_reset();
+       }
+}
+
+// TUNING: change tuning of instrument
+
+inline void gui_gotostate_tuning (void)
+{
+       make_text_tuning();
+       
+       gui_state = GUI_TUNING;
+       lcd_draw_menu (6,
+               GUI_TITLE_TUNING,"",
+               text_tuning, "", "", "", "", ""
+       );
+       reject_gui_events();
+}
+
+inline void gui_event_tuning (const uint16_t event)
+{
+       if((event & EVENT_BITS) == EVENT_RELEASE)
+       {
+               if (event & (BUTTON_X | BUTTON_Y | BUTTON_A))
+                       gui_gotostate_options();
+       }
+       else if((event & EVENT_BITS) == EVENT_JOG)
+       {
+               if ((event & ~EVENT_BITS) == JOG_PLUS)
+               {
+                       set_tuning(tuning + 1);
+               }
+               else
+               {
+                       set_tuning(tuning - 1);
+               }
+               
+               make_text_tuning();
+               lcd_update_button(0, text_tuning);
+       }
+}
+
+// TRANSPOSE: shift by -6 to 6 semitones
+
+inline void gui_gotostate_transpose (void)
+{
+       make_text_transpose();
+       
+       gui_state = GUI_TRANSPOSE;
+       lcd_draw_menu (6,
+               GUI_TITLE_TRANSPOSE,"",
+               "",text_transpose, "", "", "", ""
+       );
+       reject_gui_events();
+}
+
+inline void gui_event_transpose (const uint16_t event)
+{
+       if((event & EVENT_BITS) == EVENT_RELEASE)
+       {
+               if (event & (BUTTON_X | BUTTON_Y | BUTTON_B))
+                       gui_gotostate_options();
+       }
+       else if((event & EVENT_BITS) == EVENT_JOG)
+       {
+               if ((event & ~EVENT_BITS) == JOG_PLUS)
+               {
+                       if (transp<6)
+                               set_transp(transp + 1);
+               }
+               else
+               {
+                       if (transp>-6)
+                               set_transp(transp - 1);
+               }
+               
+               make_text_transpose();
+               lcd_update_button(1, text_transpose);
+       }
+}
+
+// MIDI_ID: choose MIDI channel ID for input
+
+inline void gui_gotostate_midi_id (void)
+{
+       make_text_midi_in();
+       
+       gui_state = GUI_MIDI_ID;
+       lcd_draw_menu (6,
+               GUI_TITLE_MIDI_ID,"",
+               "","",text_midi_in, "", "", ""
+       );
+       reject_gui_events();
+}
+
+inline void gui_event_midi_id (const uint16_t event)
+{
+       if((event & EVENT_BITS) == EVENT_RELEASE)
+       {
+               if (event & (BUTTON_X | BUTTON_Y | BUTTON_C))
+                       gui_gotostate_options();
+       }
+       else if((event & EVENT_BITS) == EVENT_JOG)
+       {
+               if ((event & ~EVENT_BITS) == JOG_PLUS)
+               {
+                       if (midi_id < N_MIDI_ID)
+                               ++midi_id;
+                       else
+                               midi_id = 0;
+               }
+               else
+               {
+                       if (midi_id != 0)
+                               --midi_id;
+                       else
+                               midi_id = N_MIDI_ID;
+               }
+               
+               ctrl_update_midi();
+               make_text_midi_in();
+               lcd_update_button(2, text_midi_in);
+       }
+}
+
+// MIDI_ID_OUT: choose MIDI channel ID for output
+
+inline void gui_gotostate_midi_id_out (void)
+{
+       make_text_midi_out();
+       
+       gui_state = GUI_MIDI_ID_OUT;
+       lcd_draw_menu (6,
+               GUI_TITLE_MIDI_ID_OUT,"",
+               "","","",text_midi_out, "", ""
+       );
+       reject_gui_events();
+}
+
+inline void gui_event_midi_id_out (const uint16_t event)
+{
+       if((event & EVENT_BITS) == EVENT_RELEASE)
+       {
+               if (event & (BUTTON_X | BUTTON_Y | BUTTON_D))
+                       gui_gotostate_options();
+       }
+       else if((event & EVENT_BITS) == EVENT_JOG)
+       {
+               if ((event & ~EVENT_BITS) == JOG_PLUS)
+               {
+                       if (midi_id_out < N_MIDI_ID-1)
+                               ++midi_id_out;
+                       else
+                               midi_id_out = 0;
+               }
+               else
+               {
+                       if (midi_id_out != 0)
+                               --midi_id_out;
+                       else
+                               midi_id_out = N_MIDI_ID-1;
+               }
+               
+               ctrl_update_midi();
+               make_text_midi_out();
+               lcd_update_button(3, text_midi_out);
+       }
+}
+
+// ERR_AIX: error from analog input expander
+
+inline void gui_gotostate_err_aix (void)
+{
+       gui_state = GUI_ERR_AIX;
+       lcd_draw_menu(1,
+               GUI_TITLE_ERR_AIX,"",
+               "","","","","",GUI_BUTTONTEXT_IGNORE
+       );
+       reject_gui_events();
+}
+
+inline void gui_event_err_aix (const uint16_t event)
+{
+       if((event & EVENT_BITS) == EVENT_RELEASE)
+       {
+               if (event & BUTTON_F)
+               {
+                       gui_ignore_error(GUI_ERRF_AIX);
+                       gui_gotostate_wave();
+               }
+       }
+}
+
+// ERR_CTRL: error from communication with main controller
+
+inline void gui_gotostate_err_ctrl (void)
+{
+       gui_state = GUI_ERR_CTRL;
+       lcd_draw_menu(1,
+               GUI_TITLE_ERR_CTRL,"",
+               "","","","","",GUI_BUTTONTEXT_IGNORE
+       );
+       reject_gui_events();
+}
+
+inline void gui_event_err_ctrl (const uint16_t event)
+{
+       if((event & EVENT_BITS) == EVENT_RELEASE)
+       {
+               if (event & BUTTON_F)
+               {
+                       gui_ignore_error(GUI_ERRF_CTRL);
+                       gui_gotostate_wave();
+               }
+       }
+}
+
+// ERR_FS: error from filesystem
+
+inline void gui_gotostate_err_fs (void)
+{
+       gui_state = GUI_ERR_FS;
+       lcd_draw_menu(3,
+               GUI_TITLE_ERR_FS,"",
+               "","","",
+               GUI_BUTTONTEXT_FORMAT,
+               GUI_BUTTONTEXT_RECOVER,
+               GUI_BUTTONTEXT_IGNORE
+       );
+       reject_gui_events();
+}
+
+inline void gui_event_err_fs (const uint16_t event)
+{
+       if((event & EVENT_BITS) == EVENT_RELEASE)
+       {
+               if (event & BUTTON_D)
+               {
+                       gui_gotostate_total_reset();
+               }
+               else if (event & BUTTON_E)
+               {
+                       // TODO: RECOVER
+               }
+               else if (event & BUTTON_F)
+               {
+                       gui_ignore_error(GUI_ERRF_FS);
+                       gui_gotostate_wave();
+               }
+       }
+}
+
+// TOTAL_RESET: reformat flash and restart
+
+inline void gui_gotostate_total_reset (void)
+{
+       gui_state = GUI_TOTAL_RESET;
+       lcd_draw_menu(0,
+               GUI_TITLE_RESET,"",
+               "","","","","",""
+       );
+       fs_format();
+       RESET_UI();
+}
+
+// Handle the actual GUI state machine
+void handle_gui(void)
+{
+               uint16_t event;
+       
+       if (gui_event_r != gui_event_w) // event received
+       {
+               // get event, advance pointer
+               event = gui_event[gui_event_r];
+               if(gui_event_r >= N_GUI_EVENT - 1)
+                       gui_event_r = 0;
+               else
+                       ++gui_event_r;
+               
+               // execute appropriate event handler for current state
+               switch (gui_state)
+               {
+               case GUI_WAVE:
+                       gui_event_wave(event);
+                       break;
+               case GUI_SELECT:
+                       gui_event_select(event);
+                       break;
+               case GUI_NAME:
+                       gui_event_name(event);
+                       break;
+               case GUI_SELECT_SAVE:
+                       gui_event_select_save(event);
+                       break;
+               case GUI_OPTIONS:
+                       gui_event_options(event);
+                       break;
+               case GUI_TUNING:
+                       gui_event_tuning(event);
+                       break;
+               case GUI_TRANSPOSE:
+                       gui_event_transpose(event);
+                       break;
+               case GUI_MIDI_ID:
+                       gui_event_midi_id(event);
+                       break;
+               case GUI_MIDI_ID_OUT:
+                       gui_event_midi_id_out(event);
+                       break;
+               case GUI_ERR_AIX:
+                       gui_event_err_aix(event);
+                       break;
+               case GUI_ERR_CTRL:
+                       gui_event_err_ctrl(event);
+                       break;
+               case GUI_ERR_FS:
+                       gui_event_err_fs(event);
+                       break;
+               default:
+                       break;
+               }
+       }
+       
+       // execute current state handler if available 
+       switch (gui_state)
+       {
+       case GUI_WAVE:
+               gui_exec_wave();
+               break;
+       case GUI_NAME:
+               gui_exec_name();
+               break;
+       case GUI_SELECT:
+       case GUI_SELECT_SAVE:
+       case GUI_OPTIONS:
+       case GUI_TUNING:
+       case GUI_TRANSPOSE:
+       case GUI_MIDI_ID:
+       case GUI_MIDI_ID_OUT:
+       case GUI_ERR_AIX:
+       case GUI_ERR_CTRL:
+       case GUI_ERR_FS:
+               break;
+       case GUI_INIT:
+       default:
+               gui_gotostate_wave();
+               break;
+       }
+       
+       // error detected? goto error screen
+       if (gui_errors && !(gui_state & GUI_ERROR))
+       {
+               if (gui_errors & GUI_ERRF_FS)
+                       gui_gotostate_err_fs();
+               else if (gui_errors & GUI_ERRF_CTRL)
+                       gui_gotostate_err_ctrl();
+               else if (gui_errors & GUI_ERRF_AIX)
+                       gui_gotostate_err_aix();
+       }
+}
+
+/* drawing of some screens */
+
+void draw_wave (void) // waveform display
+{
+       uint16_t i;
+       uint16_t x;
+       
+       // convert sample to drawable shape
+       for(i = 0; i< N_SAMPLE; ++i)
+       {
+               x = (uint16_t)(sample[i]+128) * 229;
+               wave_shape[i] = x>>9;
+       }
+       
+       lcd_draw_wave(wave_shape);
+}
+
+void draw_adsr (void) // envelope display
+{
+       uint16_t i;
+       uint32_t s;
+       uint32_t rd;
+       uint32_t x = 0;
+       uint32_t x0 = 0;
+       uint8_t  a = 1;
+       uint8_t  d = 0;
+       
+       // TODO: make magic numbers to meaningful #defines!
+       
+       // convert envelope to drawable shape
+       
+       s = adsr_S >> 7;
+       s *= 115;
+       s >>= 8;
+       
+       rd = (adsr_R > adsr_D) ? adsr_R : adsr_D; 
+       
+       for(i = 0; i < (N_SAMPLE>>1); ++i) // when key pressed
+       {
+               x0 = x;
+               
+               if (a) // attack phase
+               {
+                       x += adsr_A;
+                       if ((x >= 0x00e58000) || ( x < x0)) // reached top, goto D
+                       {
+                               x = 0x00e58000;
+                               a = 0;
+                               d = 1;
+                       }
+               }
+               else if (d) // decay phase
+               {
+                       x -= adsr_D;
+                       if ((x <= s) || (x > x0)) // reached threshold, goto S
+                       {
+                               x = s;
+                               d = 0;
+                       }
+               }
+               
+               adsr_shape[i] = x >> 17;
+       }
+       
+       d = 1;
+       
+       for(i = (N_SAMPLE>>1); i < N_SAMPLE; ++i) // when key released
+       {
+               x0 = x;
+               
+               if (d) // release decay phase above threshold
+               {
+                       x -= rd;
+                       if ((x <= s) || (x > x0)) // threshold reached, go below
+                       {
+                               x = s;
+                               d = 0;
+                       }
+               }
+               else if (x) // release decay phase below threshold
+               {
+                       x -= adsr_R;
+                       if (x > x0) // bottom reached, stop
+                               x = 0;
+               }
+               
+               adsr_shape[i] = x >> 17;
+       }
+       
+       lcd_draw_adsr(adsr_shape);
+}
+
+void draw_select (void) // voice settings selection
+{
+       fs_load_page(gui_page);
+       make_text_select();
+       
+       lcd_draw_menu(6,
+               text_select,
+               "",
+               fs_page_names[0],
+               fs_page_names[1],
+               fs_page_names[2],
+               fs_page_names[3],
+               fs_page_names[4],
+               fs_page_names[5]
+       );
+}
+
+void draw_select_save (void) // voice setting save location selection
+{
+       fs_load_page(gui_page);
+       make_text_save();
+       
+       lcd_draw_menu(7,
+               text_save,
+               gui_name,
+               fs_page_names[0],
+               fs_page_names[1],
+               fs_page_names[2],
+               fs_page_names[3],
+               fs_page_names[4],
+               fs_page_names[5]
+       );
+}
+
+/* dynamic menu texts */
+
+inline void make_text_tuning (void)
+{
+       uint8_t x = GUI_BUTTONTEXT_TUNING_INSERT;
+       x += make_dec_string(text_tuning+GUI_BUTTONTEXT_TUNING_INSERT, tuning, 2, 4);
+       text_tuning[x  ]=text_tuning[x-1];
+       text_tuning[(x++)-1]='.';
+       text_tuning[x++]='H';
+       text_tuning[x++]='z';
+       text_tuning[x  ]='\0';
+}
+
+inline void make_text_transpose (void)
+{
+       uint8_t x = GUI_BUTTONTEXT_TRANSPOSE_INSERT;
+       uint8_t abs;
+       
+       if (transp < 0)
+       {
+               abs = 0-transp;
+               text_transpose[x]='-';
+       }
+       else
+       {
+               abs = transp;
+               if (transp>0)
+                       text_transpose[x]='+';
+               else
+                       text_transpose[x]=' ';
+       }
+       ++x;
+       x += make_dec_string(text_transpose+x, abs, 1, 2);
+       text_transpose[x]='\0';
+}
+
+inline void make_text_midi_in (void)
+{
+       uint8_t x = GUI_BUTTONTEXT_MIDI_IN_INSERT;
+       if (midi_id < N_MIDI_ID)
+       {
+               x += make_dec_string(text_midi_in+GUI_BUTTONTEXT_MIDI_IN_INSERT, midi_id+1, 1, 2);
+               text_midi_in[x]='\0';
+       }
+       else
+       {
+               text_midi_in[x++]='A';
+               text_midi_in[x++]='L';
+               text_midi_in[x++]='L';
+               text_midi_in[x  ]='\0';
+       }
+}
+
+inline void make_text_midi_out (void)
+{
+       uint8_t x = GUI_BUTTONTEXT_MIDI_OUT_INSERT;
+       x += make_dec_string(text_midi_out+GUI_BUTTONTEXT_MIDI_OUT_INSERT, midi_id_out+1, 1, 2);
+       text_midi_out[x]='\0';
+}
+
+inline void make_text_select (void)
+{
+       uint8_t x = GUI_TITLE_SELECT_INSERT;
+       x += make_dec_string(text_select+GUI_TITLE_SELECT_INSERT, fs_page+1, 1, 3);
+       text_select[x++]=')';
+       text_select[x  ]='\0';
+}
+
+inline void make_text_save (void)
+{
+       uint8_t x = GUI_TITLE_SAVE_INSERT;
+       x += make_dec_string(text_save+GUI_TITLE_SAVE_INSERT, fs_page+1, 1, 3);
+       text_save[x++]=')';
+       text_save[x  ]='\0';
+}
diff --git a/gui.h b/gui.h
new file mode 100644 (file)
index 0000000..424bf52
--- /dev/null
+++ b/gui.h
@@ -0,0 +1,211 @@
+/* GUI */
+/* Encoding of this file is ISO 8859-2 */
+/*
+Copyright 2021, 2022 Balthasar Szczepañski
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include <stdint.h>
+#include "klavirko-ui.h"
+
+#define GET_JOG() ((JOG_A ? 0x1 : 0x0) ^ (JOG_B ? 0x3 : 0x0))
+
+#define BUTTON_DEBOUNCE  4
+#define BUTTON_HOLD    680
+
+#define BUTTON_A 0b00000001
+#define BUTTON_B 0b00000010
+#define BUTTON_C 0b00000100
+#define BUTTON_D 0b00001000
+#define BUTTON_E 0b00010000
+#define BUTTON_F 0b00100000
+#define BUTTON_X 0b01000000
+#define BUTTON_Y 0b10000000
+
+#define BUTTON_ABC    (BUTTON_A | BUTTON_B | BUTTON_C)
+#define BUTTON_DEF    (BUTTON_D | BUTTON_E | BUTTON_F)
+#define BUTTON_ABCDEF (BUTTON_ABC | BUTTON_DEF)
+
+#define BUTTON_THR_0  58
+#define BUTTON_THR_1  66
+#define BUTTON_THR_2  76
+#define BUTTON_THR_3  91
+#define BUTTON_THR_4 114
+#define BUTTON_THR_5 147
+#define BUTTON_THR_6 208
+
+#define JOG_PLUS  1
+#define JOG_MINUS 3
+
+#define N_GUI_EVENT 16
+
+#define EVENT_BITS    0xE000
+#define EVENT_PRESS   0x8000
+#define EVENT_RELEASE 0x4000
+#define EVENT_HOLD    0xC000
+#define EVENT_JOG     0x2000
+
+#define GUI_INIT        0x00
+#define GUI_WAVE        0x01
+#define GUI_SELECT      0x02
+#define GUI_SELECT_SAVE 0x03
+#define GUI_NAME        0x04
+#define GUI_OPTIONS     0x10
+#define GUI_TUNING      0x11
+#define GUI_TRANSPOSE   0x12
+#define GUI_MIDI_ID     0x13
+#define GUI_MIDI_ID_OUT 0x14
+
+#define GUI_ERROR       0x80
+#define GUI_ERR_AIX     0x81
+#define GUI_ERR_CTRL    0x82
+#define GUI_ERR_FS      0x83
+#define GUI_TOTAL_RESET 0xff
+
+#define GUI_ERRF_AIX  0b00000001
+#define GUI_ERRF_CTRL 0b00000010
+#define GUI_ERRF_FS   0b00000100
+
+#define GUI_ERRF_ALL (GUI_ERRF_AIX | GUI_ERRF_CTRL | GUI_ERRF_FS)
+
+#define LIST_ABC "A¡BCÆDEÊFGHIJKL£MNÑOÓPQRS¦TUVWXYZ¬¯"
+#define LIST_abc "a±bcædeêfghijkl³mnñoópqrs¶tuvwxyz¼¿"
+#define LIST_123 "0123456789.,!?:;+-*/=\\^<>()[]{}#$%&'\"`~_|@"
+
+#define N_ABC 35
+#define N_abc 35
+#define N_123 42
+
+#define GUI_TITLE_NAME        "Save as:"
+#define GUI_TITLE_OPTIONS     "Options:"
+#define GUI_TITLE_TUNING      "Tuning:"
+#define GUI_TITLE_TRANSPOSE   "Transpose:"
+#define GUI_TITLE_MIDI_ID     "MIDI in ID:"
+#define GUI_TITLE_MIDI_ID_OUT "MIDI out ID:"
+#define GUI_TITLE_ERR_AIX     "AIX communication fail!"
+#define GUI_TITLE_ERR_CTRL    "CTRL communication fail!"
+#define GUI_TITLE_ERR_FS      "File system unformatted or damaged!"
+#define GUI_TITLE_RESET       "TERAS BENDZIE INNY $WIAT"
+
+#define GUI_BUTTONTEXT_DEL          "  <xx"
+#define GUI_BUTTONTEXT_SPACE        ""
+#define GUI_BUTTONTEXT_ABC          "  ABC"
+#define GUI_BUTTONTEXT_abc          "  abc"
+#define GUI_BUTTONTEXT_123          " 123!?"
+#define GUI_BUTTONTEXT_OK           "  OK."
+#define GUI_BUTTONTEXT_MIDI_PEDAL   "MIDI   pedal         allowed"
+#define GUI_BUTTONTEXT_MIDI_NOPEDAL "MIDI   pedal         illegal"
+#define GUI_BUTTONTEXT_RESET        " TOTAL  RESET"
+#define GUI_BUTTONTEXT_IGNORE       "ignore  and   resume"
+#define GUI_BUTTONTEXT_RECOVER      "recover(TODO)"
+#define GUI_BUTTONTEXT_FORMAT       "format"
+
+#define GUI_BUTTONTEXT_TUNING        "tuning        "
+#define GUI_BUTTONTEXT_TUNING_N      15
+#define GUI_BUTTONTEXT_TUNING_INSERT 7
+
+#define GUI_BUTTONTEXT_TRANSPOSE        "transp.  +x "
+#define GUI_BUTTONTEXT_TRANSPOSE_N      13
+#define GUI_BUTTONTEXT_TRANSPOSE_INSERT 9
+
+#define GUI_BUTTONTEXT_MIDI_IN        "MIDI inID=   "
+#define GUI_BUTTONTEXT_MIDI_IN_N      14
+#define GUI_BUTTONTEXT_MIDI_IN_INSERT 10
+
+#define GUI_BUTTONTEXT_MIDI_OUT        "MIDIoutID=  "
+#define GUI_BUTTONTEXT_MIDI_OUT_N      13
+#define GUI_BUTTONTEXT_MIDI_OUT_INSERT 10
+
+#define GUI_TITLE_SELECT        "Select voice:     (page     "
+#define GUI_TITLE_SELECT_N      29
+#define GUI_TITLE_SELECT_INSERT 24
+
+#define GUI_TITLE_SAVE        "Save where(page     "
+#define GUI_TITLE_SAVE_N      21
+#define GUI_TITLE_SAVE_INSERT 16
+
+// event handling
+
+inline void add_gui_event(const uint16_t event);
+inline void reject_gui_events (void);
+inline void gui_trigger_error (const uint8_t error);
+inline void gui_ignore_error (const uint8_t error);
+
+// handle changes
+
+inline void gui_update_adsr (void);
+inline void gui_update_wave (void);
+inline void gui_lock_wave (void);
+inline void gui_unlock_wave (void);
+inline void gui_lock_adsr (void);
+inline void gui_unlock_adsr (void);
+inline void gui_switch_wave (void);
+inline void gui_switch_adsr (void);
+
+// handling inputs
+
+inline void int_jog (void) LOWTEXT_INT;
+inline void int_button_abcdef (void) LOWTEXT;
+inline void int_button_y (void) LOWTEXT_INT;
+
+// general setup
+
+inline void setup_gui(void);
+inline void init_gui(void);
+
+// state machine
+
+inline void gui_gotostate_wave (void);
+inline void gui_gotostate_select (void);
+inline void gui_gotostate_name (const uint8_t new);
+inline void gui_gotostate_select_save (void);
+inline void gui_gotostate_options (void);
+inline void gui_gotostate_tuning (void);
+inline void gui_gotostate_transpose (void);
+inline void gui_gotostate_midi_id (void);
+inline void gui_gotostate_midi_id_out (void);
+inline void gui_gotostate_err_aix (void);
+inline void gui_gotostate_err_ctrl (void);
+inline void gui_gotostate_err_fs (void);
+inline void gui_gotostate_total_reset (void);
+
+inline void gui_event_wave (const uint16_t event);
+inline void gui_event_select (const uint16_t event);
+inline void gui_event_name (const uint16_t event);
+inline void gui_event_select_save (const uint16_t event);
+inline void gui_event_options (const uint16_t event);
+inline void gui_event_tuning (const uint16_t event);
+inline void gui_event_transp (const uint16_t event);
+inline void gui_event_midi_id (const uint16_t event);
+inline void gui_event_midi_id_out (const uint16_t event);
+inline void gui_event_err_aix (const uint16_t event);
+inline void gui_event_err_ctrl (const uint16_t event);
+inline void gui_event_err_fs (const uint16_t event);
+
+inline void gui_exec_wave (void);
+inline void gui_exec_name (void);
+
+       void handle_gui(void);
+
+// drawing of some screens
+
+       void draw_wave (void);
+       void draw_adsr (void);
+       void draw_select (void);
+       void draw_select_save (void);
+
+// dynamic menu texts
+
+inline void make_text_tuning (void);
+inline void make_text_transpose (void);
+inline void make_text_midi_in (void);
+inline void make_text_midi_out (void);
+inline void make_text_select (void);
+inline void make_text_save (void);
diff --git a/img2fnt.c b/img2fnt.c
new file mode 100644 (file)
index 0000000..300776f
--- /dev/null
+++ b/img2fnt.c
@@ -0,0 +1,103 @@
+/* convert font: PNG to H */
+/*
+Copyright 2021 Balthasar Szczepański
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include <stdio.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include "IL/il.h" // Dev Image Library
+
+int cleanup(const char *t,int m,int e);
+int main(int argc, char *argv[]);
+
+ILuint inpix;
+
+int main(int argc, char *argv[])
+{
+       ILubyte *data;
+       ILint   Y;
+       uint16_t h, x, y, c, i;
+       uint8_t  v;
+       
+       if (argc<3)
+               return cleanup("img2fnt name file",0,1);
+       
+       ilInit();
+       
+       ilEnable(IL_ORIGIN_SET);
+       ilEnable(IL_FILE_OVERWRITE);
+       
+       ilGenImages(1,&inpix);
+       ilBindImage(inpix);
+       
+       if(!ilLoadImage(argv[2]))
+               return cleanup("Load fail.",1,1);
+       
+       if(ilGetInteger(IL_IMAGE_FORMAT)!=IL_COLOUR_INDEX)
+               return cleanup("Not indexed.",1,1);
+       
+       if(ilGetInteger(IL_IMAGE_WIDTH)!=128)
+               return cleanup("Incorrect width.",1,1);
+       
+       Y = ilGetInteger(IL_IMAGE_HEIGHT);
+       if (Y == 256)
+               h = 2;
+       else if (Y == 128)
+               h = 1;
+       else
+               return cleanup("Incorrect height.",1,1);
+       
+       data=ilGetData();
+       
+       fprintf(stdout,"//autogenerated from %s by img2font.c\n", argv[2]);
+       fprintf(stdout,"#define %s { \\\n", argv[1]);
+       
+       for (c=0; c<256; ++c)
+       {
+               fprintf(stdout, "\t/* %02" PRIX16 " */", c);
+               for (x=0; x<8; ++x)
+               {
+                       for (i=0; i<h; ++i)
+                       {
+                               fputs(" ",stdout);
+                               v = 0;
+                               for (y=0; y<8; ++y)
+                               {
+                                       if (data[((15-(c/16))*8*h+(h-i-1)*8+7-y)*128+(c%16)*8+x])
+                                               v |= 1 << y;
+                               }
+                               fprintf(stdout, "0x%02" PRIx16 ",", v);
+                       }
+               }
+               fputs(" \\\n",stdout);
+       }
+       
+       fputs("}\n", stdout);
+       
+       return cleanup("Ok.",1,0);
+       
+}
+int cleanup(const char *t, int m,int e)
+{
+       if(t[0] != 0)
+               fprintf(stderr, "%s\n", t);
+       switch (m)
+       {
+       // case 2:
+               // fclose(outfile);
+       case 1:
+               ilDeleteImages(1,&inpix);
+       case 0:
+       default:
+               return e;
+       }
+}
diff --git a/interrupt_handlers.c b/interrupt_handlers.c
new file mode 100644 (file)
index 0000000..75b7049
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+Copyright 2021 Balthasar Szczepański
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include "interrupt_handlers.h"
+#include "aix.h"
+#include "fs.h"
+#include "gui.h"
+#include "main.h"
+#include "ctrl.h"
+
+void FALLBACK_INT (void) { }
+
+
+// INT_AD (0x34)
+void INT_AD (void)
+{
+       // because this int. arrives every 1ms,
+       // use it as a global time source
+       int_button_abcdef();
+       int_main_timer();
+       aix_watchdog();
+       ctrl_watchdog();
+       fs_watchdog();
+}
diff --git a/interrupt_handlers.h b/interrupt_handlers.h
new file mode 100644 (file)
index 0000000..0a55af1
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+Copyright 2021 Balthasar Szczepański
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef INTERRUPT_HANDLERS_H
+#define INTERRUPT_HANDLERS_H
+
+#include "klavirko-ui.h"
+
+void FALLBACK_INT (void) LOWTEXT_INT;
+
+
+// INT_AD (0x34)
+void INT_AD (void) LOWTEXT_INT;
+
+// HWV
+// PowerON_Reset (0x0)
+void PowerON_Reset (void) __attribute__ ((interrupt));
+#endif
diff --git a/iodefine.h b/iodefine.h
new file mode 100644 (file)
index 0000000..006fd4b
--- /dev/null
@@ -0,0 +1,332 @@
+/*
+The poor replacement for the iodefine.h and iodefine_ext.h files autogenerated by e2studio.
+
+I only provided replacements for things that I actually use in project.
+(i did not want to waste too much effort on this pointless task)
+So the definition will be incomplete.
+Extend if needed, based on the hardware reference document.
+
+You will notice much similarity to the replaced thing.
+That's because these things must have such values to work correctly.
+Which makes the replacement kind of a pointless work as in the end
+almost the same thing comes out. But now it is mine.
+So, I believe this should not even be copyrightable,
+In case it is then consider it copyrighted and released under the same
+license as the other files:
+*/
+/*
+Copyright 2022 Balthasar Szczepański
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/* R5F104JG microcontroller */
+
+#include <stdint.h>
+
+#define DI()   asm("di")
+#define EI()   asm("ei")
+#define NOP()  asm("nop")
+
+// I leave all names as in original file to preserve compatibility!
+// (it's a _replacement_ after all)
+// but redefined to stdint type for clarity
+
+// structure for sigle bit access.
+
+typedef struct {
+       uint8_t no0 :1;
+       uint8_t no1 :1;
+       uint8_t no2 :1;
+       uint8_t no3 :1;
+       uint8_t no4 :1;
+       uint8_t no5 :1;
+       uint8_t no6 :1;
+       uint8_t no7 :1;
+} __BITS8;
+
+typedef struct {
+       uint16_t no0  :1;
+       uint16_t no1  :1;
+       uint16_t no2  :1;
+       uint16_t no3  :1;
+       uint16_t no4  :1;
+       uint16_t no5  :1;
+       uint16_t no6  :1;
+       uint16_t no7  :1;
+       uint16_t no8  :1;
+       uint16_t no9  :1;
+       uint16_t no10 :1;
+       uint16_t no11 :1;
+       uint16_t no12 :1;
+       uint16_t no13 :1;
+       uint16_t no14 :1;
+       uint16_t no15 :1;
+} __BITS16;
+
+// Didn't bother with the union thing; not needed at all.
+
+#define ADM2        (*(volatile uint8_t  *) 0xF0010)
+#define ADM2_bit    (*(volatile __BITS8  *) 0xF0010)
+
+#define PU0         (*(volatile uint8_t  *) 0xF0030)
+#define PU0_bit     (*(volatile __BITS8  *) 0xF0030)
+
+#define PU1         (*(volatile uint8_t  *) 0xF0031)
+#define PU1_bit     (*(volatile __BITS8  *) 0xF0031)
+
+#define PU3         (*(volatile uint8_t  *) 0xF0033)
+#define PU3_bit     (*(volatile __BITS8  *) 0xF0033)
+
+#define PU5         (*(volatile uint8_t  *) 0xF0035)
+#define PU5_bit     (*(volatile __BITS8  *) 0xF0035)
+
+#define PU7         (*(volatile uint8_t  *) 0xF0037)
+#define PU7_bit     (*(volatile __BITS8  *) 0xF0037)
+
+#define PU14        (*(volatile uint8_t  *) 0xF003E)
+#define PU14_bit    (*(volatile __BITS8  *) 0xF003E)
+
+#define PIM0        (*(volatile uint8_t  *) 0xF0040)
+#define PIM0_bit    (*(volatile __BITS8  *) 0xF0040)
+
+#define PIM1        (*(volatile uint8_t  *) 0xF0041)
+#define PIM1_bit    (*(volatile __BITS8  *) 0xF0041)
+
+#define PIM3        (*(volatile uint8_t  *) 0xF0043)
+#define PIM3_bit    (*(volatile __BITS8  *) 0xF0043)
+
+#define POM0        (*(volatile uint8_t  *) 0xF0050)
+#define POM0_bit    (*(volatile __BITS8  *) 0xF0050)
+
+#define POM1        (*(volatile uint8_t  *) 0xF0051)
+#define POM1_bit    (*(volatile __BITS8  *) 0xF0051)
+
+#define POM3        (*(volatile uint8_t  *) 0xF0053)
+#define POM3_bit    (*(volatile __BITS8  *) 0xF0053)
+
+#define POM5        (*(volatile uint8_t  *) 0xF0055)
+#define POM5_bit    (*(volatile __BITS8  *) 0xF0055)
+
+#define POM7        (*(volatile uint8_t  *) 0xF0057)
+#define POM7_bit    (*(volatile __BITS8  *) 0xF0057)
+
+#define PMC0        (*(volatile uint8_t  *) 0xF0060)
+#define PMC0_bit    (*(volatile __BITS8  *) 0xF0060)
+
+#define PMC1        (*(volatile uint8_t  *) 0xF0061)
+#define PMC1_bit    (*(volatile __BITS8  *) 0xF0061)
+
+#define PMC12       (*(volatile uint8_t  *) 0xF006C)
+#define PMC12_bit   (*(volatile __BITS8  *) 0xF006C)
+
+#define PMC14       (*(volatile uint8_t  *) 0xF006E)
+#define PMC14_bit   (*(volatile __BITS8  *) 0xF006E)
+
+#define ADPC        (*(volatile uint8_t  *) 0xF0076)
+
+#define PER1        (*(volatile uint8_t  *) 0xF007A)
+#define PER1_bit    (*(volatile __BITS8  *) 0xF007A)
+
+#define HOCODIV     (*(volatile uint8_t  *) 0xF00A8)
+
+#define PER0        (*(volatile uint8_t  *) 0xF00F0)
+#define PER0_bit    (*(volatile __BITS8  *) 0xF00F0)
+
+#define OSMC        (*(volatile uint8_t  *) 0xF00F3)
+
+#define SSR00       (*(volatile uint16_t *) 0xF0100)
+
+#define SSR01       (*(volatile uint16_t *) 0xF0102)
+
+#define SSR02       (*(volatile uint16_t *) 0xF0104)
+
+#define SSR03       (*(volatile uint16_t *) 0xF0106)
+
+#define SIR01       (*(volatile uint16_t *) 0xF010A)
+
+#define SIR03       (*(volatile uint16_t *) 0xF010E)
+
+#define SMR00       (*(volatile uint16_t *) 0xF0110)
+
+#define SMR01       (*(volatile uint16_t *) 0xF0112)
+
+#define SMR02       (*(volatile uint16_t *) 0xF0114)
+
+#define SMR03       (*(volatile uint16_t *) 0xF0116)
+
+#define SCR00       (*(volatile uint16_t *) 0xF0118)
+
+#define SCR01       (*(volatile uint16_t *) 0xF011A)
+
+#define SCR02       (*(volatile uint16_t *) 0xF011C)
+
+#define SCR03       (*(volatile uint16_t *) 0xF011E)
+
+#define SS0L        (*(volatile uint8_t  *) 0xF0122)
+#define SS0L_bit    (*(volatile __BITS8  *) 0xF0122)
+
+#define SPS0        (*(volatile uint16_t *) 0xF0126)
+
+#define SO0         (*(volatile uint16_t *) 0xF0128)
+
+#define SOE0L       (*(volatile uint8_t  *) 0xF012A)
+#define SOE0L_bit   (*(volatile __BITS8  *) 0xF012A)
+
+#define SOL0        (*(volatile uint16_t *) 0xF0134)
+
+#define SSR11       (*(volatile uint16_t *) 0xF0142)
+
+#define SIR11       (*(volatile uint16_t *) 0xF014A)
+
+#define SMR11       (*(volatile uint16_t *) 0xF0152)
+
+#define SCR11       (*(volatile uint16_t *) 0xF015A)
+
+#define SS1L        (*(volatile uint8_t  *) 0xF0162)
+#define SS1L_bit    (*(volatile __BITS8  *) 0xF0162)
+
+#define SPS1        (*(volatile uint16_t *) 0xF0166)
+
+#define SO1         (*(volatile uint16_t *) 0xF0168)
+
+#define SOE1L       (*(volatile uint8_t  *) 0xF016A)
+#define SOE1L_bit   (*(volatile __BITS8  *) 0xF016A)
+
+#define TRJCR0      (*(volatile uint8_t * ) 0xF0240)
+
+#define TRJIOC0     (*(volatile uint8_t  *) 0xF0241)
+#define TRJIOC0_bit (*(volatile __BITS8  *) 0xF0241)
+
+#define TRJMR0      (*(volatile uint8_t  *) 0xF0242)
+#define TRJMR0_bit  (*(volatile __BITS8  *) 0xF0242)
+
+#define TRJ0        (*(volatile uint16_t *) 0xF0500)
+
+
+#define P0          (*(volatile uint8_t  *) 0xFFF00)
+#define P0_bit      (*(volatile __BITS8  *) 0xFFF00)
+
+#define P1          (*(volatile uint8_t  *) 0xFFF01)
+#define P1_bit      (*(volatile __BITS8  *) 0xFFF01)
+
+#define P2          (*(volatile uint8_t  *) 0xFFF02)
+#define P2_bit      (*(volatile __BITS8  *) 0xFFF02)
+
+#define P3          (*(volatile uint8_t  *) 0xFFF03)
+#define P3_bit      (*(volatile __BITS8  *) 0xFFF03)
+
+#define P5          (*(volatile uint8_t  *) 0xFFF05)
+#define P5_bit      (*(volatile __BITS8  *) 0xFFF05)
+
+#define P6          (*(volatile uint8_t  *) 0xFFF06)
+#define P6_bit      (*(volatile __BITS8  *) 0xFFF06)
+
+#define P7          (*(volatile uint8_t  *) 0xFFF07)
+#define P7_bit      (*(volatile __BITS8  *) 0xFFF07)
+
+#define P12         (*(volatile uint8_t  *) 0xFFF0C)
+#define P12_bit     (*(volatile __BITS8  *) 0xFFF0C)
+
+#define P13         (*(volatile uint8_t  *) 0xFFF0D)
+#define P13_bit     (*(volatile __BITS8  *) 0xFFF0D)
+
+#define P14         (*(volatile uint8_t  *) 0xFFF0E)
+#define P14_bit     (*(volatile __BITS8  *) 0xFFF0E)
+
+#define SDR00       (*(volatile uint16_t *) 0xFFF10)
+
+#define SDR01       (*(volatile uint16_t *) 0xFFF12)
+
+#define ADCRH       (*(volatile uint8_t  *) 0xFFF1F)
+
+#define PM0         (*(volatile uint8_t  *) 0xFFF20)
+#define PM0_bit     (*(volatile __BITS8  *) 0xFFF20)
+
+#define PM1         (*(volatile uint8_t  *) 0xFFF21)
+#define PM1_bit     (*(volatile __BITS8  *) 0xFFF21)
+
+#define PM2         (*(volatile uint8_t  *) 0xFFF22)
+#define PM2_bit     (*(volatile __BITS8  *) 0xFFF22)
+
+#define PM3         (*(volatile uint8_t  *) 0xFFF23)
+#define PM3_bit     (*(volatile __BITS8  *) 0xFFF23)
+
+#define PM5         (*(volatile uint8_t  *) 0xFFF25)
+#define PM5_bit     (*(volatile __BITS8  *) 0xFFF25)
+
+#define PM6         (*(volatile uint8_t  *) 0xFFF26)
+#define PM6_bit     (*(volatile __BITS8  *) 0xFFF26)
+
+#define PM7         (*(volatile uint8_t  *) 0xFFF27)
+#define PM7_bit     (*(volatile __BITS8  *) 0xFFF27)
+
+#define PM12        (*(volatile uint8_t  *) 0xFFF2C)
+#define PM12_bit    (*(volatile __BITS8  *) 0xFFF2C)
+
+#define PM14        (*(volatile uint8_t  *) 0xFFF2E)
+#define PM14_bit    (*(volatile __BITS8  *) 0xFFF2E)
+
+#define ADM0        (*(volatile uint8_t  *) 0xFFF30)
+#define ADM0_bit    (*(volatile __BITS8  *) 0xFFF30)
+
+#define ADS         (*(volatile uint8_t  *) 0xFFF31)
+#define ADS_bit     (*(volatile __BITS8  *) 0xFFF31)
+
+#define ADM1        (*(volatile uint8_t  *) 0xFFF32)
+#define ADM1_bit    (*(volatile __BITS8  *) 0xFFF32)
+
+#define KRM         (*(volatile uint8_t  *) 0xFFF37)
+#define KRM_bit     (*(volatile __BITS8  *) 0xFFF37)
+
+#define EGP1        (*(volatile uint8_t  *) 0xFFF3A)
+#define EGP1_bit    (*(volatile __BITS8  *) 0xFFF3A)
+
+#define EGN1        (*(volatile uint8_t  *) 0xFFF3B)
+#define EGN1_bit    (*(volatile __BITS8  *) 0xFFF3B)
+
+#define SDR02       (*(volatile uint16_t *) 0xFFF44)
+
+#define SDR03       (*(volatile uint16_t *) 0xFFF46)
+
+#define SDR11       (*(volatile uint16_t *) 0xFFF4A)
+
+#define ITMC        (*(volatile uint16_t *) 0xFFF90)
+
+#define CMC         (*(volatile uint8_t  *) 0xFFFA0)
+
+#define CSC         (*(volatile uint8_t  *) 0xFFFA1)
+#define CSC_bit     (*(volatile __BITS8  *) 0xFFFA1)
+
+#define CKC         (*(volatile uint8_t  *) 0xFFFA4)
+#define CKC_bit     (*(volatile __BITS8  *) 0xFFFA4)
+
+#define IF2L        (*(volatile uint8_t  *) 0xFFFD0)
+#define IF2L_bit    (*(volatile __BITS8  *) 0xFFFD0)
+
+#define MK2L        (*(volatile uint8_t  *) 0xFFFD4)
+#define MK2L_bit    (*(volatile __BITS8  *) 0xFFFD4)
+
+#define IF0H        (*(volatile uint8_t  *) 0xFFFE1)
+#define IF0H_bit    (*(volatile __BITS8  *) 0xFFFE1)
+
+#define IF1L        (*(volatile uint8_t  *) 0xFFFE2)
+#define IF1L_bit    (*(volatile __BITS8  *) 0xFFFE2)
+
+#define IF1H        (*(volatile uint8_t  *) 0xFFFE3)
+#define IF1H_bit    (*(volatile __BITS8  *) 0xFFFE3)
+
+#define MK0H        (*(volatile uint8_t  *) 0xFFFE5)
+#define MK0H_bit    (*(volatile __BITS8  *) 0xFFFE5)
+
+#define MK1L        (*(volatile uint8_t  *) 0xFFFE6)
+#define MK1L_bit    (*(volatile __BITS8  *) 0xFFFE6)
+
+#define MK1H        (*(volatile uint8_t  *) 0xFFFE7)
+#define MK1H_bit    (*(volatile __BITS8  *) 0xFFFE7)
diff --git a/klavirko-ui.h b/klavirko-ui.h
new file mode 100644 (file)
index 0000000..57016ec
--- /dev/null
@@ -0,0 +1,267 @@
+/*
+Copyright 2021 Balthasar Szczepański
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef KLAVIRKO_GUI_H
+#define KLAVIRKO_GUI_H
+
+#include <stdint.h>
+#include "iodefine.h"
+// #include "iodefine_ext.h"
+
+#define LOWTEXT     __attribute__ ((section (".lowtext")))
+#define LOWTEXT_INT __attribute__ ((interrupt)) LOWTEXT
+
+/* * * IO, SFR * * */
+
+//some names taken from schematic
+
+//POWER CONTROLL
+
+//DCDC_ENABLE_UC
+#define DCDC      P0_bit.no0
+#define DCDC__PM  PM0_bit.no0
+#define DCDC__POM POM0_bit.no0
+#define DCDC__PMC PMC0_bit.no0
+
+//UART
+
+//WIFI_RXD
+#define CTRL_TX      P0_bit.no2
+#define CTRL_TX__PM  PM0_bit.no2
+#define CTRL_TX__POM POM0_bit.no2
+#define CTRL_TX__PMC PMC0_bit.no2
+#define CTRL_TX__PU  PU0_bit.no2
+
+#define CTRL_TX__SMR SMR02
+#define CTRL_TX__SCR SCR02
+#define CTRL_TX__SDR SDR02
+#define CTRL_TX__SSR SSR02
+#define CTRL_TX__SOE SOE0L_bit.no2
+#define CTRL_TX__SS  SS0L_bit.no2
+#define CTRL_TX__IF  IF1L_bit.no0
+#define CTRL_TX__MK  MK1L_bit.no0
+
+//WIFI_TXD
+#define CTRL_RX      P0_bit.no3
+#define CTRL_RX__PM  PM0_bit.no3
+#define CTRL_RX__POM POM0_bit.no3
+#define CTRL_RX__PIM PIM0_bit.no3
+#define CTRL_RX__PMC PMC0_bit.no3
+#define CTRL_RX__PU  PU0_bit.no3
+
+#define CTRL_RX__SMR SMR03
+#define CTRL_RX__SCR SCR03
+#define CTRL_RX__SDR SDR03
+#define CTRL_RX__SSR SSR03
+#define CTRL_RX__SIR SIR03
+#define CTRL_RX__SS  SS0L_bit.no3
+#define CTRL_RX__IF  IF1L_bit.no1
+#define CTRL_RX__MK  MK1L_bit.no1
+#define CTRL_RX__MKE MK1L_bit.no2
+
+//UI_TXD
+#define AIX_TX      P5_bit.no1
+#define AIX_TX__PM  PM5_bit.no1
+#define AIX_TX__POM POM5_bit.no1
+#define AIX_TX__PU  PU5_bit.no1
+
+#define AIX_TX__SMR SMR00
+#define AIX_TX__SCR SCR00
+#define AIX_TX__SDR SDR00
+#define AIX_TX__SSR SSR00
+#define AIX_TX__SOE SOE0L_bit.no0
+#define AIX_TX__SS  SS0L_bit.no0
+#define AIX_TX__IF  IF0H_bit.no5
+#define AIX_TX__MK  MK0H_bit.no5
+
+//UI_RXD
+#define AIX_RX      P5_bit.no0
+#define AIX_RX__PM  PM5_bit.no0
+#define AIX_RX__POM POM5_bit.no0
+#define AIX_RX__PIM POM5_bit.no0
+#define AIX_RX__PU  PU5_bit.no0
+
+#define AIX_RX__SMR SMR01
+#define AIX_RX__SCR SCR01
+#define AIX_RX__SDR SDR01
+#define AIX_RX__SSR SSR01
+#define AIX_RX__SIR SIR01
+#define AIX_RX__SS  SS0L_bit.no1
+#define AIX_RX__IF  IF0H_bit.no6
+#define AIX_RX__MK  MK0H_bit.no6
+#define AIX_RX__MKE MK0H_bit.no7
+
+//SPI
+
+//FLASH_RESET
+#define FS_HOLD     P6_bit.no3
+#define FS_HOLD__PM PM6_bit.no3
+
+//FLASH_WP
+#define FS_WP     P6_bit.no2
+#define FS_WP__PM PM6_bit.no2
+
+//FLASH_CS
+#define FS_CS     P7_bit.no3
+#define FS_CS__PM PM7_bit.no3
+#define FS_CS__PU PU7_bit.no3
+
+//FLASH_SCK
+#define FS_SCK     P7_bit.no0
+#define FS_SCK__PM PM7_bit.no0
+#define FS_SCK__PU PU7_bit.no0
+
+//FLASH_MOSI
+#define FS_MOSI     P7_bit.no2
+#define FS_MOSI__PM PM7_bit.no2
+#define FS_MOSI__PU PU7_bit.no2
+
+//FLASH_MISO
+#define FS_MISO      P7_bit.no1
+#define FS_MISO__PM  PM7_bit.no1
+#define FS_MISO__POM POM7_bit.no1
+#define FS_MISO__PU  PU7_bit.no1
+
+#define FS__SMR SMR11
+#define FS__SCR SCR11
+#define FS__SDR SDR11
+#define FS__SSR SSR11
+#define FS__SIR SIR11
+#define FS__SOE SOE1L_bit.no1
+#define FS__SS  SS1L_bit.no1
+
+//LCD
+
+//LCD_BACKLIGHT
+#define LCD_LIGHT      P3_bit.no0
+#define LCD_LIGHT__PM  PM3_bit.no0
+#define LCD_LIGHT__POM POM3_bit.no0
+#define LCD_LIGHT__PIM PIM3_bit.no0
+#define LCD_LIGHT__PU  PU3_bit.no0
+#define LCD_LIGHT__IF  IF1H_bit.no6
+#define LCD_LIGHT__MK  MK1H_bit.no6
+
+//CONTRAST_READ
+#define LCD_CONTRAST__P  P2_bit.no1
+#define LCD_CONTRAST__PM PM2_bit.no1
+
+//LCD_RSTB
+#define LCD_RSTB      P3_bit.no1
+#define LCD_RSTB__PM  PM3_bit.no1
+#define LCD_RSTB__PU  PU3_bit.no1
+
+//LCD_CSB_MASTER
+#define LCD_CSB_MASTER      P12_bit.no0
+#define LCD_CSB_MASTER__PM  PM12_bit.no0
+#define LCD_CSB_MASTER__PMC PMC12_bit.no0
+
+//LCD_CSB_SLAVE
+#define LCD_CSB_SLAVE  P13_bit.no0
+
+//LCD_WR
+#define LCD_WR      P14_bit.no0
+#define LCD_WR__PM  PM14_bit.no0
+#define LCD_WR__PU  PU14_bit.no0
+
+//LCD_RD
+#define LCD_RD      P14_bit.no6
+#define LCD_RD__PM  PM14_bit.no6
+#define LCD_RD__PU  PU14_bit.no6
+
+//LCD_AO
+#define LCD_A0      P14_bit.no7
+#define LCD_A0__PM  PM14_bit.no7
+#define LCD_A0__PMC PMC14_bit.no7
+#define LCD_A0__PU  PU14_bit.no7
+
+//LCD_D0 - LCD_D7
+#define LCD_D      P1
+#define LCD_D__PM  PM1
+#define LCD_D__PU  PU1
+#define LCD_D__PIM PIM1
+#define LCD_D__POM POM1
+#define LCD_D__PMC PMC1
+
+//BUTTON
+
+//BUTTON_GROUP_0
+#define BUTTON_ABC__P   P2_bit.no0
+#define BUTTON_ABC__PM  PM2_bit.no0
+#define BUTTON_ABC__ADS 0x00
+
+//BUTTON_GROUP_2
+#define BUTTON_DEF__P   P2_bit.no2
+#define BUTTON_DEF__PM  PM2_bit.no2
+#define BUTTON_DEF__ADS 0x02
+
+//BUTTON_ON_OFF_UC
+#define BUTTON_X__P P13_bit.no7
+
+//BUTTON_RESUME_UC
+#define BUTTON_Y__P   P7_bit.no7
+#define BUTTON_Y__PM  PM7_bit.no7
+#define BUTTON_Y__PU  PU7_bit.no7
+#define BUTTON_Y__KRM KRM_bit.no7
+#define BUTTON_Y__IF  IF1H_bit.no3
+#define BUTTON_Y__MK  MK1H_bit.no3
+
+//JOG_A
+#define JOG_A      P7_bit.no4
+#define JOG_A__PM  PM7_bit.no4
+#define JOG_A__POM POM7_bit.no4
+#define JOG_A__PU  PU7_bit.no4
+#define JOG_A__EGP EGP1_bit.no0
+#define JOG_A__EGN EGN1_bit.no0
+#define JOG_A__MK  MK2L_bit.no5
+#define JOG_A__IF  IF2L_bit.no5
+
+//JOG_B
+#define JOG_B      P7_bit.no5
+#define JOG_B__PM  PM7_bit.no5
+#define JOG_B__PU  PU7_bit.no5
+#define JOG_B__EGP EGP1_bit.no1
+#define JOG_B__EGN EGN1_bit.no1
+#define JOG_B__MK  MK2L_bit.no6
+#define JOG_B__IF  IF2L_bit.no6
+
+//LED
+
+//LED_0
+#define LED_U     P2_bit.no5
+#define LED_U__PM PM2_bit.no5
+
+//LED_1
+#define LED_V     P2_bit.no6
+#define LED_V__PM PM2_bit.no6
+
+//LED_2
+#define LED_W     P2_bit.no7
+#define LED_W__PM PM2_bit.no7
+
+
+//at 32MHz
+#define NOP_1us() \
+{      \
+       NOP(); NOP(); NOP(); NOP(); NOP(); NOP(); NOP(); NOP(); \
+       NOP(); NOP(); NOP(); NOP(); NOP(); NOP(); NOP(); NOP(); \
+       NOP(); NOP(); NOP(); NOP(); NOP(); NOP(); NOP(); NOP(); \
+       NOP(); NOP(); NOP(); NOP(); NOP(); NOP(); NOP(); NOP(); \
+}
+
+//reset by executing from illegal memory access
+#define RESET_UI() \
+{ \
+       asm("br !!0xEF00;"); \
+}
+
+
+#endif
diff --git a/lcd.c b/lcd.c
new file mode 100644 (file)
index 0000000..299fdc9
--- /dev/null
+++ b/lcd.c
@@ -0,0 +1,1046 @@
+/* LCD */
+/*
+Copyright 2021 Balthasar Szczepański
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include <string.h>
+#include "klavirko-ui.h"
+#include "lcd.h"
+#include "main.h"
+#include "debug.h"
+#include "font.h"
+
+volatile uint16_t lcdpwm_high;
+volatile uint16_t lcdpwm_low;
+volatile uint8_t  lcdpwm_phase;
+
+         uint8_t  lcd_mode[2] = {0xff, 0xff};
+         uint8_t  lcd_x0[2] = {0xff, 0xff};
+         uint8_t  lcd_x1[2] = {0x00, 0x00};
+         uint8_t  lcd_y0[2] = {0xff, 0xff};
+         uint8_t  lcd_y1[2] = {0x00, 0x00};
+
+const uint8_t  wave_upper [12] = WAVE_UPPER;
+const uint8_t  wave_middle[14] = WAVE_MIDDLE;
+const uint8_t  wave_lower [12] = WAVE_LOWER;
+
+const uint8_t  font8 [N_FONT8]  = FONT8;
+const uint8_t  font16[N_FONT16] = FONT16;
+
+const uint16_t button_pixels [N_BUTTONTYPES] = BUTTON_PIXELS;
+const uint16_t button_chars  [N_BUTTONTYPES] = BUTTON_CHARS;
+const uint16_t button_lmargin[N_BUTTONTYPES] = BUTTON_LMARGIN;
+const uint16_t button_rmargin[N_BUTTONTYPES] = BUTTON_RMARGIN;
+
+
+inline void lcd_brightness (const uint16_t value)
+{
+       uint16_t x;
+       
+       x = (((uint32_t)value)*((uint32_t)value))>>6;
+       lcdpwm_high = 64 + x;
+       lcdpwm_low  = 16384 - x;
+}
+
+inline void lcd_contrast (const uint16_t value)
+{
+       uint32_t x;
+       
+       x  = value;
+       x *= LCD_Vop_SCALE;
+       x += LCD_Vop_OFFSET;
+       x>>= LCD_Vop_SHIFT;
+       
+       if (x > LCD_Vop_MAX)
+               x = LCD_Vop_MAX;
+       else if (x < LCD_Vop_MIN)
+               x = LCD_Vop_MIN;
+       
+       lcd_write(LCD_MASTER, LCD_COMMAND, LCD_SetVop);
+       lcd_write(LCD_MASTER, LCD_DATA, x & LCD_Vop_LOWMASK);
+       lcd_write(LCD_MASTER, LCD_DATA, x >> LCD_Vop_HIGHSHIFT);
+}
+
+
+
+inline void setup_lcd (void)
+{
+       PER1 |= 0b00000001; //enable timer RJ
+       
+       LCD_LIGHT__POM = 0;
+       LCD_LIGHT__PIM = 0;
+       LCD_LIGHT__PU = 0;
+       LCD_LIGHT__PM = 0;
+       LCD_LIGHT = 0;
+       
+       LCD_RSTB__PM = 0;
+       LCD_RSTB = 0;
+       
+       LCD_CSB_MASTER__PMC = 0;
+       LCD_CSB_MASTER__PM = 0;
+       LCD_CSB_MASTER = 1;
+       
+       LCD_CSB_SLAVE = 1;
+       
+       LCD_WR__PM = 0;
+       LCD_WR__PU = 0;
+       LCD_WR = 1;
+       
+       LCD_RD__PM = 0;
+       LCD_RD__PU = 0;
+       LCD_RD = 1;
+       
+       LCD_A0__PMC = 0;
+       LCD_A0__PM = 0;
+       LCD_A0__PU = 0;
+       LCD_A0 = 1;
+       
+       LCD_D__PMC = 0x00;  
+       LCD_D__POM = 0x00;
+       LCD_D__PIM = 0x00;
+       LCD_D__PU = 0x00;
+       LCD_D__PM = 0x00;
+       LCD_D = 0x00;
+       
+       // lcd_brightness(0);
+       lcdpwm_high = 64;
+       lcdpwm_low  = 16384;
+       lcdpwm_phase = 0;
+       
+       TRJ0 = lcdpwm_high;
+       TRJIOC0 = 0b00000101; //enable output, start high
+       // TRJIOC0 = 0b00000001; //disable output
+       TRJMR0  = 0b00010001; //pulse mode, fclk/8 = 4MHz
+       // TRJMR0  = 0b00010000; //timer mode, fclk/8 = 4MHz
+       // LCD_LIGHT__MK = 0;    //enable int. NOT NOW
+       // TRJCR0  = 0b00000001; //start timer NOT NOW!
+}
+
+inline void init_lcd (void)
+{
+       // most of this comes from datasheet, with some adjustment.
+       // uint16_t i,j;
+
+       //start LCD PWM timer and enable its interrupt
+       LCD_LIGHT__MK = 0;
+       TRJCR0  = 0b00000001;
+
+       //reset LCD
+       LCD_RSTB=0;
+       wait_ms(1 +1);
+       LCD_RSTB=1;
+       wait_ms(1 +1);
+       
+       // Slave chip
+       lcd_write(LCD_SLAVE, LCD_COMMAND, LCD_ExtCommand1);
+       lcd_write(LCD_SLAVE, LCD_COMMAND, LCD_EnableSlave);
+       
+       // Disable auto read
+       lcd_write(LCD_SLAVE, LCD_COMMAND, LCD_ExtCommand2);
+       lcd_write(LCD_SLAVE, LCD_COMMAND, LCD_AutoReadControl);
+       lcd_write(LCD_SLAVE, LCD_DATA, 0x9F);
+       
+       // // Enable OTP Read
+       // lcd_write(LCD_SLAVE, LCD_COMMAND, LCD_OtpWrRdControl);
+       // lcd_write(LCD_SLAVE, LCD_DATA, 0x00);
+       
+       // wait_ms(10 +1);
+       
+       // // OTP Up-Load
+       // lcd_write(LCD_SLAVE, LCD_COMMAND, LCD_OtpRead);
+       
+       // wait_ms(20 +1);
+       
+       // // OTP Control Out
+       // lcd_write(LCD_SLAVE, LCD_COMMAND, LCD_OtpControlWrite);
+       
+       // Sleep out
+       lcd_write(LCD_SLAVE, LCD_COMMAND, LCD_ExtCommand1);     
+       lcd_write(LCD_SLAVE, LCD_COMMAND, LCD_PowerSaveSleepOut);
+       
+       wait_ms(50 +1);
+       
+       // Power control
+       lcd_write(LCD_SLAVE, LCD_COMMAND, LCD_PowerControl);
+       lcd_write(LCD_SLAVE, LCD_DATA, 0x01); // VB OFF; VR ON, VF OFF
+       
+       // Display Mode
+       lcd_write(LCD_SLAVE, LCD_COMMAND, LCD_DisplayMode);
+       lcd_write(LCD_SLAVE, LCD_DATA, LCD_MONO); // Monochrome Mode
+       
+       // Display Format
+       lcd_write(LCD_SLAVE, LCD_COMMAND, LCD_DataFormatLsbTop);
+       
+       // Display Control
+       lcd_write(LCD_SLAVE, LCD_COMMAND, LCD_DisplayControl);
+       lcd_write(LCD_SLAVE, LCD_DATA, 0x00); // CL Dividing Ratio -> Not Divide
+       lcd_write(LCD_SLAVE, LCD_DATA, 0x25); // Duty = 38
+       lcd_write(LCD_SLAVE, LCD_DATA, 0x00); // CL Dividing Ratio -> Not Divide
+       
+       // Data Scan Direction
+       lcd_write(LCD_SLAVE, LCD_COMMAND, LCD_DataScanDirection);
+       lcd_write(LCD_SLAVE, LCD_DATA, 0x06);
+       
+       // Analog Circuit Set
+       lcd_write(LCD_SLAVE, LCD_COMMAND, LCD_ExtCommand2);
+       lcd_write(LCD_SLAVE, LCD_COMMAND, LCD_AnalogCircuitSet);
+       lcd_write(LCD_SLAVE, LCD_DATA, 0x00);
+       lcd_write(LCD_SLAVE, LCD_DATA, 0x01); // Booster Efficiency = Level 1
+       lcd_write(LCD_SLAVE, LCD_DATA, LCD_BIAS); // Bias
+       
+       // Page Address Setting
+       lcd_write(LCD_SLAVE, LCD_COMMAND, LCD_ExtCommand1);
+       lcd_write(LCD_SLAVE, LCD_COMMAND, LCD_SetPageAddress);
+       lcd_write(LCD_SLAVE, LCD_DATA, 0);
+       lcd_write(LCD_SLAVE, LCD_DATA, 4);
+       
+       // Column Address Setting
+       lcd_write(LCD_SLAVE, LCD_COMMAND, LCD_SetColumnAddress);
+       lcd_write(LCD_SLAVE, LCD_DATA, 0); 
+       lcd_write(LCD_SLAVE, LCD_DATA, 255);
+       
+       // Display On
+       lcd_write(LCD_SLAVE, LCD_COMMAND, LCD_DisplayOn);
+       
+       // Master chip
+       
+       // Enable Master
+       lcd_write(LCD_MASTER, LCD_COMMAND, LCD_ExtCommand1);
+       lcd_write(LCD_MASTER, LCD_COMMAND, LCD_EnableMaster);
+       
+       // Disable auto read
+       lcd_write(LCD_MASTER, LCD_COMMAND, LCD_ExtCommand2);
+       lcd_write(LCD_MASTER, LCD_COMMAND, LCD_AutoReadControl);
+       lcd_write(LCD_MASTER, LCD_DATA, 0x9F);
+       
+       // // Enable OTP Read
+       // lcd_write(LCD_MASTER, LCD_COMMAND, LCD_OtpWrRdControl);
+       // lcd_write(LCD_MASTER, LCD_DATA, 0x00);
+       
+       // wait_ms(10 +1);
+       
+       // // OTP Up-Load
+       // lcd_write(LCD_MASTER, LCD_COMMAND, LCD_OtpRead);
+       
+       // wait_ms(20 +1);
+       
+       // // OTP Control Out
+       // lcd_write(LCD_MASTER, LCD_COMMAND, LCD_OtpControlWrite);
+       
+       // Sleep out
+       lcd_write(LCD_MASTER, LCD_COMMAND, LCD_ExtCommand1);
+       lcd_write(LCD_MASTER, LCD_COMMAND, LCD_PowerSaveSleepOut);
+       
+       wait_ms(50 +1);
+       
+       // Power control
+       lcd_write(LCD_MASTER, LCD_COMMAND, LCD_PowerControl);
+       lcd_write(LCD_MASTER, LCD_DATA, 0x0B); // VB ON; VR, VF ON
+       
+       // Set Vop
+       lcd_write(LCD_MASTER, LCD_COMMAND, LCD_SetVop);
+       lcd_write(LCD_MASTER, LCD_DATA, LCD_DEFAULT_Vop );//!
+       lcd_write(LCD_MASTER, LCD_DATA, LCD_DEFAULT_Vop_HIGH );//!
+       
+       // Display Mode
+       lcd_write(LCD_MASTER, LCD_COMMAND, LCD_DisplayMode);
+       lcd_write(LCD_MASTER, LCD_DATA, LCD_MONO); // Monochrome Mode
+       
+       // Display Format
+       lcd_write(LCD_MASTER, LCD_COMMAND, LCD_DataFormatLsbTop);
+       
+       // Display Control
+       lcd_write(LCD_MASTER, LCD_COMMAND, LCD_DisplayControl);
+       lcd_write(LCD_MASTER, LCD_DATA, 0x00); // CL Dividing Ratio -> Not Divide
+       lcd_write(LCD_MASTER, LCD_DATA, 0x25); // Duty = 38
+       lcd_write(LCD_MASTER, LCD_DATA, 0x00); // CL Dividing Ratio -> Not Divide
+       
+       // Data Scan Direction
+       lcd_write(LCD_MASTER, LCD_COMMAND, LCD_DataScanDirection);
+       lcd_write(LCD_MASTER, LCD_DATA, 0x06);
+       
+       // Analog Circuit Set
+       lcd_write(LCD_MASTER, LCD_COMMAND, LCD_ExtCommand2);
+       lcd_write(LCD_MASTER, LCD_COMMAND, LCD_AnalogCircuitSet);
+       lcd_write(LCD_MASTER, LCD_DATA, 0x00);
+       lcd_write(LCD_MASTER, LCD_DATA, 0x01); // Booster Efficiency = Level
+       lcd_write(LCD_MASTER, LCD_DATA, LCD_BIAS); // Bias
+       
+       // Page Address Setting
+       lcd_write(LCD_MASTER, LCD_COMMAND, LCD_ExtCommand1);
+       lcd_write(LCD_MASTER, LCD_COMMAND, LCD_SetPageAddress);
+       lcd_write(LCD_MASTER, LCD_DATA, 0);
+       lcd_write(LCD_MASTER, LCD_DATA, 4);
+       
+       // Column Address Setting
+       lcd_write(LCD_MASTER, LCD_COMMAND, LCD_SetColumnAddress);
+       lcd_write(LCD_MASTER, LCD_DATA, 0);
+       lcd_write(LCD_MASTER, LCD_DATA, 255);
+       
+       // Internal Power Supply
+       lcd_write(LCD_MASTER, LCD_COMMAND, LCD_ExtCommand2);
+       lcd_write(LCD_MASTER, LCD_COMMAND, LCD_DrivingSelectInt);
+       
+       // Display On
+       lcd_write(LCD_MASTER, LCD_COMMAND, LCD_ExtCommand1);
+       lcd_write(LCD_MASTER, LCD_COMMAND, LCD_DisplayOn);
+}
+
+
+
+// interrupt to generate brightness PWM.
+// FOR SOME REASON IT IS ON A PIN WITH TIMER OUTPUT BUT WITHOUT PWM :< 
+inline void int_lcd_pwm (void)
+{
+       if(lcdpwm_phase)
+       {
+               // LCD_LIGHT = 1;
+               TRJ0 = lcdpwm_high;
+               lcdpwm_phase = 0;
+       }
+       else
+       {
+               // LCD_LIGHT = 0;
+               TRJ0 = lcdpwm_low;
+               lcdpwm_phase = 1;
+       }
+}
+
+void lcd_write ( // write single byte to LCD
+       const uint8_t chip,
+       const uint8_t cmd,
+       const uint8_t data
+)
+{
+       uint8_t d;
+       
+       LCD_D__PM = 0x00; //data output
+       
+       if (chip == LCD_SLAVE) //reverse bits for slave chip
+               d = reverse_bits(data);
+       else
+               d = data;
+       
+       LCD_A0 = (cmd == LCD_DATA)?1:0; //set command/data
+       
+       LCD_D = d; //set data
+       
+       if(chip == LCD_MASTER) //set chipselect
+               LCD_CSB_MASTER=0;
+       else
+               LCD_CSB_SLAVE=0;
+       
+       LCD_WR = 0; //set writeenable;
+       LCD_WR = 0;
+               // LCD_D = d; //set data
+       
+       LCD_WR = 1; //unset writeenable;
+       
+       if(chip == LCD_MASTER) //unset chipselect
+               LCD_CSB_MASTER=1;
+       else
+               LCD_CSB_SLAVE=1;
+       
+       // LCD_D__PM = 0xff; //data back to input
+}
+
+inline void lcd_set_mode ( // setup drawing area and color depth for single chip
+       const uint8_t chip,
+       const uint8_t mode,
+       const uint8_t x0,
+       const uint8_t x1,
+       const uint8_t y0,
+       const uint8_t y1
+)
+{
+       if (lcd_mode[chip] != mode)
+       {
+               lcd_mode[chip] = mode;
+               lcd_write(chip, LCD_COMMAND, LCD_DisplayMode);
+               lcd_write(chip, LCD_DATA, mode);
+       }
+       if ((lcd_x0[chip] != x0)||(lcd_x1[chip] != x1))
+       {
+               lcd_x0[chip] = x0;
+               lcd_x1[chip] = x1;
+               lcd_write(chip, LCD_COMMAND, LCD_SetColumnAddress);
+               lcd_write(chip, LCD_DATA, x0);
+               lcd_write(chip, LCD_DATA, x1);
+       }
+       if ((lcd_y0[chip] != y0)||(lcd_y1[chip] != y1))
+       {
+               lcd_y0[chip] = y0;
+               lcd_y1[chip] = y1;
+               lcd_write(chip, LCD_COMMAND, LCD_SetPageAddress);
+               lcd_write(chip, LCD_DATA, y0);
+               lcd_write(chip, LCD_DATA, y1);
+       }
+}
+
+void lcd_setup_drawing ( // setup drawing area and color depth
+       const uint8_t  mode,
+       const uint16_t x0,
+       const uint16_t x1,
+       const uint8_t  y0,
+       const uint8_t  y1
+)
+{
+       uint16_t x;
+       if(x0 < 256)
+       {
+               x = (x1>255) ? 255 : x1;
+               lcd_set_mode(LCD_SLAVE, mode, x0, x, y0, y1);
+               lcd_write(LCD_SLAVE, LCD_COMMAND, LCD_WriteDataToDDRAM);
+       }
+       if(x1 >= 256)
+       {
+               x = (x0<256) ? 0 : (x0-256);
+               lcd_set_mode(LCD_MASTER, mode, x, x1-256, y0, y1);
+               lcd_write(LCD_MASTER, LCD_COMMAND, LCD_WriteDataToDDRAM);
+       }
+}
+
+
+
+void lcd_clear_screen (const uint8_t mode)
+{
+       uint8_t  y;
+       uint16_t i, j;
+       
+       y = (mode == LCD_GRAY) ? 9 : 4;
+       
+       lcd_setup_drawing(mode, 0, 511, 0, y);
+       for(i=0; i<=255; ++i)
+       {
+               for(j=0; j<=y; ++j)
+               {
+                       lcd_write(LCD_SLAVE, LCD_DATA, 0x00);
+                       lcd_write(LCD_MASTER, LCD_DATA, 0x00);
+               }
+       }
+}
+
+void lcd_draw_wave (const uint8_t * const data)
+{
+       uint16_t i;
+       uint8_t  col[10];
+       uint8_t  x;
+       
+       lcd_setup_drawing(LCD_GRAY, 0, 255, 0, 9);
+       
+       // convert shape to bit patterns & then draw
+       for (i=0; i<256; ++i)
+       {
+               memset(col, 0, 10);
+               x = data[i];
+               
+               if (x < WAVE_LOWER_0)
+               {
+                       col[4] = WAVE_LOWER_START;
+                       if (x < WAVE_LOWER_1)
+                       {
+                               col[5] = 0xff;
+                               if (x < WAVE_LOWER_2)
+                               {
+                                       col[6] = 0xff;
+                                       if (x < WAVE_LOWER_3)
+                                       {
+                                               col[7] = 0xff;
+                                               if (x < WAVE_LOWER_4)
+                                               {
+                                                       col[8] = 0xff;
+                                                       col[9] = wave_lower[x-WAVE_LOWER_5];
+                                               }
+                                               else
+                                                       col[8] = wave_lower[x-WAVE_LOWER_4];
+                                       }
+                                       else
+                                               col[7] = wave_lower[x-WAVE_LOWER_3];
+                               }
+                               else
+                                       col[6] = wave_lower[x-WAVE_LOWER_2];
+                       }
+                       else
+                               col[5] = wave_lower[x-WAVE_LOWER_1];
+               }
+               else if (x >= WAVE_UPPER_0)
+               {
+                       col[4] = WAVE_UPPER_START;
+                       if (x >= WAVE_UPPER_1)
+                       {
+                               col[3] = 0xff;
+                               if (x >= WAVE_UPPER_2)
+                               {
+                                       col[2] = 0xff;
+                                       if (x >= WAVE_UPPER_3)
+                                       {
+                                               col[1] = 0xff;
+                                               col[0] = wave_upper[x-WAVE_UPPER_3];
+                                       }
+                                       else
+                                               col[1] = wave_upper[x-WAVE_UPPER_2];
+                               }
+                               else
+                                       col[2] = wave_upper[x-WAVE_UPPER_1];
+                       }
+                       else
+                               col[3] = wave_upper[x-WAVE_UPPER_0];
+               }
+               else
+               {
+                       col[4] = wave_middle[x-WAVE_LOWER_0];
+               }
+               
+               lcd_write(LCD_SLAVE, LCD_DATA, col[0]);
+               lcd_write(LCD_SLAVE, LCD_DATA, col[1]);
+               lcd_write(LCD_SLAVE, LCD_DATA, col[2]);
+               lcd_write(LCD_SLAVE, LCD_DATA, col[3]);
+               lcd_write(LCD_SLAVE, LCD_DATA, col[4]);
+               lcd_write(LCD_SLAVE, LCD_DATA, col[5]);
+               lcd_write(LCD_SLAVE, LCD_DATA, col[6]);
+               lcd_write(LCD_SLAVE, LCD_DATA, col[7]);
+               lcd_write(LCD_SLAVE, LCD_DATA, col[8]);
+               lcd_write(LCD_SLAVE, LCD_DATA, col[9]);
+       }
+}
+
+void lcd_draw_adsr (const uint8_t * const data)
+{
+       uint16_t i;
+       uint8_t  col[10];
+       uint8_t  x;
+       
+       lcd_setup_drawing(LCD_GRAY, 256, 511, 0, 9);
+       
+       // convert shape to bit patterns & then draw
+       for (i=0; i<256; ++i)
+       {
+               memset(col, 0, 10);
+               x = data[i];
+               
+               if (x >= ADSR_UPPER_1)
+               {
+                       col[9] = 0xff;
+                       if (x >= ADSR_UPPER_2)
+                       {
+                               col[8] = 0xff;
+                               if (x >= ADSR_UPPER_3)
+                               {
+                                       col[7] = 0xff;
+                                       if (x >= ADSR_UPPER_4)
+                                       {
+                                               col[6] = 0xff;
+                                               if (x >= ADSR_UPPER_5)
+                                               {
+                                                       col[5] = 0xff;
+                                                       if (x >= ADSR_UPPER_6)
+                                                       {
+                                                               col[4] = 0xff;
+                                                               if (x >= ADSR_UPPER_7)
+                                                               {
+                                                                       col[3] = 0xff;
+                                                                       if (x >= ADSR_UPPER_8)
+                                                                       {
+                                                                               col[2] = 0xff;
+                                                                               if (x >= ADSR_UPPER_9)
+                                                                               {
+                                                                                       col[1] = 0xff;
+                                                                                       col[0] = wave_upper[x-ADSR_UPPER_9];
+                                                                               }
+                                                                               else
+                                                                                       col[1] = wave_upper[x-ADSR_UPPER_8];
+                                                                       }
+                                                                       else
+                                                                               col[2] = wave_upper[x-ADSR_UPPER_7];
+                                                               }
+                                                               else
+                                                                       col[3] = wave_upper[x-ADSR_UPPER_6];
+                                                       }
+                                                       else
+                                                               col[4] = wave_upper[x-ADSR_UPPER_5];
+                                               }
+                                               else
+                                                       col[5] = wave_upper[x-ADSR_UPPER_4];
+                                       }
+                                       else
+                                               col[6] = wave_upper[x-ADSR_UPPER_3];
+                               }
+                               else
+                                       col[7] = wave_upper[x-ADSR_UPPER_2];
+                       }
+                       else
+                               col[8] = wave_upper[x-ADSR_UPPER_1];
+               }
+               else
+                       col[9] = wave_upper[x-ADSR_UPPER_0];
+               
+               lcd_write(LCD_MASTER, LCD_DATA, col[0]);
+               lcd_write(LCD_MASTER, LCD_DATA, col[1]);
+               lcd_write(LCD_MASTER, LCD_DATA, col[2]);
+               lcd_write(LCD_MASTER, LCD_DATA, col[3]);
+               lcd_write(LCD_MASTER, LCD_DATA, col[4]);
+               lcd_write(LCD_MASTER, LCD_DATA, col[5]);
+               lcd_write(LCD_MASTER, LCD_DATA, col[6]);
+               lcd_write(LCD_MASTER, LCD_DATA, col[7]);
+               lcd_write(LCD_MASTER, LCD_DATA, col[8]);
+               lcd_write(LCD_MASTER, LCD_DATA, col[9]);
+       }
+}
+
+void lcd_subdraw_button (
+       uint16_t * const x,
+       const uint8_t type,
+       const uint8_t * const text
+)
+{
+       uint16_t line1, line2, line3, line4, margin_l, margin_r;
+       uint16_t len, N;
+       uint16_t i, i0, i1, i2, i3, j0, j1, j2, j3;
+       uint8_t  chip = LCD_SLAVE;
+       
+       line1 = button_chars[type];
+       margin_l = button_lmargin[type];
+       margin_r = button_rmargin[type];
+       
+       line2 = line1 + line1;
+       line3 = line2 + line1;
+       line4 = line3 + line1;
+       
+       N = line1 <<3;
+       
+       len = (uint16_t) strlen(text);
+       if (len>line4)
+               len = line4;
+       
+       for(i=0; i<margin_l; ++i) // left margin
+       {
+               if (*x >= 256)
+                       chip = LCD_MASTER;
+               lcd_write(chip, LCD_DATA, 0x00);
+               lcd_write(chip, LCD_DATA, 0x00);
+               lcd_write(chip, LCD_DATA, 0x00);
+               lcd_write(chip, LCD_DATA, 0x00);
+               lcd_write(chip, LCD_DATA, 0x00);
+               ++(*x);
+       }
+       
+       if (len <= line1) // single text line; 16px font
+       {
+               for (i=0, i0=-1; i<N; ++i)
+               {
+                       if (*x >= 256)
+                               chip = LCD_MASTER;
+                       if(!(i&0x7))
+                       {
+                               ++i0;
+                               if (i0 < len)
+                                       j0=text[i0]<<4;
+                               else
+                                       j0 = 0;
+                       }
+                       lcd_write(chip, LCD_DATA, 0x00);
+                       lcd_write(chip, LCD_DATA, font16[j0]);
+                       ++j0;
+                       lcd_write(chip, LCD_DATA, font16[j0]);
+                       ++j0;
+                       lcd_write(chip, LCD_DATA, 0x00);
+                       lcd_write(chip, LCD_DATA, 0x00);
+                       ++(*x);
+               }
+       }
+       else if (len <= line2) // 2 text lines; 16px font
+       {
+               for (i=0, i0=-1, i1=line1-1; i<N; ++i)
+               {
+                       if (*x >= 256)
+                               chip = LCD_MASTER;
+                       if(!(i&0x7))
+                       {
+                               ++i0;
+                               ++i1;
+                               j0=text[i0]<<4;
+                               if (i1 < len)
+                                       j1=text[i1]<<4;
+                               else
+                                       j1 = 0;
+                       }
+                       lcd_write(chip, LCD_DATA, font16[j0]);
+                       ++j0;
+                       lcd_write(chip, LCD_DATA, font16[j0]);
+                       ++j0;
+                       lcd_write(chip, LCD_DATA, font16[j1]);
+                       ++j1;
+                       lcd_write(chip, LCD_DATA, font16[j1]);
+                       ++j1;
+                       lcd_write(chip, LCD_DATA, 0x00);
+                       ++(*x);
+               }
+       }
+       else if (len <= line3) // 3 text lines; 8px font
+       {
+               for (i=0, i0=-1, i1=line1-1, i2=line2-1; i<N; ++i)
+               {
+                       if (*x >= 256)
+                               chip = LCD_MASTER;
+                       if(!(i&0x7))
+                       {
+                               ++i0;
+                               ++i1;
+                               ++i2;
+                               j0=text[i0]<<3;
+                               j1=text[i1]<<3;
+                               if (i2 < len)
+                                       j2=text[i2]<<3;
+                               else
+                                       j2 = 0;
+                       }
+                       lcd_write(chip, LCD_DATA, 0x00);
+                       lcd_write(chip, LCD_DATA, font8[j0]);
+                       ++j0;
+                       lcd_write(chip, LCD_DATA, font8[j1]);
+                       ++j1;
+                       lcd_write(chip, LCD_DATA, font8[j2]);
+                       ++j2;
+                       lcd_write(chip, LCD_DATA, 0x00);
+                       ++(*x);
+               }
+       }
+       else // 4 text lines; 8px font
+       {
+               for (i=0, i0=-1, i1=line1-1, i2=line2-1, i3=line3-1; i<N; ++i)
+               {
+                       if (*x >= 256)
+                               chip = LCD_MASTER;
+                       if(!(i&0x7))
+                       {
+                               ++i0;
+                               ++i1;
+                               ++i2;
+                               ++i3;
+                               j0=text[i0]<<3;
+                               j1=text[i1]<<3;
+                               j2=text[i2]<<3;
+                               if (i3 < len)
+                                       j3=text[i3]<<3;
+                               else
+                                       j3 = 0;
+                       }
+                       lcd_write(chip, LCD_DATA, font8[j0]);
+                       ++j0;
+                       lcd_write(chip, LCD_DATA, font8[j1]);
+                       ++j1;
+                       lcd_write(chip, LCD_DATA, font8[j2]);
+                       ++j2;
+                       lcd_write(chip, LCD_DATA, font8[j3]);
+                       ++j3;
+                       lcd_write(chip, LCD_DATA, 0x00);
+                       ++(*x);
+               }
+       }
+       for(i=0; i<margin_r; ++i) // right margin
+       {
+               if (*x >= 256)
+                       chip = LCD_MASTER;
+               lcd_write(chip, LCD_DATA, 0x00);
+               lcd_write(chip, LCD_DATA, 0x00);
+               lcd_write(chip, LCD_DATA, 0x00);
+               lcd_write(chip, LCD_DATA, 0x00);
+               lcd_write(chip, LCD_DATA, 0x00);
+               ++(*x);
+       }
+}
+
+void lcd_subdraw_lowbutton8 (
+       uint16_t * const x,
+       const uint8_t type,
+       const uint8_t * const text
+)
+{
+       uint16_t line1, margin_l, margin_r;
+       uint16_t len, N;
+       uint16_t i, i0, j0, j3;
+       uint8_t  chip = LCD_SLAVE;
+       
+       line1 = button_chars[type];
+       margin_l = button_lmargin[type];
+       margin_r = button_rmargin[type];
+       
+       N = line1 <<3;
+       
+       len = (uint16_t) strlen(text);
+       if (len>line1)
+               len = line1;
+       
+       for(i=0; i<margin_l; ++i) // left margin
+       {
+               if (*x >= 256)
+                       chip = LCD_MASTER;
+               lcd_write(chip, LCD_DATA, 0x00);
+               lcd_write(chip, LCD_DATA, 0x00);
+               ++(*x);
+       }
+       
+       for (i=0, i0=-1; i<N; ++i) // single text line; 8 font
+       {
+               if (*x >= 256)
+                       chip = LCD_MASTER;
+               if(!(i&0x7))
+               {
+                       ++i0;
+                       if (i0 < len)
+                               j0=text[i0]<<3;
+                       else
+                               j0 = 0;
+               }
+               lcd_write(chip, LCD_DATA, font8[j0]);
+               ++j0;
+               lcd_write(chip, LCD_DATA, 0x00);
+               ++(*x);
+       }
+
+       for(i=0; i<margin_r; ++i) // right margin
+       {
+               if (*x >= 256)
+                       chip = LCD_MASTER;
+               lcd_write(chip, LCD_DATA, 0x00);
+               lcd_write(chip, LCD_DATA, 0x00);
+               ++(*x);
+       }
+}
+
+void lcd_subdraw_lowbutton16 (
+       uint16_t * const x,
+       const uint8_t type,
+       const uint8_t * const text
+)
+{
+       uint16_t line1, margin_l, margin_r;
+       uint16_t len, N;
+       uint16_t i, i0, j0, j3;
+       uint8_t  chip = LCD_SLAVE;
+       
+       line1 = button_chars[type];
+       margin_l = button_lmargin[type];
+       margin_r = button_rmargin[type];
+       
+       N = line1 <<3;
+       
+       len = (uint16_t) strlen(text);
+       if (len>line1)
+               len = line1;
+       
+       for(i=0; i<margin_l; ++i) // left margin
+       {
+               if (*x >= 256)
+                       chip = LCD_MASTER;
+               lcd_write(chip, LCD_DATA, 0x00);
+               lcd_write(chip, LCD_DATA, 0x00);
+               ++(*x);
+       }
+       
+       for (i=0, i0=-1; i<N; ++i) // single text line; 16px font
+       {
+               if (*x >= 256)
+                       chip = LCD_MASTER;
+               if(!(i&0x7))
+               {
+                       ++i0;
+                       if (i0 < len)
+                               j0=text[i0]<<4;
+                       else
+                               j0 = 0;
+               }
+               lcd_write(chip, LCD_DATA, font16[j0]);
+               ++j0;
+               lcd_write(chip, LCD_DATA, font16[j0]);
+               ++j0;
+               ++(*x);
+       }
+
+       for(i=0; i<margin_r; ++i) // right margin
+       {
+               if (*x >= 256)
+                       chip = LCD_MASTER;
+               lcd_write(chip, LCD_DATA, 0x00);
+               lcd_write(chip, LCD_DATA, 0x00);
+               ++(*x);
+       }
+}
+
+void lcd_subdraw_divider (uint16_t * const x)
+{
+       uint8_t chip = LCD_SLAVE;
+       
+       if (*x >= 256)
+               chip = LCD_MASTER;
+       
+       lcd_write(chip, LCD_DATA, 0x55);
+       lcd_write(chip, LCD_DATA, 0x55);
+       lcd_write(chip, LCD_DATA, 0x55);
+       lcd_write(chip, LCD_DATA, 0x55);
+       lcd_write(chip, LCD_DATA, 0x55);
+       
+       ++(*x);
+}
+
+void lcd_subdraw_lowdivider (uint16_t * const x)
+{
+       uint8_t chip = LCD_SLAVE;
+       
+       if (*x >= 256)
+               chip = LCD_MASTER;
+       
+       lcd_write(chip, LCD_DATA, 0x55);
+       lcd_write(chip, LCD_DATA, 0x55);
+       
+       ++(*x);
+}
+
+void lcd_draw_menu (
+       const uint8_t         buttons,
+       const uint8_t * const title,
+       const uint8_t * const button_0,
+       const uint8_t * const button_A,
+       const uint8_t * const button_B,
+       const uint8_t * const button_C,
+       const uint8_t * const button_D,
+       const uint8_t * const button_E,
+       const uint8_t * const button_F
+){
+       uint16_t x=0;
+       
+       lcd_setup_drawing(LCD_MONO, 0, 511, 0, 4);
+       
+       lcd_subdraw_button (&x, FULLTEXT - buttons, title);
+       switch (buttons)
+       {
+       case 7:
+               lcd_subdraw_divider(&x);
+               lcd_subdraw_button (&x, BUTTON_1X, button_0);
+       case 6:
+               lcd_subdraw_divider(&x);
+               lcd_subdraw_button (&x, BUTTON_1X, button_A);
+       case 5:
+               lcd_subdraw_divider(&x);
+               lcd_subdraw_button (&x, BUTTON_1X, button_B);
+       case 4:
+               lcd_subdraw_divider(&x);
+               lcd_subdraw_button (&x, BUTTON_1X, button_C);
+       case 3:
+               lcd_subdraw_divider(&x);
+               lcd_subdraw_button (&x, BUTTON_1X, button_D);
+       case 2:
+               lcd_subdraw_divider(&x);
+               lcd_subdraw_button (&x, BUTTON_1X, button_E);
+       case 1:
+               lcd_subdraw_divider(&x);
+               lcd_subdraw_button (&x, BUTTON_1X, button_F);
+       case 0:
+       default:
+               break;
+       }
+}
+
+void lcd_update_button (
+       const int8_t id,
+       const uint8_t * const text
+)
+{
+       uint16_t x;
+       
+       x = TITLE_6B_PIXELS + 1 + id * (BUTTON_1X_PIXELS + 1);
+       
+       lcd_setup_drawing(LCD_MONO, x, x+BUTTON_1X_PIXELS-1, 0, 4);
+       
+       lcd_subdraw_button (&x, 0, text);
+}
+
+void lcd_draw_name_cursor (const uint8_t pos)
+{
+       uint16_t x, i, j;
+       uint8_t chip = LCD_SLAVE;
+       
+       x = (pos<<3) + TITLE_6B_PIXELS + 1 +BUTTON_6X_LMARGIN;
+       lcd_setup_drawing(LCD_MONO, x, x+7, 2, 2);
+       
+       for (i=x, j='^'<<3; i<x+8; ++i, j+=1)
+       {
+               if (i>=256)
+                       chip = LCD_MASTER;
+               lcd_write(chip, LCD_DATA, font8[j]);
+       }
+}
+
+void lcd_erase_name_cursor (const uint8_t pos)
+{
+       uint16_t x, i;
+       uint8_t chip = LCD_SLAVE;
+       
+       x = (pos<<3) + TITLE_6B_PIXELS + 1 +BUTTON_6X_LMARGIN;
+       lcd_setup_drawing(LCD_MONO, x, x+7, 2, 2);
+       
+       for (i=x; i<x+8; ++i)
+       {
+               if (i>=256)
+                       chip = LCD_MASTER;
+               lcd_write(chip, LCD_DATA, 0x00);
+       }
+}
+
+void lcd_draw_name_list (const uint8_t * const list)
+{
+       uint16_t x;
+       
+       x = TITLE_6B_PIXELS + 1;
+       lcd_setup_drawing(LCD_MONO, x, 511, 0, 1);
+       lcd_subdraw_lowbutton16(&x, BUTTON_6X, list);
+}
+
+void lcd_draw_name_menu (
+       const uint8_t * const title,
+       const uint8_t * const list,
+       const uint8_t         cursor,
+       const uint8_t * const name,
+       const uint8_t * const button_A,
+       const uint8_t * const button_B,
+       const uint8_t * const button_C,
+       const uint8_t * const button_D,
+       const uint8_t * const button_E,
+       const uint8_t * const button_F
+)
+{
+       uint16_t x = 0;
+       lcd_clear_screen(LCD_MONO);
+       lcd_setup_drawing(LCD_MONO, 0, TITLE_6B_PIXELS, 0, 4);
+       
+       lcd_subdraw_button (&x, TITLE_7B, title);
+       lcd_subdraw_divider(&x);
+       lcd_subdraw_button (&x, BUTTON_1X, name);
+       lcd_subdraw_divider(&x);
+       
+       lcd_setup_drawing(LCD_MONO, x, 511, 3, 4);
+       
+       lcd_subdraw_lowbutton8(&x, BUTTON_1X, button_A);
+       lcd_subdraw_lowdivider(&x);
+       lcd_subdraw_lowbutton8(&x, BUTTON_1X, button_B);
+       lcd_subdraw_lowdivider(&x);
+       lcd_subdraw_lowbutton8(&x, BUTTON_1X, button_C);
+       lcd_subdraw_lowdivider(&x);
+       lcd_subdraw_lowbutton8(&x, BUTTON_1X, button_D);
+       lcd_subdraw_lowdivider(&x);
+       lcd_subdraw_lowbutton8(&x, BUTTON_1X, button_E);
+       lcd_subdraw_lowdivider(&x);
+       lcd_subdraw_lowbutton8(&x, BUTTON_1X, button_F);
+       
+       lcd_draw_name_list(list);
+       
+       lcd_draw_name_cursor(cursor);
+}
diff --git a/lcd.h b/lcd.h
new file mode 100644 (file)
index 0000000..f136ead
--- /dev/null
+++ b/lcd.h
@@ -0,0 +1,473 @@
+/* LCD */
+/*
+Copyright 2021 Balthasar Szczepański
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include <stdint.h>
+#include "klavirko-ui.h"
+// #include "font.h"
+
+/* screen layout
+
+|         256         |         256         |
++-------------+----+----+----+----+----+----+
+|             |    |    |    |    |    |    |
++-------------+----+----+----+----+----+----+
+|     146     1 60 1 60 1 60 1 60 1 60 1 60 |    
+
+|2|  7x8  |2| 
++-+-------+-+
+| |acbdefg| |
++-+-------+-+
+|    60     |
+
+|       18x8       |2|
++------------------+-+ 
+|abcdefghijklmnopqr| |
++------------------+-|
+|        146         |
+
+*/
+
+#define N_FONT8  (256 * 1 * 8)
+#define N_FONT16 (256 * 2 * 8)
+
+#define BUTTON_1X_PIXELS  60
+#define BUTTON_2X_PIXELS 121
+#define BUTTON_3X_PIXELS 182 
+#define BUTTON_4X_PIXELS 243
+#define BUTTON_5X_PIXELS 304
+#define BUTTON_6X_PIXELS 365
+#define BUTTON_7X_PIXELS 426
+#define BUTTON_8X_PIXELS 487
+#define TITLE_8B_PIXELS   24
+#define TITLE_7B_PIXELS   85
+#define TITLE_6B_PIXELS  146
+#define TITLE_5B_PIXELS  207
+#define TITLE_4B_PIXELS  268
+#define TITLE_3B_PIXELS  329
+#define TITLE_2B_PIXELS  390
+#define TITLE_1B_PIXELS  451
+#define FULLTEXT_PIXELS  512
+
+#define BUTTON_1X_CHARS   7
+#define BUTTON_2X_CHARS  14
+#define BUTTON_3X_CHARS  22
+#define BUTTON_4X_CHARS  29
+#define BUTTON_5X_CHARS  37
+#define BUTTON_6X_CHARS  45
+#define BUTTON_7X_CHARS  53
+#define BUTTON_8X_CHARS  60
+#define TITLE_8B_CHARS    3
+#define TITLE_7B_CHARS   10
+#define TITLE_6B_CHARS   18
+#define TITLE_5B_CHARS   25
+#define TITLE_4B_CHARS   33
+#define TITLE_3B_CHARS   41
+#define TITLE_2B_CHARS   48
+#define TITLE_1B_CHARS   56
+#define FULLTEXT_CHARS   64
+
+#define BUTTON_1X_LMARGIN  2
+#define BUTTON_2X_LMARGIN  4
+#define BUTTON_3X_LMARGIN  3 
+#define BUTTON_4X_LMARGIN  5
+#define BUTTON_5X_LMARGIN  4
+#define BUTTON_6X_LMARGIN  2
+#define BUTTON_7X_LMARGIN  1
+#define BUTTON_8X_LMARGIN  3
+#define TITLE_8B_LMARGIN   0
+#define TITLE_7B_LMARGIN   0
+#define TITLE_6B_LMARGIN   0
+#define TITLE_5B_LMARGIN   0
+#define TITLE_4B_LMARGIN   0
+#define TITLE_3B_LMARGIN   0
+#define TITLE_2B_LMARGIN   0
+#define TITLE_1B_LMARGIN   0
+#define FULLTEXT_LMARGIN   0
+
+#define BUTTON_1X_RMARGIN  2
+#define BUTTON_2X_RMARGIN  5
+#define BUTTON_3X_RMARGIN  3 
+#define BUTTON_4X_RMARGIN  6
+#define BUTTON_5X_RMARGIN  4
+#define BUTTON_6X_RMARGIN  3
+#define BUTTON_7X_RMARGIN  1
+#define BUTTON_8X_RMARGIN  4
+#define TITLE_8B_RMARGIN   0
+#define TITLE_7B_RMARGIN   5
+#define TITLE_6B_RMARGIN   2
+#define TITLE_5B_RMARGIN   7
+#define TITLE_4B_RMARGIN   4
+#define TITLE_3B_RMARGIN   1
+#define TITLE_2B_RMARGIN   6
+#define TITLE_1B_RMARGIN   3
+#define FULLTEXT_RMARGIN   0
+
+#define BUTTON_1X 0
+#define BUTTON_2X 1
+#define BUTTON_3X 2
+#define BUTTON_4X 3
+#define BUTTON_5X 4
+#define BUTTON_6X 5
+#define BUTTON_7X 6
+#define BUTTON_8X 7
+#define TITLE_8B  8
+#define TITLE_7B  9
+#define TITLE_6B 10
+#define TITLE_5B 11
+#define TITLE_4B 12
+#define TITLE_3B 13
+#define TITLE_2B 14
+#define TITLE_1B 15
+#define FULLTEXT 16
+#define N_BUTTONTYPES 17
+
+#define BUTTON_PIXELS { \
+       BUTTON_1X_PIXELS, \
+       BUTTON_2X_PIXELS, \
+       BUTTON_3X_PIXELS, \
+       BUTTON_4X_PIXELS, \
+       BUTTON_5X_PIXELS, \
+       BUTTON_6X_PIXELS, \
+       BUTTON_7X_PIXELS, \
+       BUTTON_8X_PIXELS, \
+       TITLE_8B_PIXELS, \
+       TITLE_7B_PIXELS, \
+       TITLE_6B_PIXELS, \
+       TITLE_5B_PIXELS, \
+       TITLE_4B_PIXELS, \
+       TITLE_3B_PIXELS, \
+       TITLE_2B_PIXELS, \
+       TITLE_1B_PIXELS, \
+       FULLTEXT_PIXELS, \
+}
+
+#define BUTTON_CHARS { \
+       BUTTON_1X_CHARS, \
+       BUTTON_2X_CHARS, \
+       BUTTON_3X_CHARS, \
+       BUTTON_4X_CHARS, \
+       BUTTON_5X_CHARS, \
+       BUTTON_6X_CHARS, \
+       BUTTON_7X_CHARS, \
+       BUTTON_8X_CHARS, \
+       TITLE_8B_CHARS, \
+       TITLE_7B_CHARS, \
+       TITLE_6B_CHARS, \
+       TITLE_5B_CHARS, \
+       TITLE_4B_CHARS, \
+       TITLE_3B_CHARS, \
+       TITLE_2B_CHARS, \
+       TITLE_1B_CHARS, \
+       FULLTEXT_CHARS, \
+}
+
+#define BUTTON_LMARGIN { \
+       BUTTON_1X_LMARGIN, \
+       BUTTON_2X_LMARGIN, \
+       BUTTON_3X_LMARGIN, \
+       BUTTON_4X_LMARGIN, \
+       BUTTON_5X_LMARGIN, \
+       BUTTON_6X_LMARGIN, \
+       BUTTON_7X_LMARGIN, \
+       BUTTON_8X_LMARGIN, \
+       TITLE_8B_LMARGIN, \
+       TITLE_7B_LMARGIN, \
+       TITLE_6B_LMARGIN, \
+       TITLE_5B_LMARGIN, \
+       TITLE_4B_LMARGIN, \
+       TITLE_3B_LMARGIN, \
+       TITLE_2B_LMARGIN, \
+       TITLE_1B_LMARGIN, \
+       FULLTEXT_LMARGIN, \
+}
+
+#define BUTTON_RMARGIN { \
+       BUTTON_1X_RMARGIN, \
+       BUTTON_2X_RMARGIN, \
+       BUTTON_3X_RMARGIN, \
+       BUTTON_4X_RMARGIN, \
+       BUTTON_5X_RMARGIN, \
+       BUTTON_6X_RMARGIN, \
+       BUTTON_7X_RMARGIN, \
+       BUTTON_8X_RMARGIN, \
+       TITLE_8B_RMARGIN, \
+       TITLE_7B_RMARGIN, \
+       TITLE_6B_RMARGIN, \
+       TITLE_5B_RMARGIN, \
+       TITLE_4B_RMARGIN, \
+       TITLE_3B_RMARGIN, \
+       TITLE_2B_RMARGIN, \
+       TITLE_1B_RMARGIN, \
+       FULLTEXT_RMARGIN, \
+}
+
+//The LCD driver is ST75256.
+
+/*
+voltage, contrast, bias, settings, limits...
+
+85 <= Vop <= 360
+V0 = 3.6 + Vop * 0.04
+VG = 2 * V0 / N
+N = [9, 10, 11, 12, 13, 14]
+VG >= 1.8
+V0 <=18
+
+therefore:
+
+   | V0/ | min | min | Vop |
+ N |  /VG| V0  | Vop |range|
+---+-----+-----+-----+-----+
+ 9 | 4.5 | 8.1 | 113 | 247 | <-- our choice
+10 | 5.0 | 9.0 | 135 | 225 |
+11 | 5.5 | 9.9 | 158 | 202 |
+12 | 6.0 |10.8 | 180 | 180 |
+13 | 6.5 |11.7 | 203 | 157 |
+14 | 7.0 |12.6 | 225 | 135 |
+
+*/
+
+#define LCD_BIAS_14 0
+#define LCD_BIAS_13 1
+#define LCD_BIAS_12 2
+#define LCD_BIAS_11 3
+#define LCD_BIAS_10 4
+#define LCD_BIAS_9  5
+
+#define LCD_BIAS    LCD_BIAS_9
+
+#define LCD_Vop_MIN_14 225
+#define LCD_Vop_MIN_13 203
+#define LCD_Vop_MIN_12 180
+#define LCD_Vop_MIN_11 158
+#define LCD_Vop_MIN_10 135
+#define LCD_Vop_MIN_9  113
+
+#define LCD_Vop_MAX 360
+
+
+#if LCD_BIAS == LCD_BIAS_14
+       #define LCD_Vop_MIN   LCD_Vop_MIN_14
+#elif LCD_BIAS == LCD_BIAS_13
+       #define LCD_Vop_MIN   LCD_Vop_MIN_13
+#elif LCD_BIAS == LCD_BIAS_12
+       #define LCD_Vop_MIN   LCD_Vop_MIN_12
+#elif LCD_BIAS == LCD_BIAS_11
+       #define LCD_Vop_MIN   LCD_Vop_MIN_11
+#elif LCD_BIAS == LCD_BIAS_10
+       #define LCD_Vop_MIN   LCD_Vop_MIN_10
+#elif LCD_BIAS == LCD_BIAS_9
+       #define LCD_Vop_MIN   LCD_Vop_MIN_9
+#endif
+
+#define LCD_Vop_SCALE (LCD_Vop_MAX - LCD_Vop_MIN)
+
+#define LCD_Vop_SHIFT 10
+
+#define LCD_Vop_OFFSET ((1 << (LCD_Vop_SHIFT-1)) + ((uint32_t)LCD_Vop_MIN << LCD_Vop_SHIFT))
+
+#define LCD_Vop_LOWMASK  0x3F
+#define LCD_Vop_HIGHSHIFT   6
+
+#define LCD_DEFAULT_Vop      180
+#define LCD_DEFAULT_Vop_LOW  (LCD_DEFAULT_Vop & LCD_Vop_LOWMASK)
+#define LCD_DEFAULT_Vop_HIGH (LCD_DEFAULT_Vop >> LCD_Vop_HIGHSHIFT)
+
+#define LCD_MASTER 0
+#define LCD_SLAVE 1
+
+#define LCD_COMMAND 0
+#define LCD_DATA 1
+
+#define LCD_MONO 0x10
+#define LCD_GRAY 0x11
+
+//LCD commands
+
+//ext
+#define LCD_ExtCommand1          0x30
+#define LCD_ExtCommand2          0x31
+#define LCD_ExtCommand3          0x38
+#define LCD_ExtCommand4          0x39
+//ext1                                     
+#define LCD_DisplayOn            0xAF
+#define LCD_DisplayOff           0xAE
+#define LCD_DisplayNormal        0xA6
+#define LCD_DisplayInverse       0xA7
+#define LCD_AllPixelOn           0x23
+#define LCD_AllPixelOff          0x22
+#define LCD_DisplayControl       0xCA
+#define LCD_PowerSaveSleepIn     0x95
+#define LCD_PowerSaveSleepOut    0x94
+#define LCD_SetPageAddress       0x75
+#define LCD_SetColumnAddress     0x15
+#define LCD_DataScanDirection    0xBC
+#define LCD_WriteDataToDDRAM     0x5C
+#define LCD_PartialIn            0xA8
+#define LCD_PartialOut           0xA9
+#define LCD_OscOn                0xD1
+#define LCD_OscOff               0xD2
+#define LCD_PowerControl         0x20
+#define LCD_SetVop               0x81
+#define LCD_VopIncrease          0xD6
+#define LCD_VopDecrease          0xD7
+#define LCD_ReadContrastLo       0x7C
+#define LCD_ReadContrastHi       0x7D
+#define LCD_Nop                  0x25
+#define LCD_DataFormatLsbTop     0x0C
+#define LCD_DataFormatLsbBottom  0x08
+#define LCD_DisplayMode          0xF0
+#define LCD_EnableMaster         0x6E
+#define LCD_EnableSlave          0x6F
+#define LCD_ReadStatus           0x6F
+//ext2
+#define LCD_AnalogCircuitSet     0x32
+#define LCD_BoosterLevel         0x51
+#define LCD_DrivingSelectInt     0x40
+#define LCD_DrivingSelectExt     0x41
+#define LCD_AutoReadControl      0xD7
+#define LCD_OtpWrRdControl       0xE0
+#define LCD_OtpControlWrite      0xE1
+#define LCD_OtpWrite             0xE2
+#define LCD_OtpRead              0xE3
+#define LCD_OtpSelectionControl  0xE4
+
+
+// bit patterns to display the waveform
+#define WAVE_UPPER \
+{ \
+       0x40, 0x80, 0xc0, \
+       0xd0, 0xe0, 0xf0, \
+       0xf4, 0xf8, 0xfc, \
+       0xfd, 0xfe, 0xff \
+}
+#define WAVE_MIDDLE \
+{ \
+       0xc0, 0x80, 0x40, \
+       0x00, 0x10, 0x20, \
+       0x30, 0x34, 0x38, \
+       0x3c, 0x3d, 0x3e, \
+       0x3f \
+}
+#define WAVE_LOWER \
+{ \
+       0xff, 0xbf, 0x7f, \
+       0x3f, 0x2f, 0x1f, \
+       0x0f, 0x0b, 0x07, \
+       0x03, 0x02, 0x01 \
+}
+#define WAVE_LOWER_START 0xc0
+#define WAVE_UPPER_START 0x3f
+
+#define WAVE_LOWER_0 54
+#define WAVE_LOWER_1 42
+#define WAVE_LOWER_2 30
+#define WAVE_LOWER_3 18
+#define WAVE_LOWER_4  6
+#define WAVE_LOWER_5 (-6)
+
+#define WAVE_UPPER_0  67
+#define WAVE_UPPER_1  79
+#define WAVE_UPPER_2  91
+#define WAVE_UPPER_3 103
+
+#define ADSR_UPPER_0 (-5)
+#define ADSR_UPPER_1   7
+#define ADSR_UPPER_2  19
+#define ADSR_UPPER_3  31
+#define ADSR_UPPER_4  43
+#define ADSR_UPPER_5  55
+#define ADSR_UPPER_6  67
+#define ADSR_UPPER_7  79
+#define ADSR_UPPER_8  91
+#define ADSR_UPPER_9 103
+
+
+inline void lcd_brightness (const uint16_t value);
+inline void lcd_contrast (const uint16_t value);
+
+inline void setup_lcd (void);
+inline void init_lcd (void);
+
+inline void int_lcd_pwm (void) LOWTEXT_INT;
+
+void        lcd_write ( // write single byte to LCD
+       const uint8_t chip,
+       const uint8_t cmd,
+       const uint8_t data
+);
+inline void lcd_set_mode ( // setup drawing area and color depth for single chip
+       const uint8_t chip,
+       const uint8_t mode,
+       const uint8_t x0,
+       const uint8_t x1,
+       const uint8_t y0,
+       const uint8_t y1
+);
+void        lcd_setup_drawing ( // setup drawing area and color depth
+       const uint8_t  mode,
+       const uint16_t x0,
+       const uint16_t x1,
+       const uint8_t  y0,
+       const uint8_t  y1
+);
+
+void        lcd_clear_screen (const uint8_t mode);
+void        lcd_draw_wave (const uint8_t * const data);
+void        lcd_draw_adsr (const uint8_t * const data);
+void        lcd_subdraw_button (
+       uint16_t * const x,
+       const uint8_t type,
+       const uint8_t * const text
+);
+void        lcd_subdraw_lowbutton8 (
+       uint16_t * const x,
+       const uint8_t type,
+       const uint8_t * const text
+);
+void        lcd_subdraw_lowbutton16 (
+       uint16_t * const x,
+       const uint8_t type,
+       const uint8_t * const text
+);
+void        lcd_subdraw_divider (uint16_t * const x);
+void        lcd_draw_menu (
+       const uint8_t         buttons,
+       const uint8_t * const title,
+       const uint8_t * const button_0,
+       const uint8_t * const button_A,
+       const uint8_t * const button_B,
+       const uint8_t * const button_C,
+       const uint8_t * const button_D,
+       const uint8_t * const button_E,
+       const uint8_t * const button_F
+);
+void        lcd_update_button (
+       const int8_t id,
+       const uint8_t * const text
+);
+void lcd_draw_name_cursor(const uint8_t pos);
+void lcd_erase_name_cursor(const uint8_t pos);
+void lcd_draw_name_list (const uint8_t * const list);
+void lcd_draw_name_menu (
+       const uint8_t * const title,
+       const uint8_t * const list,
+       const uint8_t         cursor,
+       const uint8_t * const name,
+       const uint8_t * const button_A,
+       const uint8_t * const button_B,
+       const uint8_t * const button_C,
+       const uint8_t * const button_D,
+       const uint8_t * const button_E,
+       const uint8_t * const button_F
+);
diff --git a/license.txt b/license.txt
new file mode 100644 (file)
index 0000000..b1b9c4a
--- /dev/null
@@ -0,0 +1,10 @@
+This my software "klavirko-ui" is released under the 2-clause BSD license.
+
+It includes the appearances of some characters from the font unscii
+which is in the public domain
+https://github.com/viznut/unscii
+files: font*.*
+
+It used to contain files automatically created by e2studio.
+But because their copyright/license status was not clear I replaced
+all the files to avoid any problems.
diff --git a/main.c b/main.c
new file mode 100644 (file)
index 0000000..ef8838c
--- /dev/null
+++ b/main.c
@@ -0,0 +1,187 @@
+/*
+Copyright 2021, 2022 Balthasar Szczepański
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include "klavirko-ui.h"
+#include "main.h"
+#include "aix.h"
+#include "lcd.h"
+#include "gui.h"
+#include "ctrl.h"
+#include "fs.h"
+
+volatile uint32_t main_timer = 0;
+
+inline void int_main_timer (void)
+{
+       ++main_timer;
+}
+
+// use only when interrupts already enabled
+void wait_ms (uint32_t const ms)
+{
+       uint32_t start;
+       
+       start = main_timer;
+       
+       while (((uint32_t)(main_timer - start)) < ms)
+               ;
+       return;
+}
+
+/* ! remember to keep reset_program.asm in agreement with main() definition */
+// int main (int argc, char ** argv)
+void main (void)
+{
+       /* * * SETUP IO * * */
+       
+       //sustain DCDC power
+       DCDC__POM = 0;
+       DCDC__PMC = 0;
+       DCDC__PM = 0;
+       DCDC = 1;
+       
+       /* * * SETUP CLOCK * * */
+       
+       CMC = 0x00; // ignore external X1=8MHz
+       CKC = 0x00; // fclk = fmain; fmain=fih
+       CSC = 0xc0; // run only onchip HS.
+       //HIOTRM don't touch
+       HOCODIV = 0x00; //option byte / 1 : fih=32MHz, fhoco=64MHz
+       
+       /* * * SETUP ALL SUBMODULES * * */
+       
+       setup_lcd();
+       setup_aix();
+       setup_ctrl();
+       setup_gui();
+       setup_fs();
+       
+       /* * * START * * */
+       
+       EI();
+       init_lcd();
+       init_ctrl();
+       //init_gui();
+       init_fs();
+       
+       /* * * PERFORM MAIN TASKS FOREVER * * */
+       
+       while(1)
+       {
+               handle_aix();
+               handle_wave();
+               handle_gui();
+               handle_ctrl();
+               handle_debug();
+       }
+}
+
+uint8_t reverse_bits (uint8_t in) // reverse order of bits in a byte
+{
+       uint8_t out = 0;
+       
+       out |= in & 0x01;
+       out <<= 1;
+       in >>= 1;
+       out |= in & 0x01;
+       out <<= 1;
+       in >>= 1;
+       out |= in & 0x01;
+       out <<= 1;
+       in >>= 1;
+       out |= in & 0x01;
+       out <<= 1;
+       in >>= 1;
+       out |= in & 0x01;
+       out <<= 1;
+       in >>= 1;
+       out |= in & 0x01;
+       out <<= 1;
+       in >>= 1;
+       out |= in & 0x01;
+       out <<= 1;
+       in >>= 1;
+       out |= in & 0x01;
+       return out;
+}
+
+uint8_t make_dec_string ( // create decimal text representation of number
+       uint8_t * const string,
+       uint32_t value,
+       const uint8_t min_digits,
+       const uint8_t max_digits
+)
+{
+       int8_t i, j;
+       uint8_t text[10];
+       uint8_t digit;
+       uint8_t first_digit = 9;
+       
+       for(i=9; i>=0; --i)
+       {
+               digit = value % 10;
+               value /= 10;
+               
+               if (digit != 0)
+                       first_digit = i;
+               
+               text[i] = digit + '0';
+       }
+       if ((10 - min_digits) < first_digit)
+               first_digit = 10 - min_digits;
+       if ((10 - first_digit) > max_digits)
+               first_digit = 10 - max_digits;
+       
+       for (i=first_digit, j=0; i<10; ++i, ++j)
+               string[j] = text[i];
+       
+       return 10 - first_digit; // return actual number of digits
+}
+
+uint8_t make_hex_string ( // create hexadecimal text representation of number
+       uint8_t * const string,
+       uint32_t value,
+       const uint8_t min_digits,
+       const uint8_t max_digits,
+       const uint8_t uppercase
+)
+{
+       int8_t i, j;
+       uint8_t text[8];
+       uint8_t digit;
+       uint8_t first_digit = 7;
+       
+       for(i=7; i>=0; --i)
+       {
+               digit = value & 0x0f;
+               value >>=4;
+               
+               if (digit != 0)
+                       first_digit = i;
+               
+               if (digit < 10)
+                       text[i] = digit + '0';
+               else if (uppercase)
+                       text[i] = digit + 'A' - 10;
+               else
+                       text[i] = digit + 'a' - 10;
+       }
+       if ((8 - min_digits) < first_digit)
+               first_digit = 8 - min_digits;
+       if ((8 - first_digit) > max_digits)
+               first_digit = 8 - max_digits;
+       
+       for (i=first_digit, j=0; i<8; ++i, ++j)
+               string[j] = text[i];
+       
+       return 8 - first_digit; // return actual number of digits
+}
diff --git a/main.h b/main.h
new file mode 100644 (file)
index 0000000..fb6247c
--- /dev/null
+++ b/main.h
@@ -0,0 +1,37 @@
+/*
+Copyright 2021, 2022 Balthasar Szczepański
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include <stdint.h>
+#include "klavirko-ui.h"
+
+inline void    int_main_timer (void) LOWTEXT; // handle timer every 1ms
+       void    wait_ms (uint32_t const ms);   // wait for defined number of milliseconds
+       /* ! remember to keep reset_program.asm in agreement with main() definition */
+       // int     main (int argc, char ** argv);
+       void    main (void);
+       uint8_t reverse_bits (uint8_t in);    // reverse order of bits in a byte
+
+uint8_t make_dec_string ( // create decimal text representation of number
+       uint8_t * const string, // address where to create string
+       uint32_t value,
+       const uint8_t min_digits,
+       const uint8_t max_digits
+); // return actual number of digits
+uint8_t make_hex_string ( // create hexadecimal text representation of number
+       uint8_t * const string, // address where to create string
+       uint32_t value,
+       const uint8_t min_digits,
+       const uint8_t max_digits,
+       uint8_t uppercase // bool, 0xABCDEF, or 0xabcdef?
+); // return actual number of digits
+
+extern volatile uint32_t main_timer; // milliseconds since start
diff --git a/makefile b/makefile
new file mode 100644 (file)
index 0000000..17ade47
--- /dev/null
+++ b/makefile
@@ -0,0 +1,173 @@
+# Copyright 2022 Balthasar Szczepański
+#
+# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+#
+# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+# My replacement for the makefile generated by e2studio
+
+# PATHS BELOW ARE SPECIFIC TO MY MACHINE,
+# UPDATE TO WHERE YOUR TOOLCHAIN IS LOCATED
+# Also it is assumed that rl78-elf-* tools are in PATH
+
+OBJCOPY := rl78-elf-objcopy
+LD      := rl78-elf-ld
+SIZE    := rl78-elf-size
+GCC     := rl78-elf-gcc
+
+LIB += \
+--start-group \
+-loptm \
+-loptc \
+-lgcc \
+--end-group
+
+LIB_DIR += \
+-L"/usr/rl78-elf/rl78-elf/lib/g14" \
+-L"/usr/rl78-elf/lib/gcc/rl78-elf/4.9.2.201604-GNURL78/g14"
+
+OPTLIB_INC := -I "/usr/rl78-elf/rl78-elf/optlibinc"
+
+OPT_STACK    := -Wstack-usage=312
+OPT_PLATFORM := -mg14
+
+OPT_D_C := -x c -nostdinc $(OPTLIB_INC) $(OPT_STACK) $(OPT_PLATFORM)
+OPT_O_C := -c $(OPT_D_C)
+# OPT_D_C := -x assembler-with-cpp -nostdinc $(OPTLIB_INC) $(OPT_STACK) $(OPT_PLATFORM)
+OPT_D_ASM := -x assembler -nostdinc $(OPTLIB_INC) $(OPT_STACK) $(OPT_PLATFORM)
+OPT_O_ASM := -c $(OPT_D_ASM)
+
+GCC_TOOL := gcc
+OPT_TOOL := -Wall
+LIB_M    := -lm
+LIB_IL   := -lIL
+RM       := rm -rf
+
+HEX += \
+klavirko-ui.hex \
+klavirko-ui.mot 
+
+OUT += \
+klavirko-ui.x
+
+OBJS += \
+interrupt_handlers.o \
+reset_program.o \
+vector_table.o \
+main.o \
+aix.o \
+lcd.o \
+gui.o \
+wave.o \
+ctrl.o \
+fs.o \
+debug.o
+
+CLEAN += \
+       $(OBJS) \
+       $(OUT) \
+       $(HEX) \
+       *.lst \
+       *.map \
+       *.x \
+       *.o \
+       *.d \
+       font.h \
+       sinus.h \
+       img2fnt \
+       sinus \
+       memory.ld \
+       size.tmp \
+
+
+all: $(OUT) $(HEX)
+       @echo 'complete'
+
+clean:
+       $(RM) $(CLEAN)
+
+
+klavirko-ui.hex: klavirko-ui.x
+       $(OBJCOPY) -O ihex klavirko-ui.x klavirko-ui.hex
+
+klavirko-ui.mot: klavirko-ui.x
+       $(OBJCOPY) -O srec klavirko-ui.x klavirko-ui.mot
+
+klavirko-ui.x: $(OBJS) memory.ld LinkerSubCommand.tmp
+       $(LD) -o klavirko-ui.x -T"memory.ld" @"LinkerSubCommand.tmp" -M=klavirko-ui.map $(LIB_DIR) $(LIB) -e_PowerON_Reset
+
+
+memory.ld: metalinker.pl memory_template.ld size.tmp LinkerSubCommand.tmp
+       ./metalinker.pl LinkerSubCommand.tmp size.tmp <memory_template.ld >memory.ld
+
+size.tmp: $(OBJS)
+       $(SIZE) $(OBJS) -A > size.tmp
+
+
+reset_program.o: reset_program.asm
+       $(GCC) -MM -MP -MF reset_program.o -MT reset_program.o -MT reset_program.d $(OPT_D_ASM) reset_program.asm
+       $(GCC) -Wa,-adlhn=reset_program.lst -o reset_program.o $(OPT_O_ASM) reset_program.asm
+
+interrupt_handlers.o: interrupt_handlers.c interrupt_handlers.h aix.h fs.h gui.h main.h ctrl.h
+       $(GCC) -MM -MP -MF interrupt_handlers.o -MT interrupt_handlers.o -MT interrupt_handlers.d $(OPT_D_C) interrupt_handlers.c
+       $(GCC) -Wa,-adlhn=interrupt_handlers.lst -o interrupt_handlers.o $(OPT_O_C) interrupt_handlers.c
+
+vector_table.o: vector_table.c interrupt_handlers.h aix.h ctrl.h gui.h lcd.h
+       $(GCC) -MM -MP -MF vector_table.o -MT vector_table.o -MT vector_table.d $(OPT_D_C) vector_table.c
+       $(GCC) -Wa,-adlhn=vector_table.lst -o vector_table.o $(OPT_O_C) vector_table.c
+       
+main.o: main.c main.h aix.h lcd.h gui.h ctrl.h fs.h
+       $(GCC) -MM -MP -MF main.o -MT main.o -MT main.d $(OPT_D_C) main.c
+       $(GCC) -Wa,-adlhn=main.lst -o main.o $(OPT_O_C) main.c
+       
+aix.o: aix.c aix.h debug.h wave.h gui.h
+       $(GCC) -MM -MP -MF aix.o -MT aix.o -MT aix.d $(OPT_D_C) aix.c
+       $(GCC) -Wa,-adlhn=aix.lst -o aix.o $(OPT_O_C) aix.c
+       
+lcd.o: lcd.c lcd.h main.h debug.h font.h
+       $(GCC) -MM -MP -MF lcd.o -MT lcd.o -MT lcd.d $(OPT_D_C) lcd.c
+       $(GCC) -Wa,-adlhn=lcd.lst -o lcd.o $(OPT_O_C) lcd.c
+       
+gui.o: gui.c gui.h debug.h wave.h lcd.h main.h fs.h ctrl.h
+       $(GCC) -MM -MP -MF gui.o -MT gui.o -MT gui.d $(OPT_D_C) gui.c
+       $(GCC) -Wa,-adlhn=gui.lst -o gui.o $(OPT_O_C) gui.c
+       
+wave.o: wave.c wave.h aix.h debug.h ctrl.h sinus.h
+       $(GCC) -MM -MP -MF wave.o -MT wave.o -MT wave.d $(OPT_D_C) wave.c
+       $(GCC) -Wa,-adlhn=wave.lst -o wave.o $(OPT_O_C) wave.c
+       
+ctrl.o: ctrl.c ctrl.h wave.h debug.h gui.h
+       $(GCC) -MM -MP -MF ctrl.o -MT ctrl.o -MT ctrl.d $(OPT_D_C) ctrl.c
+       $(GCC) -Wa,-adlhn=ctrl.lst -o ctrl.o $(OPT_O_C) ctrl.c
+       
+fs.o: fs.c fs.h debug.h main.h gui.h wave.h
+       $(GCC) -MM -MP -MF fs.o -MT fs.o -MT fs.d $(OPT_D_C) fs.c
+       $(GCC) -Wa,-adlhn=fs.lst -o fs.o $(OPT_O_C) fs.c
+       
+debug.o: debug.c debug.h main.h
+       $(GCC) -MM -MP -MF debug.o -MT debug.o -MT debug.d $(OPT_D_C) debug.c
+       $(GCC) -Wa,-adlhn=debug.lst -o debug.o $(OPT_O_C) debug.c
+
+
+sinus.h: sinus
+       ./sinus > sinus.h
+
+font.h: img2fnt font16.png font8.png
+       ./img2fnt FONT8 font8.png > font.h
+       ./img2fnt FONT16 font16.png >> font.h
+
+
+sinus: sinus.c
+       $(GCC_TOOL) $(OPT_TOOL) $(LIB_M) -o sinus sinus.c
+
+img2fnt: img2fnt.c
+       $(GCC_TOOL) $(OPT_TOOL) $(LIB_IL) -o img2fnt img2fnt.c
+
+
+.PHONY: all clean
+# What is secondary? Please explain.
+.SECONDARY:
diff --git a/makefile_orig b/makefile_orig
new file mode 100644 (file)
index 0000000..016ce1e
--- /dev/null
@@ -0,0 +1,134 @@
+#initially autogenerated file.
+#now I'm taking it over manually.
+
+
+# Remove command components
+RM := rm -rf *.lst *.lis *.lpp *.map libgcc.a *.x *.o *.d
+
+# PATHS BELOW ARE SPECIFIC TO MY MACHINE,
+# UPDATE TO WHERE YOUR TOOLCHAIN IS LOCATED
+# Also it is assumed that rl78-elf-* tools are in PATH.
+
+# Compiler includes
+OPTLIB_INC := "/usr/rl78-elf/rl78-elf/optlibinc"
+
+# Linker includes
+LIB_INC1 := "/usr/rl78-elf/rl78-elf/lib/g14"
+LIB_INC2 := "/usr/rl78-elf/lib/gcc/rl78-elf/4.9.2.201604-GNURL78/g14"
+
+# All of the sources participating in the build are defined here
+
+OBJS += \
+./interrupt_handlers.o \
+./reset_program.o \
+./vector_table.o \
+./main.o \
+./aix.o \
+./lcd.o \
+./gui.o \
+./wave.o \
+./ctrl.o \
+./fs.o \
+./debug.o
+
+
+# Add inputs and outputs from these tool invocations to the build variables 
+LINKER_OUTPUT_OUTPUTS += \
+klavirko-ui.x \
+
+
+# All Target
+# Main-build Target
+all: klavirko-ui.mot klavirko-ui.hex
+       @echo 'Build complete.'
+
+# Tool invocations
+klavirko-ui.mot: $(LINKER_OUTPUT_OUTPUTS)
+       rl78-elf-objcopy -O srec  $(LINKER_OUTPUT_OUTPUTS)"klavirko-ui.mot"
+
+klavirko-ui.hex: $(LINKER_OUTPUT_OUTPUTS)
+       rl78-elf-objcopy -O ihex  $(LINKER_OUTPUT_OUTPUTS)"klavirko-ui.hex"
+
+klavirko-ui.x: $(OBJS) $(LIBRARY_GENERATOR_OUTPUTTYPE_OUTPUTS) $(ALL_ASMS) memory.ld LinkerSubCommand.tmp
+       rl78-elf-ld -o "klavirko-ui.x" -T"memory.ld" @"LinkerSubCommand.tmp" $(USER_OBJS) $(LIBS) $(LIBRARY_GENERATOR_OUTPUTTYPE_OUTPUTS) -M=klavirko-ui.map -L$(LIB_INC1) -L$(LIB_INC2) --start-group -loptm -loptc -lgcc --end-group -e_PowerON_Reset
+
+memory.ld: ./metalinker.pl ./memory_template.ld size.tmp LinkerSubCommand.tmp
+       ./metalinker.pl LinkerSubCommand.tmp size.tmp <memory_template.ld >memory.ld
+
+size.tmp: $(OBJS)
+       rl78-elf-size *.o -A > size.tmp
+
+
+# Build the objects
+
+# TODO: consider defining all repeating parameters as variable to avoid repetition.
+# hardware_setup.o: ./hardware_setup.c
+# rl78-elf-gcc -MM -MP -MF "hardware_setup.d" -MT"hardware_setup.o" -MT"hardware_setup.d" -x c   -nostdinc -I$(OPTLIB_INC) -mg14 "$<"
+# rl78-elf-gcc -c -x c  -Wa,-adlhn="$(basename $(notdir $<)).lst" -nostdinc -I"$(OPTLIB_INC)" -mg14 -o "$(@:%.d=%.o)" "$<"
+
+interrupt_handlers.o: ./interrupt_handlers.c ./interrupt_handlers.h ./aix.h ./fs.h ./gui.h ./main.h ./ctrl.h
+       rl78-elf-gcc -MM -MP -MF "interrupt_handlers.d" -MT"interrupt_handlers.o" -MT"interrupt_handlers.d" -x c   -nostdinc -I$(OPTLIB_INC) -Wstack-usage=312 -mg14 "$<"
+       rl78-elf-gcc -c -x c  -Wa,-adlhn="$(basename $(notdir $<)).lst" -nostdinc -I$(OPTLIB_INC) -Wstack-usage=312 -mg14 -o "$(@:%.d=%.o)" "$<"
+
+reset_program.o: ./reset_program.asm
+       rl78-elf-gcc -MM -MP -MF "reset_program.d" -MT"reset_program.o" -MT"reset_program.d" -x assembler-with-cpp   -nostdinc -I$(OPTLIB_INC) -Wstack-usage=312 -mg14 "$<"
+       rl78-elf-gcc -c -x assembler-with-cpp  -Wa,-adlhn="$(basename $(notdir $<)).lst" -nostdinc -I$(OPTLIB_INC) -Wstack-usage=312 -mg14 -o "$(@:%.d=%.o)" "$<"
+
+main.o: ./main.c ./main.h ./aix.h ./lcd.h ./gui.h ./ctrl.h ./fs.h
+       rl78-elf-gcc -MM -MP -MF "main.d" -MT"main.o" -MT"main.d" -x c   -nostdinc -I$(OPTLIB_INC) -Wstack-usage=312 -mg14 "$<"
+       rl78-elf-gcc -c -x c  -Wa,-adlhn="$(basename $(notdir $<)).lst" -nostdinc -I$(OPTLIB_INC) -Wstack-usage=312 -mg14 -o "$(@:%.d=%.o)" "$<"
+
+aix.o: ./aix.c ./aix.h ./debug.h ./wave.h ./gui.h
+       rl78-elf-gcc -MM -MP -MF "aix.d" -MT"aix.o" -MT"aix.d" -x c   -nostdinc -I$(OPTLIB_INC) -Wstack-usage=312 -mg14 "$<"
+       rl78-elf-gcc -c -x c  -Wa,-adlhn="$(basename $(notdir $<)).lst" -nostdinc -I$(OPTLIB_INC) -Wstack-usage=312 -mg14 -o "$(@:%.d=%.o)" "$<"
+
+lcd.o: ./lcd.c ./lcd.h ./main.h ./debug.h font.h
+       rl78-elf-gcc -MM -MP -MF "lcd.d" -MT"lcd.o" -MT"lcd.d" -x c   -nostdinc -I$(OPTLIB_INC) -Wstack-usage=312 -mg14 "$<"
+       rl78-elf-gcc -c -x c  -Wa,-adlhn="$(basename $(notdir $<)).lst" -nostdinc -I$(OPTLIB_INC) -Wstack-usage=312 -mg14 -o "$(@:%.d=%.o)" "$<"
+
+gui.o: ./gui.c ./gui.h ./debug.h ./wave.h ./lcd.h ./main.h ./fs.h ./ctrl.h
+       rl78-elf-gcc -MM -MP -MF "gui.d" -MT"gui.o" -MT"gui.d" -x c   -nostdinc -I$(OPTLIB_INC) -Wstack-usage=312 -mg14 "$<"
+       rl78-elf-gcc -c -x c  -Wa,-adlhn="$(basename $(notdir $<)).lst" -nostdinc -I$(OPTLIB_INC) -Wstack-usage=312 -mg14 -o "$(@:%.d=%.o)" "$<"
+
+debug.o: ./debug.c ./debug.h ./main.h
+       rl78-elf-gcc -MM -MP -MF "debug.d" -MT"debug.o" -MT"debug.d" -x c   -nostdinc -I$(OPTLIB_INC) -Wstack-usage=312 -mg14 "$<"
+       rl78-elf-gcc -c -x c  -Wa,-adlhn="$(basename $(notdir $<)).lst" -nostdinc -I$(OPTLIB_INC) -Wstack-usage=312 -mg14 -o "$(@:%.d=%.o)" "$<"
+
+wave.o: ./wave.c ./wave.h ./aix.h ./debug.h ./ctrl.h sinus.h
+       rl78-elf-gcc -MM -MP -MF "wave.d" -MT"wave.o" -MT"wave.d" -x c   -nostdinc -I$(OPTLIB_INC) -Wstack-usage=312 -mg14 "$<"
+       rl78-elf-gcc -c -x c  -Wa,-adlhn="$(basename $(notdir $<)).lst" -nostdinc -I$(OPTLIB_INC) -Wstack-usage=312 -mg14 -o "$(@:%.d=%.o)" "$<"
+
+ctrl.o: ./ctrl.c ./ctrl.h ./wave.h ./debug.h ./gui.h
+       rl78-elf-gcc -MM -MP -MF "ctrl.d" -MT"ctrl.o" -MT"ctrl.d" -x c   -nostdinc -I$(OPTLIB_INC) -Wstack-usage=312 -mg14 "$<"
+       rl78-elf-gcc -c -x c  -Wa,-adlhn="$(basename $(notdir $<)).lst" -nostdinc -I$(OPTLIB_INC) -Wstack-usage=312 -mg14 -o "$(@:%.d=%.o)" "$<"
+
+fs.o: ./fs.c ./fs.h ./debug.h ./main.h ./gui.h ./wave.h
+       rl78-elf-gcc -MM -MP -MF "fs.d" -MT"fs.o" -MT"fs.d" -x c   -nostdinc -I$(OPTLIB_INC) -Wstack-usage=312 -mg14 "$<"
+       rl78-elf-gcc -c -x c  -Wa,-adlhn="$(basename $(notdir $<)).lst" -nostdinc -I$(OPTLIB_INC) -Wstack-usage=312 -mg14 -o "$(@:%.d=%.o)" "$<"
+
+vector_table.o: ./vector_table.c ./interrupt_handlers.h ./aix.h ./ctrl.h ./gui.h ./lcd.h
+       rl78-elf-gcc -MM -MP -MF "vector_table.d" -MT"vector_table.o" -MT"vector_table.d" -x c   -nostdinc -I$(OPTLIB_INC) -Wstack-usage=312 -mg14 "$<"
+       rl78-elf-gcc -c -x c  -Wa,-adlhn="$(basename $(notdir $<)).lst" -nostdinc -I$(OPTLIB_INC) -Wstack-usage=312 -mg14 -o "$(@:%.d=%.o)" "$<"
+
+sinus.h: sinus
+       ./sinus > sinus.h
+
+sinus: ./sinus.c
+       gcc -lm -Wall -o sinus sinus.c
+
+font.h: img2fnt ./font16.png ./font8.png
+       ./img2fnt FONT8 ./font8.png > font.h
+       ./img2fnt FONT16 ./font16.png >> font.h
+
+img2fnt: ./img2fnt.c
+       gcc -lIL -Wall -o img2fnt img2fnt.c
+
+
+# Other Targets
+clean:
+       rm -rf   klavirko-ui.mot klavirko-ui.hex font.h sinus.h img2fnt sinus memory.ld
+       $(RM)
+
+# What is phony? Please explain.
+.PHONY: all clean dependents
+.SECONDARY:
diff --git a/memory_template.ld b/memory_template.ld
new file mode 100644 (file)
index 0000000..7b2365c
--- /dev/null
@@ -0,0 +1,149 @@
+/* memory.ld is generated from memory_template.ld by the meta linker script */
+/*
+my replacement for linker_script.gsi generated by e2studio.
+You will notice much similarity to the replaced thing.
+That's because these things must have such values to work correctly.
+Which makes the replacement kind of a pointless work as in the end
+almost the same thing comes out. But now it is mine.
+So, I believe this should not even be copyrightable,
+In case it is (after all, I did a creative trick here, however most of
+the actual creativity is in the meta linker script) then consider it
+copyrighted and released under the same license as the other files:
+*/
+/*
+Copyright 2021, 2022 Balthasar Szczepański
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+MEMORY
+{
+       VEC     : ORIGIN = 0x00000, LENGTH =     0x4
+       IVEC    : ORIGIN = 0x00004, LENGTH =    0xBC
+       OPT     : ORIGIN = 0x000C0, LENGTH =     0x4
+       SEC_ID  : ORIGIN = 0x000C4, LENGTH =     0xA
+       OCDSTAD : ORIGIN = 0x000CE, LENGTH =     0xA
+       ROM     : ORIGIN = 0x000D8, LENGTH = 0x1FD28
+       OCDROM  : ORIGIN = 0x1FE00, LENGTH =   0x200
+       /* 20000 - E0000 : unused */
+       /* F0000 - F2FFF : special stuff */
+       MIRROR  : ORIGIN = 0xF3000, LENGTH =  0x8F00
+       RAM     : ORIGIN = 0xFBF00, LENGTH =  0x4000
+       /* FFF00 - FFFFF : special stuff */
+}
+
+SECTIONS
+{
+       /* platform-specific stuff. must be here */
+       .vec 0x0 : AT (0x0)
+       {
+               KEEP(*(.vec))
+       } > VEC
+       .vects 0x4 : AT (0x4)
+       {
+               KEEP(*(.vects))
+       } > IVEC
+       .option_bytes 0xC0 : AT (0xC0)
+       {
+               KEEP(*(.option_bytes))
+       } > OPT
+       .security_id 0xC4 : AT (0xC4)
+       {
+               KEEP(*(.security_id))
+       } > SEC_ID
+       
+       /* here, the actually interesting stuff starts */
+       
+       /* low text - code with as low address as possible - reset, interrupt, etc. */
+       .lowtext 0xD8 : AT (0xD8)
+       {
+               *(.plt)
+               *(.lowtext)
+               . = ALIGN(2);
+               _mdata = .;
+       } > ROM
+       
+       /* initial values for .data implicitly put here */
+       
+       /* text - normal code */
+       .text (. + __romdatacopysize) : 
+       {
+               /* here the meta linker will insert list of modules before mirror: */
+               /*OBJ_LOW_TEXT*/
+       } > ROM
+       
+       /* here we interrupt the code, to insert data in the mirror area */
+       
+       /* read-only data, which has to be in mirror */
+       .rodata MAX(., 0x3000) : 
+       {
+               . = ALIGN(2);
+               *(.rodata)
+               *(.rodata.*)
+               _erodata = .;
+               ASSERT ((LOADADDR(.rodata) + SIZEOF(.rodata)) <= 0xbf00,"Error: no room left for .rodata");
+       } > ROM
+       
+       /* resume with the code now */
+       
+       /* text2 - normal code again - all modules which didn't get to text */
+       .text2 :
+       {
+               *(.text)
+               *(.text.*)
+               etext = .;
+               . = ALIGN(2);
+       } > ROM
+       
+       PROVIDE (__rl78_abs__ = 0); /*?*/
+       
+       /* .init, .fini, .got, could get here, however at this point not needed */
+       
+       /* far read-only data which does not have to be in mirror */
+       .frodata : 
+       {
+               *(.frodata)
+       } > ROM
+       
+       
+       /* end of flash, start of ram */
+       
+       .data 0xFBF00 : AT (_mdata)
+       {
+               . = ALIGN(2);
+               _data = .;
+               *(.data)
+               *(.data.*)
+               . = ALIGN(2);
+               _edata = .;
+       } > RAM
+
+       PROVIDE (__romdatacopysize = SIZEOF(.data));
+
+       .bss : 
+       {
+               . = ALIGN(2);
+               _bss = .;
+               *(.bss)
+               *(.bss.**)
+               . = ALIGN(2);
+               *(COMMON)
+               . = ALIGN(2);
+               _ebss = .;
+               _end = .;
+       } > RAM
+
+       PROVIDE (stack_size = 0x190);
+
+       .stack 0xFFEDC (NOLOAD)  : AT (0xFFEDC)
+       {
+               _stack = .;
+               ASSERT ((_stack > (_end + stack_size)),"Error: Too much data - no room left for the stack");
+       } > RAM
+}
diff --git a/metalinker.pl b/metalinker.pl
new file mode 100755 (executable)
index 0000000..4b4f66b
--- /dev/null
@@ -0,0 +1,256 @@
+#!/usr/bin/perl
+# linker script generator: META LINKER?
+#
+# Copyright 2021 Balthasar Szczepański
+#
+# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+#
+# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+use strict;
+
+# modules which should always stay at low address
+use constant ALWAYS_LOW => {
+       # 'hardware_setup.o' => 1,
+       'interrupt_handlers.o' => 1,
+       'reset_program.o' => 1,
+       'vector_table.o' => 1,
+};
+
+use constant ROM_START => 0x000D8;
+use constant MIRROR_START => 0x03000;
+use constant MIRROR_END   => 0x0BEFF;
+
+my $listfile;
+my $sizefile;
+my $line;
+my @obj_low;
+my @obj_high;
+my @obj_check;
+my @obj_low_text;
+my @obj_high_text;
+my %data_size;
+my %rodata_size;
+my %text_size;
+my %lowtext_size;
+my $all_data = 0;
+my $all_rodata = 0;
+my $all_text = 0;
+my $low_text = 0;
+my $ptr;
+my $n_layout;
+my $max_layout;
+my $m_layout;
+
+if (@ARGV<2) {
+       print STDERR $0." objects_list_file objects_sizes_file\n";
+       exit;
+}
+
+# get list of modules & categorise
+
+unless (open ($listfile, "<", $ARGV[0])) {
+       print STDERR 'can\'t read '.$ARGV[0]."\n";
+       exit;
+}
+$line = <$listfile>;
+close ($listfile);
+
+while (length($line)>0) {
+       if ($line =~ /^\s*"([^"]*)"\s*/){
+               if (exists(ALWAYS_LOW->{$1})){
+                       push(@obj_low, $1);
+               }
+               else {
+                       push(@obj_check, $1);
+               }
+               $line = substr($line, $+[0]);
+       }
+       elsif ($line =~ /^\s*([^\s]*)\s*/){
+               if (exists(ALWAYS_LOW->{$1})){
+                       push(@obj_low, $1);
+               }
+               else {
+                       push(@obj_check, $1);
+               }
+               $line = substr($line, $+[0]);
+       }
+       else {
+               last;
+       }
+}
+
+$n_layout = @obj_check;
+$max_layout = (1 << $n_layout) - 1;
+
+# get module sizes
+
+unless (open ($sizefile, "<", $ARGV[1])) {
+       print STDERR 'can\'t read '.$ARGV[1]."\n";
+       exit;
+}
+{
+       my @words;
+       my $name;
+       while (defined($line = <$sizefile>)) {
+               $line =~ s/[\n]$//g;
+               @words = split (/\s*\t\s*/,$line);
+               
+               if ($line =~ /^([^\s]+)\s*:/) {
+                       $name = $1;
+                       $data_size{$name} = 0;
+                       $rodata_size{$name} = 0;
+                       $text_size{$name} = 0;
+                       $lowtext_size{$name} = 0;
+               }
+               # elsif ($line =~ /^\.((text)|(comment)|(debug_.*))\s+([0-9]+)/) {
+                       # $text_size{$name} += int($5);
+               # }
+               elsif ($line =~ /^\.text\s+([0-9]+)/) {
+                       $text_size{$name} += int($1);
+               }
+               elsif ($line =~ /^\.lowtext\s+([0-9]+)/) {
+                       $lowtext_size{$name} += int($1);
+               }
+               elsif ($line =~ /^\.data\s+([0-9]+)/) {
+                       $data_size{$name} += int($1);
+               }
+               elsif ($line =~ /^\.rodata\s+([0-9]+)/) {
+                       $rodata_size{$name} += int($1);
+               }
+       }
+}
+close ($sizefile);
+
+# combine sizes
+
+foreach my $obj (@obj_low) {
+       $all_data += $data_size{$obj};
+       $all_rodata += $rodata_size{$obj};
+       $low_text += $lowtext_size{$obj} + $text_size{$obj};
+       $all_text += $lowtext_size{$obj} + $text_size{$obj};
+}
+foreach my $obj (@obj_check) {
+       $all_data += $data_size{$obj};
+       $all_rodata += $rodata_size{$obj};
+       $low_text += $lowtext_size{$obj};
+       $all_text += $lowtext_size{$obj} + $text_size{$obj};
+}
+
+# calculate base address
+
+$ptr = ROM_START;
+if ($ptr & 0x1) {
+       ++$ptr;
+}
+# print STDERR sprintf ("ROM START    %05X\n",$ptr);
+$ptr += $all_data;
+if ($ptr & 0x1) {
+       ++$ptr;
+}
+# print STDERR sprintf ("TEXT START   %05X\n",$ptr);
+$ptr += $low_text;
+# print STDERR sprintf ("TEXT 1 START %05X\n",$ptr);
+
+# find a valid layout
+
+$m_layout = find_layout();
+
+# my @ds = %data_size;
+# my @rs = %rodata_size;
+# my @ts = %text_size;
+
+# print STDERR "DATA @ds\nRODATA @rs\nTEXT @ts\n";
+
+# prepare replacement texts
+
+for(my $i=0; $i<$n_layout; ++$i) {
+       if ($m_layout & (1<<$i)) {
+               push(@obj_low, $obj_check[$i]);
+       }
+       else {
+               push(@obj_high, $obj_check[$i]);
+       }
+}
+
+# fill template with replacement texts
+
+foreach my $obj (@obj_low) {
+       push (@obj_low_text, $obj);
+       push (@obj_low_text, '(.text)');
+}
+foreach my $obj (@obj_high) {
+       push (@obj_high_text, $obj);
+       push (@obj_high_text, '(.text)');
+}
+while (defined($line = <STDIN>)) {
+       $line =~ s/\/\*OBJ_LOW\*\//@obj_low/;
+       $line =~ s/\/\*OBJ_HIGH\*\//@obj_high/;
+       $line =~ s/\/\*OBJ_ALL\*\//@obj_low @obj_high/;
+       $line =~ s/\/\*OBJ_LOW_TEXT\*\//@obj_low_text/;
+       $line =~ s/\/\*OBJ_HIGH_TEXT\*\//@obj_high_text/;
+       $line =~ s/\/\*OBJ_ALL_TEXT\*\//@obj_low_text @obj_high_text/;
+       print $line;
+}
+
+
+sub find_layout {
+       my $best_layout = $max_layout;
+       my $best_score = 0x100000;
+       my $layout;
+       my $score;
+       
+       if (check_layout($max_layout) >= 0) {
+               return $max_layout;
+       }
+       
+       for ($layout = $max_layout>>1; $layout != 0; $layout >>= 1) {
+               $score = check_layout($layout);
+               if ($score == 0) {
+                       return $layout;
+               }
+               elsif (($score >0) && ($score < $best_score)) {
+                       $best_layout = $layout;
+                       $best_score = $score;
+               }
+       }
+       
+       for ($layout = 0; $layout <= $max_layout; ++$layout) {
+               $score = check_layout($layout);
+               if ($score == 0) {
+                       return $layout;
+               }
+               elsif (($score >0) && ($score < $best_score)) {
+                       $best_layout = $layout;
+                       $best_score = $score;
+               }
+       }
+       
+       return $best_layout;
+}
+
+sub check_layout {
+       (my $layout) = @_;
+       my $add_text = 0;
+       my $pos;
+       
+       for(my $i=0; $i<$n_layout; ++$i) {
+               if ($layout & (1<<$i)) {
+                       $add_text += $text_size{$obj_check[$i]};
+               }
+       }
+       
+       $pos = $ptr + $add_text;
+       if ($pos < MIRROR_START) {
+               return MIRROR_START - $pos;
+       }
+       $pos += $all_rodata;
+       if ($pos > (MIRROR_END + 1)) {
+               return -1;
+       }
+       return 0;
+}
diff --git a/reset_program.asm b/reset_program.asm
new file mode 100644 (file)
index 0000000..4a3c4cc
--- /dev/null
@@ -0,0 +1,96 @@
+;; Copyright 2022 Balthasar Szczepański
+;; 
+;; Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+;; 
+;; 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+;; 
+;; 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+;; 
+;; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+;; My replacement for the reset_program.asm generated by e2studio
+
+       .list
+       .section .lowtext
+       
+       .extern _stack
+       
+       .extern _mdata
+       .extern _data
+       .extern _edata
+       
+       .extern _bss
+       .extern _ebss
+       
+       .extern _main
+       .extern _exit ;; but why; isn't _exit defined here?
+       
+       .global _PowerON_Reset
+       .short  _PowerON_Reset
+       
+_PowerON_Reset:
+       
+;; step 1: set the stack pointer
+       
+       movw sp,  #_stack
+       
+;; step 2: init .data
+init_data:
+       
+       mov  es,  #0  ;; because we copy from 0xxxx to Fxxxx
+       
+       movw de,  #_mdata ;; .data in FLASH start
+       movw hl,  #_data  ;; .data in RAM start
+       movw bc,  #_edata ;; .data in RAM end
+
+init_data_loop:
+       movw ax,  es:[de] ;; get word from flash
+       movw [hl],ax      ;; put word to ram
+       
+       incw de ;; increment pointers to next word
+       incw de
+       incw hl
+       incw hl
+       
+       movw ax, hl          ;; prepare to compare
+       cmpw ax, bc          ;; did we reach the end?
+       bnz  $init_data_loop ;; if not: another iteration
+       
+;; step 3: init .bss
+init_bss:
+       
+       movw hl,  #_bss  ;; .bss in RAM start
+       movw bc,  #_ebss ;; .bss in RAM end
+       
+init_bss_loop:
+       movw ax,  #0
+       movw [hl],ax ;; put 0 to ram
+       
+       incw hl ;; increment pointer to next word
+       incw hl
+       
+       movw ax,  hl        ;; prepare to compare
+       cmpw ax,  bc        ;; did we reach the end?
+       bnz  $init_bss_loop ;; if not: another iteration
+       
+;; step 4: call main()
+call_main:
+       
+       movw ax,  #0
+       ;;push ax
+       ;;push ax
+       ;;push ax
+       
+       ;; removed pointless passing of argc, argv, envp
+       ;; redefined main as void main (void)
+       
+       call !!_main
+       
+;; step 5: main() has returned / exited for some reason:
+_exit:
+       
+       ;;br   $_exit ;; in this case, get stuck forever
+       br   $_PowerON_Reset ;; in this case, restart from beginning
+       
+       .end
+       
\ No newline at end of file
diff --git a/sinus.c b/sinus.c
new file mode 100644 (file)
index 0000000..eb4c4a6
--- /dev/null
+++ b/sinus.c
@@ -0,0 +1,39 @@
+/* generator of .h file with sine wave */
+/*
+Copyright 2021 Balthasar Szczepański
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#define _USE_MATH_DEFINES
+#include <math.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <inttypes.h>
+
+int main(int argc, char **argv)
+{
+       uint16_t i;
+       uint32_t j;
+
+       printf("//autogenerated by sinus.c\n");
+       printf("#define DEFAULT_SINE \\\n{");
+       for (i=0; i<256; ++i)
+       {
+               j = ((uint16_t)(16*(cos(i * M_PI / 128) + 1) * 255/2 + 0.5) - 128*16) & 0xffff;
+               printf("%s0x%04" PRIX32 "%s", ((i%8)==0)?" \\\n\t":" ", j,(i==255)?"":",");
+       }
+       printf (" \\\n}\n#define DEFAULT_SAMPLE \\\n{");
+       for (i=0; i<256; ++i)
+       {
+               j = ((uint16_t)((cos(i * M_PI / 128) + 1) * 255/2 + 0.5) - 128) & 0xff;
+               printf("%s0x%02" PRIX32 "%s", ((i%8)==0)?" \\\n\t":" ", j,(i==255)?"":",");
+       }
+       printf (" \\\n}\n");
+}
diff --git a/vector_table.c b/vector_table.c
new file mode 100644 (file)
index 0000000..42b85ce
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+Copyright 2021 Balthasar Szczepański
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include "interrupt_handlers.h"
+#include "aix.h"
+#include "ctrl.h"
+#include "gui.h"
+#include "lcd.h"
+
+extern void PowerON_Reset (void);
+
+const unsigned char Option_Bytes[]  __attribute__ ((section (".option_bytes"))) = {
+       0xef,       // default
+       0b01110111, // LV reset: 2.95V - 3.02V
+       0b11111000, // HS fhoco=64Mhz, fih = 32MHz
+       0x85        // default
+};
+
+const unsigned char Security_Id[]  __attribute__ ((section (".security_id"))) = {
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+const void *HardwareVectors[] __attribute__ ((section (".vec"))) = {
+       // Address 0x0
+       PowerON_Reset,
+       // Secure for Debugging
+       (void*)0xFFFF
+};
+
+const void *Vectors[] __attribute__ ((section (".vects"))) = {
+       FALLBACK_INT,  // INT_SRO/INT_WDTI (0x4)
+       FALLBACK_INT,  // INT_LVI (0x6)
+       FALLBACK_INT,  // INT_P0 (0x8)
+       FALLBACK_INT,  // INT_P1 (0xA)
+       FALLBACK_INT,  // INT_P2 (0xC)
+       FALLBACK_INT,  // INT_P3 (0xE)
+       FALLBACK_INT,  // INT_P4 (0x10)
+       FALLBACK_INT,  // INT_P5 (0x12)
+       FALLBACK_INT,  // INT_CSI20/INT_IIC20/INT_ST2 (0x14)
+       FALLBACK_INT,  // INT_CSI21/INT_IIC21/INT_SR2 (0x16)
+       FALLBACK_INT,  // INT_SRE2 (0x18)
+       (void*)0xFFFF, // Padding
+       (void*)0xFFFF, // Padding
+       FALLBACK_INT,  // INT_CSI00/INT_IIC00/INT_ST0 (0x1E)
+       int_aix_rx,///////INT_CSI01/INT_IIC01/INT_SR0 (0x20)
+       int_aix_err,//////INT_SRE0/INT_TM01H (0x22)
+       FALLBACK_INT,  // INT_ST1 (0x24)
+       int_ctrl_rx,//////INT_CSI11/INT_IIC11/INT_SR1 (0x26)
+       int_ctrl_err,/////INT_SRE1/INT_TM03H (0x28)
+       FALLBACK_INT,  // INT_IICA0 (0x2A)
+       FALLBACK_INT,  // INT_TM00 (0x2C)
+       FALLBACK_INT,  // INT_TM01 (0x2E)
+       FALLBACK_INT,  // INT_TM02 (0x30)
+       FALLBACK_INT,  // INT_TM03 (0x32)
+       INT_AD,///////////INT_AD (0x34)
+       FALLBACK_INT,  // INT_RTC (0x36)
+       FALLBACK_INT,  // INT_IT (0x38)
+       int_button_y,/////INT_KR (0x3A)
+       (void*)0xFFFF, // Padding
+       (void*)0xFFFF, // Padding
+       int_lcd_pwm,//////INT_TRJ0 (0x40)
+       (void*)0xFFFF, // Padding
+       (void*)0xFFFF, // Padding
+       (void*)0xFFFF, // Padding
+       (void*)0xFFFF, // Padding
+       FALLBACK_INT,  // INT_P6 (0x4A)
+       (void*)0xFFFF, // Padding
+       int_jog,//////////INT_P8 (0x4E)
+       int_jog,//////////INT_P9 (0x50)
+       FALLBACK_INT,  // INT_CMP0/INT_P10 (0x52)
+       FALLBACK_INT,  // INT_CMP1/INT_P11 (0x54)
+       FALLBACK_INT,  // INT_TRD0 (0x56)
+       FALLBACK_INT,  // INT_TRD1 (0x58)
+       FALLBACK_INT,  // INT_TRG (0x5A)
+       (void*)0xFFFF, // Padding
+       (void*)0xFFFF, // Padding
+       (void*)0xFFFF, // Padding
+       FALLBACK_INT,  // INT_FL (0x62)
+       (void*)0xFFFF, // Padding
+       (void*)0xFFFF, // Padding
+       (void*)0xFFFF, // Padding
+       (void*)0xFFFF, // Padding
+       (void*)0xFFFF, // Padding
+       (void*)0xFFFF, // Padding
+       (void*)0xFFFF, // Padding
+       (void*)0xFFFF, // Padding
+       (void*)0xFFFF, // Padding
+       (void*)0xFFFF, // Padding
+       (void*)0xFFFF, // Padding
+       (void*)0xFFFF, // Padding
+       (void*)0xFFFF, // Padding
+       FALLBACK_INT,  // INT_BRK_I (0x7E)
+};
diff --git a/wave.c b/wave.c
new file mode 100644 (file)
index 0000000..5c35c2d
--- /dev/null
+++ b/wave.c
@@ -0,0 +1,307 @@
+/* waveform & envelope */
+/*
+Copyright 2021 Balthasar Szczepański
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include "klavirko-ui.h"
+#include "wave.h"
+#include "aix.h"
+#include "debug.h"
+#include "ctrl.h"
+#include "sinus.h"
+
+const int16_t  sine[N_SAMPLE] = DEFAULT_SINE;
+      int8_t   sample[N_SAMPLE] = DEFAULT_SAMPLE;
+      uint32_t adsr_A = DEFAULT_A;
+      uint32_t adsr_D = DEFAULT_D;
+      uint32_t adsr_S = DEFAULT_S;
+      uint32_t adsr_R = DEFAULT_R;
+      uint16_t tuning = DEFAULT_TUNING;
+      int8_t   transp = 0;
+      uint16_t max_tuning = DEFAULT_TUNING;
+      uint32_t main_clock = MAIN_CLOCK;
+      uint32_t clock_div  = CLOCK_DIV;
+      uint32_t adsr_factor_a;
+      uint32_t adsr_factor_d;
+
+      uint8_t  wave_changed = 0;
+      uint8_t  adsr_changed = 0;
+      uint8_t  wave_locked = 0;
+      uint8_t  adsr_locked = 0;
+
+void set_timing (
+       const uint32_t clock,
+       const uint32_t div
+)
+{
+       main_clock = clock;
+       clock_div  = div;
+       
+       adsr_factor_a = clock * ADSR_FACTOR_A;
+       adsr_factor_d = clock * ADSR_FACTOR_D;
+       
+       adsr_factor_a /= div;
+       adsr_factor_d /= div;
+       
+       adsr_factor_a >>= ADSR_REDUCE_A;
+       adsr_factor_d >>= ADSR_REDUCE_D;
+}
+
+inline void set_max_tuning (const uint16_t max)
+{
+       max_tuning = max;
+       if (tuning > max)
+       {
+               tuning = max;
+               transp = 0;
+               ctrl_update_tuning();
+       }
+}
+
+inline void set_tuning (const uint16_t new)
+{
+       if (new <= max_tuning)
+       {
+               tuning = new;
+               ctrl_update_tuning();
+       }
+}
+
+inline void set_transp (const int8_t new)
+{
+       transp = new;
+       ctrl_update_tuning();
+}
+
+inline void update_wave (void)
+{
+       wave_changed = 1;
+}
+
+inline void update_adsr (void)
+{
+       adsr_changed = 1;
+}
+
+inline void lock_wave (void)
+{
+       wave_locked = 1;
+}
+
+inline void unlock_wave (void)
+{
+       wave_locked = 0;
+}
+
+inline void lock_adsr (void)
+{
+       adsr_locked = 1;
+}
+
+inline void unlock_adsr (void)
+{
+       adsr_locked = 0;
+}
+
+void calculate_adsr (void)
+{
+       uint32_t a, d, s, r;
+       
+       a = aix_data[AIX_A] & ADSR_IN_MASK;
+       d = aix_data[AIX_D] & ADSR_IN_MASK;
+       s = aix_data[AIX_S] & ADSR_IN_MASK;
+       r = aix_data[AIX_R] & ADSR_IN_MASK;
+       
+       adsr_S = (s<<22)|(s<<12)|(s<<2)|(s>>8); //spread 10 bits to 32
+       if (adsr_S < ADSR_FAKE_MIN)
+       {
+               s = ADSR_FAKE_MIN;
+               if (adsr_S < ADSR_MIN)
+                       adsr_S = 0;
+       }
+       else
+               s = adsr_S;
+       
+       
+       a *= a * a;
+       d *= d * d;
+       r *= r * r;
+       
+       // a >>= ADSR_REDUCE_1;
+       // d >>= ADSR_REDUCE_1;
+       // r >>= ADSR_REDUCE_1;
+       
+       // a *= a;
+       // d *= d;
+       // r *= r;
+       
+       a >>= ADSR_REDUCE_2A;
+       d >>= ADSR_REDUCE_2D;
+       r >>= ADSR_REDUCE_2D;
+       
+       a *= adsr_factor_a;
+       d *= adsr_factor_d;
+       r *= adsr_factor_d;
+       
+       a >>= ADSR_REDUCE_3A;
+       d >>= ADSR_REDUCE_3D;
+       r >>= ADSR_REDUCE_3D;
+       
+       adsr_A = a ? (ADSR_MAX / a) : ADSR_MAX;
+       adsr_D = d ? ((ADSR_MAX - adsr_S) / d) : ADSR_MAX;
+       adsr_R =  adsr_S ? (r ? (s / r) : ADSR_MAX) : adsr_D;
+}
+
+void calculate_wave (void)
+{
+       uint32_t v1, v2, v3, v4;
+       uint8_t  t1, t2, t3, t4;
+       uint16_t a1, a2, a3, a4;
+       uint16_t b1, b2, b3, b4;
+       int16_t x;
+       int32_t  acc;
+       uint16_t i;
+       
+       v1 = aix_data[AIX_V1] & WAVE_IN_MASK;
+       v2 = aix_data[AIX_V2] & WAVE_IN_MASK;
+       v3 = aix_data[AIX_V3] & WAVE_IN_MASK;
+       v4 = aix_data[AIX_V4] & WAVE_IN_MASK;
+
+       v1 *= v1;
+       v2 *= v2;
+       v3 *= v3;
+       v4 *= v4;
+
+       v1 >>= WAVE_REDUCE_V1;
+       v2 >>= WAVE_REDUCE_V1;
+       v3 >>= WAVE_REDUCE_V1;
+       v4 >>= WAVE_REDUCE_V1;
+       
+       v1 *= v1;
+       v2 *= v2;
+       v3 *= v3;
+       v4 *= v4;
+       
+       v1 >>= WAVE_REDUCE_V2;
+       v2 >>= WAVE_REDUCE_V2;
+       v3 >>= WAVE_REDUCE_V2;
+       v4 >>= WAVE_REDUCE_V2;
+       
+       v1 *= WAVE_XV1;
+       v2 *= WAVE_XV2;
+       v3 *= WAVE_XV3;
+       v4 *= WAVE_XV4;
+       
+       v1 >>= WAVE_REDUCE_V3;
+       v2 >>= WAVE_REDUCE_V3;
+       v3 >>= WAVE_REDUCE_V3;
+       v4 >>= WAVE_REDUCE_V3;
+       
+       t1 = aix_data[AIX_T1] >> WAVE_REDUCE_T;
+       t2 = aix_data[AIX_T2] >> WAVE_REDUCE_T;
+       t3 = aix_data[AIX_T3] >> WAVE_REDUCE_T;
+       t4 = aix_data[AIX_T4] >> WAVE_REDUCE_T;
+       
+       if (aix_data[AIX_SW] == WAVE_SQUARE)
+       {
+               t1 = ((((uint16_t)t1)*SQWV_X)>>SQWV_RED)+SQWV_OFF;
+               for (i=0; i<256; ++i)
+               {
+                       x = (i <= t1) ? SAMPLE_MAX : SAMPLE_MIN;
+                       acc = (int32_t)(x * v1);
+                       x = (((i<<1)-t2) & 0x80) ? SAMPLE_MIN : SAMPLE_MAX;
+                       acc += (int32_t)(x * v2);
+                       x = (((i*3)-t3) & 0x80) ? SAMPLE_MIN : SAMPLE_MAX;
+                       acc += (int32_t)(x * v3);
+                       x = (((i<<2)-t4) & 0x80) ? SAMPLE_MIN : SAMPLE_MAX;
+                       acc += (int32_t)(x * v4);
+                       acc >>= WAVE_REDUCE;
+                       if (acc > 127)
+                               acc = 127;
+                       else if (acc < -128)
+                               acc = -128;
+                       sample[i] = (int8_t)acc;
+               }
+       }
+       else if (aix_data[AIX_SW] == WAVE_TRIANGLE)
+       {
+               if (t1!=0)
+                       a1 = (SAMPLE_MAX - SAMPLE_MIN) / t1;
+               b1 = (SAMPLE_MAX - SAMPLE_MIN) / (N_SAMPLE - t1);
+               if (t2!=0)
+                       a2 = (SAMPLE_MAX - SAMPLE_MIN) / t2;
+               b2 = (SAMPLE_MAX - SAMPLE_MIN) / (N_SAMPLE - t2);
+               if (t3!=0)
+                       a3 = (SAMPLE_MAX - SAMPLE_MIN) / t3;
+               b3 = (SAMPLE_MAX - SAMPLE_MIN) / (N_SAMPLE - t3);
+               if (t4!=0)
+                       a4 = (SAMPLE_MAX - SAMPLE_MIN) / t4;
+               b4 = (SAMPLE_MAX - SAMPLE_MIN) / (N_SAMPLE - t4);
+               for (i=0; i<256; ++i)
+               {
+                       x = (i<t1) ? (SAMPLE_MIN + a1 * i)  : (SAMPLE_MIN + b1 * (N_SAMPLE - i));
+                       acc = (int32_t)(x * v1);
+                       x = (i<<1)&0xff;
+                       x = (x<t2) ? (SAMPLE_MIN + a2 * x)  : (SAMPLE_MIN + b2 * (N_SAMPLE - x));
+                       acc += (int32_t)(x * v2);
+                       x = (i*3)&0xff;
+                       x = (x<t3) ? (SAMPLE_MIN + a3 * x)  : (SAMPLE_MIN + b3 * (N_SAMPLE - x));
+                       acc += (int32_t)(x * v3);
+                       x = (i<<2)&0xff;
+                       x = (x<t4) ? (SAMPLE_MIN + a4 * x)  : (SAMPLE_MIN + b4 * (N_SAMPLE - x));
+                       acc += (int32_t)(x * v4);
+                       acc >>= WAVE_REDUCE;
+                       if (acc > 127)
+                               acc = 127;
+                       else if (acc < -128)
+                               acc = -128;
+                       sample[i] = (int8_t)acc;
+               }
+       }
+       else // sine
+       {
+               for (i=0; i<256; ++i)
+               {
+                       x = sine[(i-t1)&0xff];
+                       acc = (int32_t)(x * v1);
+                       x = sine[((i<<1)-t2)&0xff];
+                       acc += (int32_t)(x * v2);
+                       x = sine[((i*3)-t3)&0xff];
+                       acc += (int32_t)(x * v3);
+                       x = sine[((i<<2)-t4)&0xff];
+                       acc += (int32_t)(x * v4);
+                       acc >>= WAVE_REDUCE;
+                       if (acc > 127)
+                               acc = 127;
+                       else if (acc < -128)
+                               acc = -128;
+                       sample[i] = (int8_t)acc;
+               }
+       }
+}
+
+void handle_wave (void)
+{
+       if (adsr_changed && !adsr_locked)
+       {
+               adsr_changed = 0;
+               calculate_adsr();
+               gui_update_adsr();
+               ctrl_update_adsr();
+       }
+       if (wave_changed && !wave_locked)
+       {
+               wave_changed = 0;
+               calculate_wave();
+               gui_update_wave();
+               ctrl_update_wave();
+       }
+}
diff --git a/wave.h b/wave.h
new file mode 100644 (file)
index 0000000..64bf619
--- /dev/null
+++ b/wave.h
@@ -0,0 +1,129 @@
+/* waveform & envelope */
+/*
+Copyright 2021 Balthasar Szczepański
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include <stdint.h>
+
+// #include "sinus.h"
+
+#define N_SAMPLE 256
+
+#define WAVE_TRIANGLE 0x20 //top
+#define WAVE_SINE     0x30 //middle
+#define WAVE_SQUARE   0x10 //bottom
+
+#define SAMPLE_MAX  2032
+#define SAMPLE_MIN -2048
+
+#define DEFAULT_TUNING 4400
+#define MAIN_CLOCK     32000000
+#define CLOCK_DIV      720
+
+#define WAVE_IN_MASK 0x3FF
+//starting with 3ff - 10 bit
+//xx: ff801 - 20 bit
+#define WAVE_REDUCE_V1 4
+//ff80 - 16 bit
+//xx: ff004000 - 32 bit
+#define WAVE_REDUCE_V2 10
+//3fc010 - 22bit
+#define WAVE_XV1 642 //10/1
+#define WAVE_XV2 321 //10/2
+#define WAVE_XV3 214 //10/3
+#define WAVE_XV4 161 //10/4
+//9fdfa820 - 4fefd410 - 354a8d60 - 2817ca10 - 32 bit
+#define WAVE_REDUCE_V3 14
+//27f7e - 13fbf - 0d52a - 0a05f - 18 bit
+
+#define WAVE_REDUCE_T 2
+
+#define SQWV_X   207
+#define SQWV_OFF  25
+#define SQWV_RED   8 
+
+#define WAVE_REDUCE 18
+
+// #define ADSR_IN_MASK 0x3FF
+// //starting with 3ff - 10 bit
+// //xx: ff801 - 20 bit
+// #define ADSR_REDUCE_1 4
+// //ff80 - 16 bit
+// //xx: ff004000 - 32 bit
+// #define ADSR_REDUCE_2A 13
+// #define ADSR_REDUCE_2D 17
+// //a: 1FE00 - 17bit; d: 1fe0 - 13 bit //OUTDATED
+// //xf: a: d00f2000, d: 81b30ac0 //OUTDATED
+// #define ADSR_REDUCE_3A 17
+// #define ADSR_REDUCE_3D 16
+// //a: 6807 (26631), d: 40d98 (265627) //OUTDATED
+
+#define ADSR_IN_MASK 0x3FF
+//10 bit
+//xxx: 30 bit
+#define ADSR_REDUCE_2A 11
+#define ADSR_REDUCE_2D 15
+//a: 19bit; d: 15 bit
+//xf: 32
+#define ADSR_REDUCE_3A 17
+#define ADSR_REDUCE_3D 13
+//a: 6807 (26631), d: 40d98 (265627) //OUTDATED
+
+#define ADSR_MAX      0xffffffff
+#define ADSR_MIN      0x02222222
+#define ADSR_FAKE_MIN 0x0aaaaaaa
+
+// max A 0.6s
+#define ADSR_FACTOR_A 77
+// #define ADSR_REDUCE_A  7
+#define ADSR_REDUCE_A  9
+#define DEFAULT_FACTOR_A \
+       (((MAIN_CLOCK * ADSR_FACTOR_A) / CLOCK_DIV) >> ADSR_REDUCE_A)
+
+// max D 6s
+#define ADSR_FACTOR_D 96
+// #define ADSR_REDUCE_D  4
+#define ADSR_REDUCE_D  6
+#define DEFAULT_FACTOR_D \
+       (((MAIN_CLOCK * ADSR_FACTOR_D) / CLOCK_DIV) >> ADSR_REDUCE_D)
+
+#define DEFAULT_A ADSR_MAX
+#define DEFAULT_D ADSR_MAX
+#define DEFAULT_S ADSR_MAX
+#define DEFAULT_R ADSR_MAX
+
+inline void update_adsr (void);
+inline void update_wave (void);
+inline void lock_wave (void);
+inline void unlock_wave (void);
+inline void lock_adsr (void);
+inline void unlock_adsr (void);
+
+       void calculate_adsr (void);
+       void calculate_wave (void);
+       void handle_wave (void);
+       void set_timing (
+       const uint32_t clock,
+       const uint32_t div
+);
+inline void set_max_tuning (const uint16_t max);
+inline void set_tuning (const uint16_t new);
+inline void set_transp (const int8_t new);
+
+extern int8_t   sample[N_SAMPLE];
+extern uint32_t adsr_A;
+extern uint32_t adsr_D;
+extern uint32_t adsr_S;
+extern uint32_t adsr_R;
+extern uint16_t tuning;
+extern int8_t   transp;
+extern uint8_t  wave_locked;
+extern uint8_t  adsr_locked;
\ No newline at end of file