From c47485371ac69797b487f9549368fab62859de78 Mon Sep 17 00:00:00 2001 From: Clyne Sullivan Date: Tue, 24 Apr 2018 14:11:01 -0400 Subject: [PATCH] file cleanup, better text, documentation --- Makefile | 10 +-- include/display_text.h | 58 +++++++++++++++ include/fat32.h | 38 +++++++++- include/sdcard.h | 2 +- initrd/init | 1 + libinterp.a | Bin 68136 -> 69334 bytes src/flash.c.bak => old/flash.c | 0 src/font.c.bak => old/font.c | 0 .../inconsolata24.c | 0 {src => old}/initrd.c | 0 {include => old}/initrd.h | 0 src/lcd.c.bak => old/lcd.c | 0 {include => old}/lcd.h | 0 src/display_text.c | 69 +++++++++++++----- src/fat32.c | 18 ++++- src/flash.c | 20 +++++ src/keypad.c | 22 +++--- src/main.c | 33 ++++++--- src/script.c | 33 +++++++-- src/svc.c | 23 +++++- tools/rba | Bin 14664 -> 0 bytes 21 files changed, 262 insertions(+), 65 deletions(-) rename src/flash.c.bak => old/flash.c (100%) rename src/font.c.bak => old/font.c (100%) rename src/inconsolata24.c.bak => old/inconsolata24.c (100%) rename {src => old}/initrd.c (100%) rename {include => old}/initrd.h (100%) rename src/lcd.c.bak => old/lcd.c (100%) rename {include => old}/lcd.h (100%) delete mode 100755 tools/rba diff --git a/Makefile b/Makefile index 07af411..777c75f 100644 --- a/Makefile +++ b/Makefile @@ -21,9 +21,6 @@ CROSS = arm-none-eabi- CC = gcc AS = as -AR = tools/rba -OBJCOPY = objcopy -STRIP = strip MCUFLAGS = -mthumb -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=fpv4-sp-d16 AFLAGS = $(MCUFLAGS) @@ -42,15 +39,10 @@ OFILES = $(patsubst src/%.c, $(OUTDIR)/%.o, $(CFILES)) \ $(patsubst src/%.s, $(OUTDIR)/%.asm.o, $(AFILES)) OUT = out/main.elf -INITRD = initrd.img all: $(OUT) -$(OUT): $(OFILES) initrd/* libinterp.a - @echo " INITRD " $(INITRD) - @rm -f $(INITRD) - @$(AR) $(INITRD) initrd/* - @$(CROSS)$(OBJCOPY) -B arm -I binary -O elf32-littlearm $(INITRD) out/initrd.img.o +$(OUT): $(OFILES) @echo " LINK " $(OUT) @$(CROSS)$(CC) $(CFLAGS) $(LFLAGS) out/*.o -o $(OUT) -L. -linterp -lm diff --git a/include/display_text.h b/include/display_text.h index de7ebce..65b0703 100644 --- a/include/display_text.h +++ b/include/display_text.h @@ -1,13 +1,71 @@ +/* + * @file display_text.h + * A text-rendering/managing library to work with the display + * + * Copyright (C) 2018 Clyne Sullivan + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + #ifndef DISPLAY_TEXT_H_ #define DISPLAY_TEXT_H_ +/** + * The number of existing buffers. + */ +#define TTY_COUNT 2 + +/** + * Initializes text buffers and starts the blinking cursor. + * Must call before using other functions in this file. + */ void text_init(void); + +/** + * Switches to the i'th buffer. + * The display shows text from a buffer that has data and a position. By + * switching buffers, current text (and position in the text) can be preserved + * and switched back to later. + * @param i the buffer to switch to + */ void text_switch(unsigned int i); + +/** + * Puts a string of text to the display/current buffer. + * The cursor advances, and the buffer scrolls if the end of it is reached. + * @param s the string to put + */ void text_puts(const char *s); +/** + * Clears the display/current buffer. + */ void text_clear(void); +/** + * Sets the cursor's position. + * @param x the x position to move to + * @param y the y position to move to + */ void text_setpos(uint8_t x, uint8_t y); + +/** + * Moves the cursor relative to its current position. + * Can move in both positive and negative directions. + * @param x how many characters to move in the x direction + * @param y how many characters to move in the y direction + */ void text_relpos(int8_t x, int8_t y); #endif // DISPLAY_TEXT_H_ diff --git a/include/fat32.h b/include/fat32.h index 39d22f6..279b652 100644 --- a/include/fat32.h +++ b/include/fat32.h @@ -1,11 +1,36 @@ +/** + * @file fat32.h + * A very basic FAT32 reading implementation + * This only supports reading files in the root directory. Currently, the files + * must also have a name under eight characters, with no extension. + * + * Copyright (C) 2018 Clyne Sullivan + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + #ifndef FAT32_H_ #define FAT32_H_ #include +/** + * A structure to store important information for loading a file. + */ typedef struct { - uint32_t size; - uint32_t start; + uint32_t size; /**< The file's size in bytes */ + uint32_t start; /**< The file's first data cluster */ } file_t; /** @@ -22,10 +47,17 @@ int fat_find(void); file_t *fat_findfile(const char *name); /** - * + * Reads given file into an allocated buffer. + * @param file the file's data structure from fat_findfile() + * @return a malloc'd buffer containing the file's data */ char *fat_readfile(file_t *file); +/** + * Gets the name of the nth file in the root directory. + * @param index n, the index of the file + * @return the file's name, in a malloc'd buffer + */ char *fat_getname(uint32_t index); #endif // FAT32_H_ diff --git a/include/sdcard.h b/include/sdcard.h index 8cdef58..3722afc 100644 --- a/include/sdcard.h +++ b/include/sdcard.h @@ -1,5 +1,5 @@ /** - * @file sdcard.c + * @file sdcard.h * Provides a basic library for accessing an SD card * * Copyright (C) 2018 Clyne Sullivan diff --git a/initrd/init b/initrd/init index 7592b4a..feb01f8 100644 --- a/initrd/init +++ b/initrd/init @@ -1,4 +1,5 @@ while (1) { + #tty(1) print("Free mem: ") print(freemem()) print("\n") diff --git a/libinterp.a b/libinterp.a index c5a233714d2a57a0216e8bb491d33643438db010..d016267281878c0836e444f9644f033d378ad89b 100644 GIT binary patch delta 7841 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 delta 6655 zcmbtYe|QwtxjtuhH`z=!+fBd_eoWZxhRtpgNFs>@3}BE_#8g44C4vwkK!On>$yFMl zi%2vpa zX5R1p&i8)jJ7>a97pV)~drbC59+d4GVjzH(k+a?wGtA=CyqJx65DVj>*+l;EE+)@DAED zYSA9?g355$CP%p=u98blpSMh$mw9g0kW1%gcI`Yj(-cAKU88!Aax#fb>mA+q zpic-lE(OkV01V#>j}UL!4k1pt)TFH5(fuwV48!w*4Vmi=Ft?{e;=-NA$*7- zm)exoJ9hw@U8j-6RZtGU>!M1WE0I4{f6XeDQB`R6>KR6aJF=c#3fH}ebeB8_l6_Q- zGmGNGDfR8ATVw`3!C#{bGk~r=MKp!^I;!-9cEfg?V=`cekHPku&mpa+cm+ayW|DTl zci<10uTUGIzkANy>7Bw;!aN1$hm48d4}Tx?GVK^`^{LrI_ElNgBjeXW=-P%;W@ZOk zTneVQyn$u_<$34Q%zX#atm(1~w#V(=(gd1!3@7i_8k*PA{5id%+aR3UVRh%p#VM;` zcCYt|4RDwrVPM|P>`It-ctl)yx7EXsNy=o)FYcr2B5L>U=nnHLw`(!VF!MS6`5EvO znEm}oXpW(~f~Lrsa+feGnbU2~rrWl?=Eq3m*go^{6@MUS$~<9Kb%(OMP&fK|+#N&O zVzXu=(`HdK$f>WAnp!7sJ^h1ed(H3Tf|x^S`^+YauB9C?GicY*?rt8Vb|~!v^JDsl z(e7jBtHZu3IbF$|(nu@(tE4%x3?p~OGl`}!pQ7Uq3f$(O)VQ1)!;M48>$WB`i$lED<*!c$u%W3K{oZJHtq8J9FP;w~V$PWy{UR5oj8$Y1G_ehmEzivAA1}mjUCgw-|P> zu@Yeu3OV~`m%$kyEep3%ls(`02;K%+_@zhX1fH$Nx)i1*I{t#KrE(IdJKcsM&^m;jUBRTTnHp#H!YH)5OzMHN_gEy40#**A_*XXuh zV6uruPt@qOin!7y8O127*z&XG4trdqtuQ;7Y_vf?)*8gHDaIdA*#v7o$JJ!;vbI=t zY`N1W?bhcAHm8#0ih0(5v)5@x3>_`9iq*%t-}R4R53}s(SFICjOx`GeBeQiFcOyzz zSE)zxu8~>lc;1Y-oAUoPo(B}Kr+wLY2@tpkg6t`r?|y&7NtS!y05OnTRyrhdE6H3w zt{g|ngLE9`3~oUSUti ze=wJF9VG=<3fP#Etou~rR$6ieU3o?n3BFeJtZAFn^Vxg>1?88Fo!XX zstl+m*rKT^(X_9Lrhjb|Gg3X5{1Gc%HZsKl9L~~sz;~1GxY5bcDM}hx@QsXHg@cbC znb?`?>c&)9*&5U#hoaRElL>o;4NY#(uQQOKO#Si6@|;t!Qol9H%AWnCZ>YSn(fN5V zscF3?8I6BUzSrvmsj@ELoGi5Z%Ivv!PP=P*=af0^(`T!k_luLQg)N3^nVF|P3U*WX z&m5>8Iq6rUi$-A{)#|6MB34=N_bxLYlJ)*TeNN^KzqdXoM+9}xtJLd7VR=A(jB|$S z77EMBY7owks);xks5s7x)Dt)#QTuSNRcAxt-l^N?O6CG&ZnQJ2q9H5QJK>SJybR7y zif_SKr7DUm#w9Q@>0Yjx6vs3QzK&CzDz3{EXM6RPNprH+svX5U;XQ{A%3MB4iJ&(7%`TtD;?sA#qoYIdI<3V z5gqN){Gi5nHGZIWRP=~X%yy!u6A^vC<_~LpMB`T7{vR6O)&8>@FKU0T&&e0mSV~0R zQJRm}IL*PZxLXH2s2kR5zDx6GeQHDH8|vPwPmI$pwevtIV?~h=Z%ei6>X4@ZS1F*f zyTV3uSe>{!Vnp4_Juu>l!e6B-28J^lVIA?Qr@BI(&M<@H1H(ovcl}A9%vb*T0pq@I zDmXu4ERLzs^CO=1-Gumw>RTN)*2dHZ`1@a_UIaYUTOB!*uP)CIcwX!+L_)n#9nMJL z((M$gZb8VHWT~bF5zhuoh@cu&6ZZTjD8wJsl$x;7P^uQ!#PsK76+SQXlLu;E$xd_h za90n$+WFo*d5wCzKGAFWPO;-s({oK-P0!;CQk1!m8v4soWn8~V2GzLl)FgLbf7)C4 zxu0Jq{JG^|7(TAWmuv^tSMqla`?KPPOu%uQm(}u>6UGXN&K1&-v#{dvj*en0V90ix zv_z+VMVdwIdstfXGbT8Rz1zKZf;*9*6bU|a^qr;9f9A;BOCi^Gn~3o%GA_?kKI@8n zla`-Plb=tMYa4QY9?3IQU%&&#>HIydK9iNbx2xT9|2$hD1mD`)42sE$vC-06J1mLE#% z{7_ow+D7O0K54H#a$HqMeO#;erm6R)skIGtK1<(2^-?!pEsne!?kJ9YPnvvBnq1qE zH*u`bP|h37QIFH=XVTQqq^Y$H^}FnRJJtN=na58inRr;sx2MUsr^&Sq`DV&DQy#VD zR3Fmn&1ve*X=-gleGM+O+(`9nxDlFwGM>oJaYnu|O};Tru5HLSK_u5v-jb3hZqVv= zY3g-pYHdT!7mZv=^%+<{Wao)xIT$~uePx<_Wtv>ukpF^$C6qr1d6O;Qt2MbSJWt3u%x0{PclO^0r~HAVY#DG6?h!cp=iwyp(dCrSX=}z8 z*Y_~*cq9oCn+Hoyu=62~RmsF9bWyRzZdi|;2&`Dh!Y8o7Slu!J4+nb#Qr;@$?azQ1 ztBuY9$>%2ignQ=-b;smzPDU2|`uBEn<>cW~PE7vilrk^!a^I^;TZ)SwN5GXGY7{$E zqru*y2DPwM&1nhC2YEKmNcEeRkb%u~JK!}3qAoSMHKanVQMINuD3_{-Tf_1K&&E`b zTGiUiEY_)xc+?lI5o2ivydmQPU{)_P)z7TVuhEmFA$eOeIV&mSEHV%KKfIDenZD}e zA#na1(xlt*q7rApxg^<+J%(t-Xqzo-Fz#uM-1d?46P$Qn<1H9C zxxLu}j=;FccWNxeILP^rE%17AI|}DEZYL29g(#f3R-=hGG5K$aDCAWlc6EQzc)^>z zFej1`=VIpGX(|z8ZPWZw&9`a(zUF_?yic|hA0a}|?|AB)HQ%85Su9A$onAtD=_AYY80DK2<7wNgDC!R{`b8J5U-rL?op04H| zGNWf7Ax;=7SdDwR+TKK7EVMU~xAjYHB8}z!RA52G_@Y|X0ve*+JsQiSYB^wQnc4{` h$5wE`*Y}Nu3*Q=>wr|W&=DqQjm2xCMIs0Fs{{yG>f)fA$ diff --git a/src/flash.c.bak b/old/flash.c similarity index 100% rename from src/flash.c.bak rename to old/flash.c diff --git a/src/font.c.bak b/old/font.c similarity index 100% rename from src/font.c.bak rename to old/font.c diff --git a/src/inconsolata24.c.bak b/old/inconsolata24.c similarity index 100% rename from src/inconsolata24.c.bak rename to old/inconsolata24.c diff --git a/src/initrd.c b/old/initrd.c similarity index 100% rename from src/initrd.c rename to old/initrd.c diff --git a/include/initrd.h b/old/initrd.h similarity index 100% rename from include/initrd.h rename to old/initrd.h diff --git a/src/lcd.c.bak b/old/lcd.c similarity index 100% rename from src/lcd.c.bak rename to old/lcd.c diff --git a/include/lcd.h b/old/lcd.h similarity index 100% rename from include/lcd.h rename to old/lcd.h diff --git a/src/display_text.c b/src/display_text.c index 8a41fa5..880485e 100644 --- a/src/display_text.c +++ b/src/display_text.c @@ -1,3 +1,22 @@ +/* + * @file display_text.c + * A text-rendering/managing library to work with the display + * + * Copyright (C) 2018 Clyne Sullivan + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ #include #include @@ -8,8 +27,6 @@ #define WIDTH 40 #define HEIGHT 18 -#define TTY_COUNT 2 - typedef struct { char *buf; uint8_t x; @@ -51,25 +68,19 @@ void text_init(void) void text_redraw(void) { - for (unsigned int i = 0; i < WIDTH; i++) { - //unsigned int zc = 0; - for (unsigned int j = 0; j < HEIGHT; j++) { - int c = text_current->buf[i + j * WIDTH]; - /*if (c == '\0') { - if (++zc == 3) - break; - } else if (zc > 0) { - zc = 0; - }*/ - - dsp_putchar(c, i, j); + for (unsigned int j = 0; j < HEIGHT; j++) { + unsigned int i = WIDTH; + //for (; i > 0 && text_current->buf[j * WIDTH + i - 1] == '\0'; i--); + for (; i > 0; i--) { + int c = text_current->buf[j * WIDTH + i - 1]; + dsp_putchar(c, i - 1, j); } } } void text_switch(unsigned int i) { - if (i >= TTY_COUNT) + if (i >= TTY_COUNT || text_current == text_tty + i) return; text_current = &text_tty[i]; @@ -78,10 +89,14 @@ void text_switch(unsigned int i) void text_clear(void) { - for (unsigned int i = 0; i < WIDTH; i++) { - for (unsigned int j = 0; j < HEIGHT; j++) { - text_current->buf[i + j * WIDTH] = 0; - dsp_putchar(' ', i, j); + + for (unsigned int j = 0; j < HEIGHT; j++) { + for (unsigned int i = 0; i < WIDTH; i++) { + //int c = text_current->buf[j * WIDTH + 1]; + //if (c != 0) { + text_current->buf[j * WIDTH + i] = 0; + dsp_putchar(' ', i, j); + //} } } text_current->x = 0; @@ -131,7 +146,21 @@ void text_putchar(int c) for (int i = 0; i < WIDTH; i++) text_current->buf[i + y * WIDTH] = 0; - text_redraw(); + for (int j = HEIGHT - 1; j >= 0; j--) { + int i = WIDTH - 1; + if (j > 0) { + for (; i >= 0; i--) { + int p = text_current->buf[(j - 1) * WIDTH + i]; + int c = text_current->buf[j * WIDTH + i]; + if (p != '\0' || c != '\0') + break; + } + } + for (; i >= 0; i--) { + int c = text_current->buf[j * WIDTH + i]; + dsp_putchar(c, i, j); + } + } } text_current->x = x; diff --git a/src/fat32.c b/src/fat32.c index 3c64f52..db2eb60 100644 --- a/src/fat32.c +++ b/src/fat32.c @@ -1,5 +1,21 @@ /** - * Implementation of a driver to read a FAT32 filesystem. + * @file fat32.h + * A very basic FAT32 reading implementation + * + * Copyright (C) 2018 Clyne Sullivan + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . */ #include diff --git a/src/flash.c b/src/flash.c index 396daaf..c335923 100644 --- a/src/flash.c +++ b/src/flash.c @@ -1,3 +1,23 @@ +/** + * @file flash.c + * Provides functionality for using an external SPI flash + * + * Copyright (C) 2018 Clyne Sullivan + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + #include #include #include diff --git a/src/keypad.c b/src/keypad.c index cb569e1..068850e 100644 --- a/src/keypad.c +++ b/src/keypad.c @@ -61,20 +61,20 @@ static const port_t keypad_cols[COLS] = { static const char keypad_map[ROWS * COLS * 4] = { "\x7F\0\0\0" "\xFF\0\0\0" "\xFF\x02\0\0" "\x19\0\0\0" "\x18\0\0\0" - "sin\0" "cos\0" "tan\0" "\x1B\0\0\0" "\x1A\0\0\0" - "7\0\0\0" "8\0\0\0" "9\0\0\0" "(\0\0\0" ")\0\0\0" - "4\0\0\0" "5\0\0\0" "6\0\0\0" "/\0\0\0" "*\0\0\0" - "1\0\0\0" "2\0\0\0" "3\0\0\0" "-\0\0\0" "+\0\0\0" - ".\0\0\0" "0\0\0\0" "=\0\0\0" "\b\0\0\0" "\n\0\0\0" + "\x11\0\0\0" "\x12\0\0\0" "\x13\0\0\0" "\x1B\0\0\0" "\x1A\0\0\0" + "7\0\0\0" "8\0\0\0" "9\0\0\0" "(\0\0\0" ")\0\0\0" + "4\0\0\0" "5\0\0\0" "6\0\0\0" "/\0\0\0" "*\0\0\0" + "1\0\0\0" "2\0\0\0" "3\0\0\0" "-\0\0\0" "+\0\0\0" + ".\0\0\0" "0\0\0\0" "=\0\0\0" "\b\0\0\0" "\n\0\0\0" }; static const char keypad_map_2nd[ROWS * COLS * 4] = { - "\x7F\0\0\0" "pi\0\0" "X\0\0\0" "Y\0\0\0" "Z\0\0\0" - "A\0\0\0" "B\0\0\0" "C\0\0\0" "D\0\0\0" "E\0\0\0" - "F\0\0\0" "G\0\0\0" "H\0\0\0" "I\0\0\0" "J\0\0\0" - "K\0\0\0" "L\0\0\0" "M\0\0\0" "N\0\0\0" "O\0\0\0" - "P\0\0\0" "Q\0\0\0" "R\0\0\0" "S\0\0\0" "T\0\0\0" - "U\0\0\0" "V\0\0\0" "W\0\0\0" "\b\0\0\0" "\xFF\x01\0\0" + "\x7F\0\0\0" "\xFF\x01\0\0" "X\0\0\0" "Y\0\0\0" "Z\0\0\0" + "A\0\0\0" "B\0\0\0" "C\0\0\0" "D\0\0\0" "E\0\0\0" + "F\0\0\0" "G\0\0\0" "H\0\0\0" "I\0\0\0" "J\0\0\0" + "K\0\0\0" "L\0\0\0" "M\0\0\0" "N\0\0\0" "O\0\0\0" + "P\0\0\0" "Q\0\0\0" "R\0\0\0" "S\0\0\0" "T\0\0\0" + "U\0\0\0" "V\0\0\0" "W\0\0\0" "\b\0\0\0" "\n\0\0\0" }; #define KEY(r, c, i) map[r * COLS * 4 + c * 4 + i] diff --git a/src/main.c b/src/main.c index b60f626..d232f48 100644 --- a/src/main.c +++ b/src/main.c @@ -26,9 +26,7 @@ #include #include #include -#include #include -#include #include #include #include @@ -55,6 +53,7 @@ int main(void) FLASH->ACR &= ~(FLASH_ACR_LATENCY); FLASH->ACR |= FLASH_ACR_LATENCY_4WS; + // init core components clock_init(); heap_init(&__bss_end__); random_init(); @@ -63,17 +62,14 @@ int main(void) keypad_init(); flash_init(); - //gpio_mode(GPIOA, 5, OUTPUT); // taken by sd - // enable FPU SCB->CPACR |= (0xF << 20); - // enable MPU - //MPU->CTRL |= MPU_CTRL_PRIVDEFENA_Msk | MPU_CTRL_ENABLE_Msk; task_init(kmain); while (1); } +// enters deep sleep, activated through button press void sleep(void) { dsp_sleep(); @@ -82,6 +78,8 @@ void sleep(void) asm("wfi"); } +// wakes up from sleep, re-initializes components that +// need to be void wakeup(void) { clock_init(); @@ -90,14 +88,17 @@ void wakeup(void) void kmain(void) { + // prepare display dsp_init(); dsp_cursoron(); text_init(); + // prepare SD card and keypad sd_init(); fat_find(); keypad_start(); + // start tasks task_start(task_interpreter, 4096); task_start(task_status, 512); @@ -108,13 +109,18 @@ void kmain(void) while (sleep_pending) delay(1); } - //gpio_dout(GPIOA, 5, 1); - //delay(250); - //gpio_dout(GPIOA, 5, 0); delay(100); } } +/** + * Loads the given script on SD card into an interpreter instance. + * @param name the file's name + * @return a new, loaded instance + * Files are expected to be in the root directory of the filesystem. + * See fat32.h for more info. + * Pass the returned instance to irun() to start the script. + */ instance *load_program(const char *name) { // load file @@ -157,8 +163,10 @@ fail: return 0; } - - +/** + * Display and update a status bar. + * This currently only has an icon for insert/delete mode. + */ void task_status(void) { extern int keypad_insert; @@ -185,6 +193,9 @@ void task_status(void) } } +/** + * Loads the initial program from the SD card and runs it. + */ void task_interpreter(void) { dsp_rect(0, 0, 480, 300, 0); diff --git a/src/script.c b/src/script.c index 65dd5a4..a31630c 100644 --- a/src/script.c +++ b/src/script.c @@ -27,7 +27,6 @@ #include #include #include -#include #include #include #include @@ -55,6 +54,7 @@ int script_filemenu(instance *it); int script_program(instance *it); int script_free(instance *it); int script_clear(instance *it); +int script_tty(instance *it); int math_sin(instance *it); int math_cos(instance *it); @@ -85,6 +85,7 @@ void script_loadlib(instance *it) inew_cfunc(it, "filemenu", script_filemenu); inew_cfunc(it, "program", script_program); inew_cfunc(it, "freemem", script_free); + inew_cfunc(it, "tty", script_tty); inew_cfunc(it, "sin", math_sin); inew_cfunc(it, "cos", math_cos); @@ -152,8 +153,10 @@ int script_filemenu(instance *it) char listbuf[4]; char *fname; strncpy(listbuf, " : \0", 4); + //text_switch(1); + //text_clear(); text_puts("Choose a file: \n"); - for (unsigned int i = 0; (fname = /*initrd*/fat_getname(i)) != 0; i++) { + for (unsigned int i = 0; (fname = fat_getname(i)) != 0; i++) { listbuf[0] = i + '0'; text_puts(listbuf); text_puts(fname); @@ -168,6 +171,7 @@ int script_filemenu(instance *it) variable *v = make_varf(0, isdigit(c) ? c - '0' : -1.0f); ipush(it, (uint32_t)v); + //text_switch(0); return 0; } @@ -222,6 +226,15 @@ int script_gets(instance *it) delay(1); } while (c[0] == 0); + // fn keys + if (c[0] == 0x11 || c[0] == 0x12 || c[0] == 0x13) { + /*s[index] = c[0]; + if (furthest == 0) + furthest = 1; + break;*/ + continue; + } + if (c[0] == 0x7F) { it->lnidx = 998; break; @@ -240,10 +253,8 @@ int script_gets(instance *it) } else if (c[0] == K_UP || c[0] == K_DOWN) continue; - if (c[0] == '\n') { - s[furthest] = '\n'; + if (c[0] == '\n') break; - } extern int keypad_insert; if (keypad_insert != 0 && index < furthest) { @@ -355,13 +366,13 @@ int script_pixel(instance *it) extern instance *load_program(const char *name); int script_program(instance *it) { - int initrdOffset = (int)igetarg(it, 0)->value.f; - char *name = fat_getname(initrdOffset); + int offset = (int)igetarg(it, 0)->value.f; + char *name = fat_getname(offset); - text_clear(); instance *it2 = load_program(name); free(name); + text_clear(); int ret = irun(it2); if (ret != 0) return -1; @@ -383,3 +394,9 @@ int script_clear(instance *it) text_clear(); return 0; } + +int script_tty(instance *it) +{ + text_switch(igetarg_integer(it, 0)); + return 0; +} diff --git a/src/svc.c b/src/svc.c index ad6e33f..d68fb72 100644 --- a/src/svc.c +++ b/src/svc.c @@ -1,7 +1,28 @@ +/** + * @file svc.c + * An unused handler for SVC calls + * TODO: use SVC calls, possibly allowing for switch to unprivileged mode? + * + * Copyright (C) 2018 Clyne Sullivan + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + #include #include #include -#include +//#include void SVC_Handler(void) { uint32_t *stack; diff --git a/tools/rba b/tools/rba deleted file mode 100755 index 52f37cb59cac5d5e55e8bb84700b1e522d162270..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14664 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