Compare commits

...

26 Commits

Author SHA1 Message Date
aae8970584 The default example is now _slightly_ less cringe 2025-09-26 19:24:50 +02:00
a80db635cc Bugfix: tulflash run hot reload broke after a637486 2025-09-26 19:23:39 +02:00
073f6e2549 Turns out I only need to do this (source: Martinec) 2025-09-26 16:21:25 +02:00
7d40995934 ...even more 2025-09-25 12:09:54 +02:00
276ae61783 Improved chances for successful sync 2025-09-25 12:06:30 +02:00
62dadc65d0 Added the tulflash directory to .gitignore 2025-09-25 12:02:43 +02:00
6d8b9bc65e Literally changed a single character 2025-09-25 12:02:01 +02:00
c9c31ebd23 Updated README 2025-09-25 12:00:44 +02:00
75664c433e jfdlsjflsajflsajflkajdslf 2025-09-25 11:57:25 +02:00
3cec3eacc6 Made the docs a bit more clear 2025-09-25 11:55:43 +02:00
7ca0c605b8 Made the commandline arguments a bit more clear 2025-09-25 11:50:38 +02:00
664e402080 Fixed port auto-detection on Windows 2025-09-24 13:45:53 +02:00
329f7e1284 Now printing the list of all available ports (Wokňous issue?) 2025-09-24 13:28:44 +02:00
a637486847 Merge pull request 'Compile in separate directory to keep out the bloat' (#1) from compilation_path into master
Reviewed-on: #1
2025-09-24 11:20:03 +00:00
04b642776d Made the code look a bit less ugly 2025-09-24 13:19:01 +02:00
882cd55ec1 Now building stripped binaries 2025-09-24 13:10:03 +02:00
9f4a61a7ec add compilation in separate directory 2025-09-24 12:06:43 +02:00
d35c88e38f Temporarily removed the serial echo (was causing terrible performance) 2025-09-21 12:26:04 +02:00
cf04037f9d Example now enables the serial RX interrupt for echo 2025-09-21 11:10:53 +02:00
2a88835da5 Massively sped up LCD comms 2025-09-21 10:56:15 +02:00
aa84a2c03b Massively sped up lcd_putchar's handling of '\n' 2025-09-21 10:51:08 +02:00
52a232f1e9 Now using the correct header file with all port definitions 2025-09-21 10:49:39 +02:00
ab9078ab3f Added command for generating the kopyto 2025-09-20 23:04:18 +02:00
37f15bf187 Swapped out termion for crossterm 2025-09-20 22:48:12 +02:00
e4c4a7f14a The program no longer swears at you 2025-09-19 18:09:28 +02:00
d7b70b7629 Fixed wording 2025-09-19 18:09:14 +02:00
9 changed files with 490 additions and 112 deletions

1
.gitignore vendored
View File

@@ -1 +1,2 @@
/target
tulflash

325
Cargo.lock generated
View File

@@ -44,6 +44,12 @@ dependencies = [
"syn",
]
[[package]]
name = "autocfg"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
[[package]]
name = "bitflags"
version = "2.9.4"
@@ -56,6 +62,15 @@ version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9"
[[package]]
name = "convert_case"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb402b8d4c85569410425650ce3eddc7d698ed96d39a73f941b08fb63082f1e7"
dependencies = [
"unicode-segmentation",
]
[[package]]
name = "crossbeam-channel"
version = "0.5.15"
@@ -71,12 +86,79 @@ version = "0.8.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
[[package]]
name = "crossterm"
version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8b9f2e4c67f833b660cdb0a3523065869fb35570177239812ed4c905aeff87b"
dependencies = [
"bitflags",
"crossterm_winapi",
"derive_more",
"document-features",
"mio",
"parking_lot",
"rustix",
"signal-hook",
"signal-hook-mio",
"winapi",
]
[[package]]
name = "crossterm_winapi"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b"
dependencies = [
"winapi",
]
[[package]]
name = "defer-lite"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6a1ef0750c0fcdb9bd00e7829fcad58e9a0a2ab7078b57c39743b973f3878ce"
[[package]]
name = "derive_more"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678"
dependencies = [
"derive_more-impl",
]
[[package]]
name = "derive_more-impl"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3"
dependencies = [
"convert_case",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "document-features"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95249b50c6c185bee49034bcb378a49dc2b5dff0be90ff6616d31d64febab05d"
dependencies = [
"litrs",
]
[[package]]
name = "errno"
version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
dependencies = [
"libc",
"windows-sys 0.61.0",
]
[[package]]
name = "getopts"
version = "0.2.24"
@@ -93,16 +175,33 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543"
[[package]]
name = "libredox"
version = "0.1.10"
name = "linux-raw-sys"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "416f7e718bdb06000964960ffa43b4335ad4012ae8b99060261aa4a8088d5ccb"
checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039"
[[package]]
name = "litrs"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f5e54036fe321fd421e10d732f155734c4e4afd610dd556d9a82833ab3ee0bed"
[[package]]
name = "lock_api"
version = "0.4.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765"
dependencies = [
"bitflags",
"libc",
"redox_syscall",
"autocfg",
"scopeguard",
]
[[package]]
name = "log"
version = "0.4.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432"
[[package]]
name = "mach"
version = "0.1.2"
@@ -124,6 +223,18 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
[[package]]
name = "mio"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c"
dependencies = [
"libc",
"log",
"wasi",
"windows-sys 0.59.0",
]
[[package]]
name = "nom"
version = "7.1.3"
@@ -135,10 +246,27 @@ dependencies = [
]
[[package]]
name = "numtoa"
version = "0.2.4"
name = "parking_lot"
version = "0.12.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6aa2c4e539b869820a2b82e1aef6ff40aa85e65decdd5185e83fb4b1249cd00f"
checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13"
dependencies = [
"lock_api",
"parking_lot_core",
]
[[package]]
name = "parking_lot_core"
version = "0.9.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5"
dependencies = [
"cfg-if",
"libc",
"redox_syscall",
"smallvec",
"windows-targets",
]
[[package]]
name = "pbr"
@@ -191,10 +319,23 @@ dependencies = [
]
[[package]]
name = "redox_termios"
version = "0.1.3"
name = "rustix"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "20145670ba436b55d91fc92d25e71160fbfbdd57831631c8d7d36377a476f1cb"
checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e"
dependencies = [
"bitflags",
"errno",
"libc",
"linux-raw-sys",
"windows-sys 0.61.0",
]
[[package]]
name = "scopeguard"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "serial2"
@@ -228,6 +369,42 @@ version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "signal-hook"
version = "0.3.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d881a16cf4426aa584979d30bd82cb33429027e42122b169753d6ef1085ed6e2"
dependencies = [
"libc",
"signal-hook-registry",
]
[[package]]
name = "signal-hook-mio"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34db1a06d485c9142248b7a054f034b349b212551f3dfd19c94d45a754a217cd"
dependencies = [
"libc",
"mio",
"signal-hook",
]
[[package]]
name = "signal-hook-registry"
version = "1.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b"
dependencies = [
"libc",
]
[[package]]
name = "smallvec"
version = "1.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
[[package]]
name = "syn"
version = "2.0.106"
@@ -239,28 +416,16 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "termion"
version = "4.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3669a69de26799d6321a5aa713f55f7e2cd37bd47be044b50f2acafc42c122bb"
dependencies = [
"libc",
"libredox",
"numtoa",
"redox_termios",
]
[[package]]
name = "tulflash"
version = "0.1.0"
dependencies = [
"argp",
"crossterm",
"pbr",
"serial2",
"serial_enumerator",
"shlex",
"termion",
]
[[package]]
@@ -275,12 +440,24 @@ version = "1.0.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d"
[[package]]
name = "unicode-segmentation"
version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"
[[package]]
name = "unicode-width"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a1a07cc7db3810833284e8d372ccdc6da29741639ecc70c9ec107df0fa6154c"
[[package]]
name = "wasi"
version = "0.11.1+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
[[package]]
name = "winapi"
version = "0.3.9"
@@ -309,39 +486,127 @@ version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aac7fef12f4b59cd0a29339406cc9203ab44e440ddff6b3f5a41455349fa9cf3"
dependencies = [
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_msvc",
"windows_aarch64_msvc 0.29.0",
"windows_i686_gnu 0.29.0",
"windows_i686_msvc 0.29.0",
"windows_x86_64_gnu 0.29.0",
"windows_x86_64_msvc 0.29.0",
]
[[package]]
name = "windows-link"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "45e46c0661abb7180e7b9c281db115305d49ca1709ab8242adf09666d2173c65"
[[package]]
name = "windows-sys"
version = "0.59.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-sys"
version = "0.61.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e201184e40b2ede64bc2ea34968b28e33622acdbbf37104f0e4a33f7abe657aa"
dependencies = [
"windows-link",
]
[[package]]
name = "windows-targets"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc 0.52.6",
"windows_i686_gnu 0.52.6",
"windows_i686_gnullvm",
"windows_i686_msvc 0.52.6",
"windows_x86_64_gnu 0.52.6",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc 0.52.6",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_msvc"
version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3d027175d00b01e0cbeb97d6ab6ebe03b12330a35786cbaca5252b1c4bf5d9b"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_i686_gnu"
version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8793f59f7b8e8b01eda1a652b2697d87b93097198ae85f823b969ca5b89bba58"
[[package]]
name = "windows_i686_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_msvc"
version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8602f6c418b67024be2996c512f5f995de3ba417f4c75af68401ab8756796ae4"
[[package]]
name = "windows_i686_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_x86_64_gnu"
version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3d615f419543e0bd7d2b3323af0d86ff19cbc4f816e6453f36a2c2ce889c354"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
name = "windows_x86_64_msvc"
version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "11d95421d9ed3672c280884da53201a5c46b7b2765ca6faf34b0d71cf34a3561"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"

View File

@@ -3,10 +3,14 @@ name = "tulflash"
version = "0.1.0"
edition = "2024"
[profile.release]
opt-level = "s"
strip = true
[dependencies]
argp = "0.4.0"
crossterm = "0.29.0"
pbr = "1.1.1"
serial2 = "0.2.32"
serial_enumerator = "0.2.12"
shlex = "1.3.0"
termion = "4.0.5"

View File

@@ -9,24 +9,33 @@ No need to clone this repo manually. You'll just need to install the Rust toolch
cargo install --git https://git.zumepro.cz/michal.prochazka/tulflash
```
## Flashing Intel HEX binaries
> [!NOTE]
> For macOS/Linux users: remember to add `$HOME/.cargo/bin` to your `$PATH` if you have not done so already!
## Running the example program
To compile the example program using tulflash, you will need to install SDCC.
```bash
# All OSes, automatically detects all ports
# Generate the example program (only needed to run once):
tulflash example
# Compile, flash and monitor the example program:
tulflash run example.c
# note: `tulflash run example.c` is the same thing as `mkdir "tulflash/" && tulflash write --cmd "sdcc example.c" --cmd-path "tulflash/" --monitor example.ihx`
```
If the flash fails, try to add `--slow` before `run` (should not be needed though).
## Manually flashing Intel HEX binaries
If you use an external toolchain (such as MCU8051IDE with SDCC) and only want to use this program just for flashing the generated HEX file, just run the following:
```bash
# All OSes, automatically detects the correct serial port
tulflash write <path-to-hex>
```
If the flash fails, try to add `--slow` before `write` (should not be needed though).
The tool also allows reading and erasing the chip, figure that out on your own if you need that (although `tulflash --help` might guide you if you ask it nicely).
## Example program
You'll need to clone this repo (or just [download the file](example.c)) and install SDCC:
```bash
tulflash run example.c
# Same thing as `tulflash write --cmd "sdcc example.c" --monitor example.ihx`
```

View File

@@ -8,19 +8,12 @@
//**********************************************************************
//**********************************************************************
#include <at89c55.h>
#include <at89c51ed2.h>
#include <stdio.h>
#include <stdint.h>
#define TICK_RATE 100
#define NPER (0x10000 - (20000000 / 12 / TICK_RATE))
// Missing port definitions
__sbit __at (0xC4) P4_4;
__sbit __at (0xC3) P4_3;
__sbit __at (0xC2) P4_2;
__sbit __at (0xC1) P4_1;
__sbit __at (0xC0) P4_0;
#define NPER (0x10000 - (20000000 / 6 / TICK_RATE))
// I/O definitions
#define BUTTON_1 P3_2
@@ -71,8 +64,7 @@ uint8_t lcd_pos;
// Quick'n'dirty CPU pause, just enough for the LCD to stabilize.
void lcd_pause(void) {
int i = 30;
while(i--);
__asm__ ("nop");
}
// Reads the status register from the LCD.
@@ -170,11 +162,8 @@ void lcd_init(void) {
// Puts a character to the LCD. Correctly handles '\n'.
void lcd_putchar(char c) {
if(c == 10) {
while((lcd_pos != 0) && (lcd_pos != 40)) {
lcd_send_data(32);
lcd_pos++;
if(lcd_pos == 80) lcd_pos=0;
}
if(lcd_pos < 40) lcd_pos = 40;
else lcd_pos = 0;
} else {
lcd_send_data(c);
lcd_pos++;
@@ -184,6 +173,8 @@ void lcd_putchar(char c) {
// Initializes the background tick timer (for delays).
void timer_init(void) {
CKCON0 = X2; // Enable 2x clock mode
// Initialize timer 0 as 16-bit countdown, timer 1 as 8-bit auto-reload countdown
TMOD = 0x21;
@@ -196,7 +187,8 @@ void timer_init(void) {
// Enable interrupts only for timer 0
IE = 0x82;
EA = 1;
ET0 = 1;
}
// Initializes the serial port (along with timer 2).
@@ -204,7 +196,7 @@ void serial_init(void) {
// Initialize timer 2 as 312500 Hz as the baud rate generator (for tulflash)
RCAP2H = 0xFF;
RCAP2L = 0xFE;
RCAP2L = 0xFC;
T2CON = 0x34;
// Initialize serial I/O
@@ -217,6 +209,7 @@ void serial_init(void) {
void serial_putchar(char c) {
while(TI == 0);
SBUF = c;
TI = 0;
}
__bit stdout_to_lcd = 0;
@@ -296,6 +289,8 @@ void main(void) {
lcd_init();
serial_init();
delay(1);
stdout_to_serial = 1;
stdout_to_lcd = 1;
@@ -303,7 +298,7 @@ void main(void) {
while(1) {
lcd_clear();
printf("hellOwOrld! %d\n", i);
printf("Hellorld! %d\n", i);
led_bar_set(i);
LED_RED = !(i & 1);

View File

@@ -24,7 +24,8 @@ pub enum ArgCommand {
Write(ArgWrite),
Erase(ArgErase),
Monitor(ArgMonitor),
Run(ArgRun)
Run(ArgRun),
Example(ArgExample)
}
#[derive(FromArgs)]
@@ -60,6 +61,10 @@ pub struct ArgWrite {
#[argp(description = "Command to run before writing the file. Handy for compiling the program and running it at the same time.")]
pub cmd: Option<OsString>,
#[argp(option)]
#[argp(description = "Where to run the command. If no command is specified, this option is ignored.")]
pub cmd_path: Option<std::path::PathBuf>,
#[argp(option, default = "0")]
#[argp(description = "Start address where to write from. Only for raw binaries, defaults to 0.")]
pub start: usize,
@@ -77,7 +82,7 @@ pub struct ArgWrite {
pub skip_verify: bool,
#[argp(switch)]
#[argp(description = "Write individual sections to Flash rather than performing a single write. Only applies to Intel HEX.")]
#[argp(description = "Write individual sections to Flash rather than performing a single write. Only applies to Intel HEX. Can in some very specific cases speed up the writing process.")]
pub write_individual: bool,
#[argp(switch)]
@@ -91,18 +96,27 @@ pub struct ArgWrite {
pub struct ArgErase {}
#[derive(FromArgs)]
#[argp(description = "Handy shorthand for `write --cmd \"sdcc <srcpath>\" --monitor <hexpath>`.")]
#[argp(description = "Opens the serial monitor.")]
#[argp(subcommand, name = "monitor")]
pub struct ArgMonitor {}
#[derive(FromArgs)]
#[argp(description = "Handy shorthand for `write --cmd \"sdcc <srcpath>\" --cmd-path \"<srcparentpath>/tulflash\" --monitor <hexpath>`.")]
#[argp(subcommand, name = "run")]
pub struct ArgRun {
#[argp(positional)]
#[argp(description = "Path to the source C file.")]
pub path: OsString
pub path: std::path::PathBuf
}
#[derive(FromArgs)]
#[argp(description = "Opens the serial monitor.")]
#[argp(subcommand, name = "monitor")]
pub struct ArgMonitor {}
#[argp(description = "Generates a \"stub\" C file for the TUL výukový přípravek™.")]
#[argp(subcommand, name = "example")]
pub struct ArgExample {
#[argp(positional)]
#[argp(description = "Path to the source C file to be generated.")]
pub path: Option<OsString>
}
pub fn parse() -> Args {
let args: Args = argp::parse_args_or_exit(argp::DEFAULT);

View File

@@ -3,6 +3,7 @@ use std::io::{Cursor, Error, Write};
use std::path::PathBuf;
use std::time::{Duration, SystemTime};
use crossterm::event::{KeyCode, KeyModifiers};
use pbr::ProgressBar;
mod args;
@@ -16,7 +17,6 @@ use monitor::Monitor;
mod target;
use target::{PhysicalTarget, Target};
use termion::event::Key;
mod utils;
@@ -204,16 +204,23 @@ fn write_chip_data(args: &ArgWrite, target: &mut PhysicalTarget) -> Result<(), E
fn write_chip(args: ArgWrite, target: &mut PhysicalTarget) -> Result<(), Error> {
loop {
if let Some(cmd) = args.cmd.as_ref() {
let cmd_path = &args.cmd_path;
let mut args = shlex::bytes::split(cmd.as_encoded_bytes())
.ok_or(Error::other("Error parsing commandline arguments"))?
.into_iter()
.map(|x| unsafe { OsString::from_encoded_bytes_unchecked(x) });
if let Some(cmd_path) = cmd_path {
if !cmd_path.is_dir() {
Err(Error::other("invalid cmd path (not a dir)"))?
}
}
let Some(name) = args.next() else {
Err(Error::other("No command provided"))?
};
let out = std::process::Command::new(name)
.current_dir(cmd_path.as_ref().map(|p| p.as_path()).unwrap_or(&std::path::Path::new(".")))
.args(args)
.output()?;
@@ -230,11 +237,13 @@ fn write_chip(args: ArgWrite, target: &mut PhysicalTarget) -> Result<(), Error>
if !args.monitor { break }
let cause = Monitor::new()
.add_hook(Key::Ctrl('f'), if args.cmd.is_none() { "Re-flash target" } else { "Re-run command and flash target" })
.run(target.port())?;
let mut mon = Monitor::new();
if cause != Key::Ctrl('f') { break }
let reflash_handle = mon.add_hook(KeyCode::Char('f'), KeyModifiers::CONTROL, if args.cmd.is_none() { "Re-flash target" } else { "Re-run command and flash target" });
let cause = mon.run(target.port())?;
if cause != reflash_handle { break }
target.resync_target()?;
}
@@ -255,11 +264,20 @@ fn monitor_chip(_args: ArgMonitor, target: &mut PhysicalTarget) -> Result<(), Er
}
fn run_chip(args: ArgRun, target: &mut PhysicalTarget) -> Result<(), Error> {
let path = PathBuf::from(&args.path).with_extension("ihx").into_os_string();
let cmd = Some(OsString::from(format!("sdcc \"{}\"", unsafe { String::from_utf8_unchecked(args.path.into_encoded_bytes()) })));
let build_dir = args.path.parent().unwrap().join("tulflash");
let source_file_name = args.path.file_name().ok_or(Error::other("invalid path to compilation source"))?;
let source_file = build_dir.join(&source_file_name);
let output_file = source_file.with_extension("ihx");
let cmd = Some(OsString::from(format!("sdcc \"{}\"", args.path.canonicalize()?.to_string_lossy())));
let cmd_path = Some(build_dir.clone());
std::fs::create_dir_all(build_dir)?;
write_chip(ArgWrite {
path,
path: output_file.into_os_string(),
cmd_path,
cmd,
start: 0,
end: 65535,
@@ -270,15 +288,50 @@ fn run_chip(args: ArgRun, target: &mut PhysicalTarget) -> Result<(), Error> {
}, target)
}
fn gen_example(args: ArgExample) -> Result<(), Error> {
let path = PathBuf::from(args.path.unwrap_or(OsString::from("example.c")));
if path.try_exists()? {
Err(Error::other("File already exists"))?
}
std::fs::write(&path, include_bytes!("../example.c"))?;
let path = path.to_string_lossy();
println!("Example source code generated to `{}`.", path);
println!("Execute `tulflash run \"{}\"` to run it.", path);
Ok(())
}
fn main() {
let args = args::parse();
if let ArgCommand::Example(args) = args.command {
if let Err(err) = gen_example(args) {
println!("Error generating example source code: {}", err);
}
return
}
let start = SystemTime::now();
let port = args.port.or_else(|| {
for port in serial_enumerator::get_serial_list() {
if let Some(vendor) = port.vendor && vendor.as_str() == "TUL" {
return Some(port.name.into())
let ports = serial_enumerator::get_serial_list();
// First try TUL (works on Linux and macOS)...
for port in &ports {
if let Some(vendor) = &port.vendor && vendor.as_str() == "TUL" {
return Some(port.name.clone().into())
}
}
// ...then try FTDI (which Windows overrides)
for port in &ports {
if let Some(vendor) = &port.vendor && vendor.as_str() == "FTDI" {
return Some(port.name.clone().into())
}
}
@@ -288,6 +341,25 @@ fn main() {
let Some(port) = port else {
println!("Could not find any compatible serial port. Please make sure that the target is connected.");
println!("If the issue persists, please specify the port manually with the `--port ...` argument.");
println!();
let ports = serial_enumerator::get_serial_list();
if ports.is_empty() {
println!("No usable ports detected.");
}
if !ports.is_empty() {
println!("Possible ports:");
for port in serial_enumerator::get_serial_list() {
println!(" - {} ({}, {})",
port.name,
port.vendor.unwrap_or_else(|| "unknown vendor".to_string()),
port.product.unwrap_or_else(|| "unknown product".to_string()));
}
}
std::process::exit(1);
};
@@ -298,7 +370,8 @@ fn main() {
ArgCommand::Write(args) => write_chip(args, &mut target),
ArgCommand::Erase(args) => erase_chip(args, &mut target),
ArgCommand::Monitor(args) => monitor_chip(args, &mut target),
ArgCommand::Run(args) => run_chip(args, &mut target)
ArgCommand::Run(args) => run_chip(args, &mut target),
ArgCommand::Example(_) => unreachable!()
};
if let Err(err) = res {

View File

@@ -1,32 +1,46 @@
use std::io::{Error, ErrorKind, Write};
use std::time::Duration;
use crossterm::event::{Event, KeyCode, KeyModifiers};
use serial2::SerialPort;
use termion::raw::IntoRawMode;
use termion::event::Key;
use termion::input::TermRead;
struct RawMode {}
impl RawMode {
fn init() -> Result<Self, Error> {
crossterm::terminal::enable_raw_mode()?;
Ok(Self {})
}
}
impl Drop for RawMode {
fn drop(&mut self) {
let _ = crossterm::terminal::disable_raw_mode();
}
}
pub struct Monitor {
hooks: Vec<(Key, &'static str)>
hooks: Vec<(KeyCode, KeyModifiers, &'static str)>
}
impl Monitor {
pub fn new() -> Self {
let mut hooks = Vec::new();
hooks.push((Key::Ctrl('c'), "Exit monitor"));
hooks.push((Key::Ctrl('r'), "Reboot target"));
hooks.push((KeyCode::Char('c'), KeyModifiers::CONTROL, "Exit monitor"));
hooks.push((KeyCode::Char('r'), KeyModifiers::CONTROL, "Reboot target"));
Self { hooks }
}
pub fn add_hook(&mut self, key: Key, desc: &'static str) -> &mut Self {
self.hooks.push((key, desc));
self
pub fn add_hook(&mut self, key: KeyCode, mods: KeyModifiers, desc: &'static str) -> usize {
let idx = self.hooks.len();
self.hooks.push((key, mods, desc));
idx
}
pub fn run(&mut self, port: &mut SerialPort) -> Result<Key, Error> {
pub fn run(&mut self, port: &mut SerialPort) -> Result<usize, Error> {
port.set_dtr(true)?;
port.set_rts(true)?;
std::thread::sleep(Duration::from_millis(100));
@@ -36,10 +50,10 @@ impl Monitor {
println!();
println!("Monitor controls:");
for (key, desc) in &self.hooks {
let key = match key {
Key::Char(c) => format!("{}", c.to_uppercase()),
Key::Ctrl(c) => format!("Ctrl+{}", c.to_uppercase()),
for (key, mods, desc) in &self.hooks {
let key = match (key, *mods) {
(KeyCode::Char(c), KeyModifiers::NONE) => format!("{}", c.to_uppercase()),
(KeyCode::Char(c), KeyModifiers::CONTROL) => format!("Ctrl+{}", c.to_uppercase()),
key => format!("{:?}", key)
};
@@ -48,9 +62,8 @@ impl Monitor {
println!();
let mut stdout = std::io::stdout().into_raw_mode().unwrap();
let mut stdin = termion::async_stdin().keys();
let _raw = RawMode::init()?;
let mut stdout = std::io::stdout();
'main: loop {
let mut buf = [0u8; 1];
@@ -70,21 +83,25 @@ impl Monitor {
Err(err)?
}
if let Some(Ok(key)) = stdin.next() {
if let Key::Ctrl('r') = key {
if crossterm::event::poll(Duration::ZERO)? {
let (key, mods) = match crossterm::event::read()? {
Event::Key(ev) => (ev.code, ev.modifiers),
_ => continue
};
if key == KeyCode::Char('r') && mods == KeyModifiers::CONTROL {
stdout.write_all(b"\r\n*** Reboot ***\r\n")?;
port.set_dtr(true)?;
port.set_rts(true)?;
port.set_rts(false)?;
std::thread::sleep(Duration::from_millis(10));
port.set_dtr(false)?;
port.set_rts(false)?;
continue
}
for (hook_key, _) in &self.hooks {
if hook_key == &key {
for (idx, (hook_key, hook_mods, _)) in self.hooks.iter().enumerate() {
if hook_key == &key && hook_mods == &mods {
stdout.write_all(b"\r\n")?;
break 'main Ok(key)
break 'main Ok(idx)
}
}
}

View File

@@ -31,13 +31,12 @@ impl PhysicalTarget {
let name = new.read_info(0x00, 0x02).unwrap();
let rev = new.read_info(0x00, 0x03).unwrap();
println!("Detected chip ID: {manufacturer:02X} {family:02X} {name:02X} {rev:02X} (whatever the fuck that means)");
println!("Detected chip ID: {manufacturer:02X} {family:02X} {name:02X} {rev:02X} (whatever that means)");
Ok(new)
}
pub fn resync_target(&mut self) -> Result<(), Error> {
self.port.discard_buffers()?;
self.port.set_dtr(true)?;
self.port.set_rts(true)?;
std::thread::sleep(Duration::from_millis(10));
@@ -45,10 +44,12 @@ impl PhysicalTarget {
std::thread::sleep(Duration::from_millis(100));
for i in (0..5).rev() {
self.port.discard_buffers()?;
std::thread::sleep(Duration::from_millis(10));
self.port.write(b"U")?;
let mut buf = [0u8; 1];
self.port.read(&mut buf)?;
let _ = self.port.read(&mut buf);
if buf[0] == b'U' { break }
@@ -243,10 +244,9 @@ impl Target for PhysicalTarget {
impl Drop for PhysicalTarget {
fn drop(&mut self) {
let _ = self.port.set_dtr(true);
let _ = self.port.set_rts(true);
let _ = self.port.set_rts(false);
std::thread::sleep(Duration::from_millis(100));
let _ = self.port.set_dtr(false);
let _ = self.port.set_rts(false);
}
}