Compare commits

..

24 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
9 changed files with 488 additions and 110 deletions

1
.gitignore vendored
View File

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

325
Cargo.lock generated
View File

@@ -44,6 +44,12 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "autocfg"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
[[package]] [[package]]
name = "bitflags" name = "bitflags"
version = "2.9.4" version = "2.9.4"
@@ -56,6 +62,15 @@ version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" 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]] [[package]]
name = "crossbeam-channel" name = "crossbeam-channel"
version = "0.5.15" version = "0.5.15"
@@ -71,12 +86,79 @@ version = "0.8.21"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" 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]] [[package]]
name = "defer-lite" name = "defer-lite"
version = "1.0.0" version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6a1ef0750c0fcdb9bd00e7829fcad58e9a0a2ab7078b57c39743b973f3878ce" 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]] [[package]]
name = "getopts" name = "getopts"
version = "0.2.24" version = "0.2.24"
@@ -93,16 +175,33 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543"
[[package]] [[package]]
name = "libredox" name = "linux-raw-sys"
version = "0.1.10" version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index" 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 = [ dependencies = [
"bitflags", "autocfg",
"libc", "scopeguard",
"redox_syscall",
] ]
[[package]]
name = "log"
version = "0.4.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432"
[[package]] [[package]]
name = "mach" name = "mach"
version = "0.1.2" version = "0.1.2"
@@ -124,6 +223,18 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" 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]] [[package]]
name = "nom" name = "nom"
version = "7.1.3" version = "7.1.3"
@@ -135,10 +246,27 @@ dependencies = [
] ]
[[package]] [[package]]
name = "numtoa" name = "parking_lot"
version = "0.2.4" version = "0.12.4"
source = "registry+https://github.com/rust-lang/crates.io-index" 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]] [[package]]
name = "pbr" name = "pbr"
@@ -191,10 +319,23 @@ dependencies = [
] ]
[[package]] [[package]]
name = "redox_termios" name = "rustix"
version = "0.1.3" version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index" 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]] [[package]]
name = "serial2" name = "serial2"
@@ -228,6 +369,42 @@ version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" 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]] [[package]]
name = "syn" name = "syn"
version = "2.0.106" version = "2.0.106"
@@ -239,28 +416,16 @@ dependencies = [
"unicode-ident", "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]] [[package]]
name = "tulflash" name = "tulflash"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"argp", "argp",
"crossterm",
"pbr", "pbr",
"serial2", "serial2",
"serial_enumerator", "serial_enumerator",
"shlex", "shlex",
"termion",
] ]
[[package]] [[package]]
@@ -275,12 +440,24 @@ version = "1.0.19"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d" checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d"
[[package]]
name = "unicode-segmentation"
version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"
[[package]] [[package]]
name = "unicode-width" name = "unicode-width"
version = "0.2.1" version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a1a07cc7db3810833284e8d372ccdc6da29741639ecc70c9ec107df0fa6154c" 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]] [[package]]
name = "winapi" name = "winapi"
version = "0.3.9" version = "0.3.9"
@@ -309,39 +486,127 @@ version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aac7fef12f4b59cd0a29339406cc9203ab44e440ddff6b3f5a41455349fa9cf3" checksum = "aac7fef12f4b59cd0a29339406cc9203ab44e440ddff6b3f5a41455349fa9cf3"
dependencies = [ dependencies = [
"windows_aarch64_msvc", "windows_aarch64_msvc 0.29.0",
"windows_i686_gnu", "windows_i686_gnu 0.29.0",
"windows_i686_msvc", "windows_i686_msvc 0.29.0",
"windows_x86_64_gnu", "windows_x86_64_gnu 0.29.0",
"windows_x86_64_msvc", "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]] [[package]]
name = "windows_aarch64_msvc" name = "windows_aarch64_msvc"
version = "0.29.0" version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3d027175d00b01e0cbeb97d6ab6ebe03b12330a35786cbaca5252b1c4bf5d9b" checksum = "c3d027175d00b01e0cbeb97d6ab6ebe03b12330a35786cbaca5252b1c4bf5d9b"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]] [[package]]
name = "windows_i686_gnu" name = "windows_i686_gnu"
version = "0.29.0" version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8793f59f7b8e8b01eda1a652b2697d87b93097198ae85f823b969ca5b89bba58" 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]] [[package]]
name = "windows_i686_msvc" name = "windows_i686_msvc"
version = "0.29.0" version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8602f6c418b67024be2996c512f5f995de3ba417f4c75af68401ab8756796ae4" checksum = "8602f6c418b67024be2996c512f5f995de3ba417f4c75af68401ab8756796ae4"
[[package]]
name = "windows_i686_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]] [[package]]
name = "windows_x86_64_gnu" name = "windows_x86_64_gnu"
version = "0.29.0" version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3d615f419543e0bd7d2b3323af0d86ff19cbc4f816e6453f36a2c2ce889c354" 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]] [[package]]
name = "windows_x86_64_msvc" name = "windows_x86_64_msvc"
version = "0.29.0" version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "11d95421d9ed3672c280884da53201a5c46b7b2765ca6faf34b0d71cf34a3561" 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" version = "0.1.0"
edition = "2024" edition = "2024"
[profile.release]
opt-level = "s"
strip = true
[dependencies] [dependencies]
argp = "0.4.0" argp = "0.4.0"
crossterm = "0.29.0"
pbr = "1.1.1" pbr = "1.1.1"
serial2 = "0.2.32" serial2 = "0.2.32"
serial_enumerator = "0.2.12" serial_enumerator = "0.2.12"
shlex = "1.3.0" 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 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
# 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 ```bash
# All OSes, automatically detects the correct serial port # All OSes, automatically detects the correct serial port
tulflash write <path-to-hex> 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). 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 <stdio.h>
#include <stdint.h> #include <stdint.h>
#define TICK_RATE 100 #define TICK_RATE 100
#define NPER (0x10000 - (20000000 / 12 / TICK_RATE)) #define NPER (0x10000 - (20000000 / 6 / 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;
// I/O definitions // I/O definitions
#define BUTTON_1 P3_2 #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. // Quick'n'dirty CPU pause, just enough for the LCD to stabilize.
void lcd_pause(void) { void lcd_pause(void) {
int i = 30; __asm__ ("nop");
while(i--);
} }
// Reads the status register from the LCD. // Reads the status register from the LCD.
@@ -170,11 +162,8 @@ void lcd_init(void) {
// Puts a character to the LCD. Correctly handles '\n'. // Puts a character to the LCD. Correctly handles '\n'.
void lcd_putchar(char c) { void lcd_putchar(char c) {
if(c == 10) { if(c == 10) {
while((lcd_pos != 0) && (lcd_pos != 40)) { if(lcd_pos < 40) lcd_pos = 40;
lcd_send_data(32); else lcd_pos = 0;
lcd_pos++;
if(lcd_pos == 80) lcd_pos=0;
}
} else { } else {
lcd_send_data(c); lcd_send_data(c);
lcd_pos++; lcd_pos++;
@@ -184,6 +173,8 @@ void lcd_putchar(char c) {
// Initializes the background tick timer (for delays). // Initializes the background tick timer (for delays).
void timer_init(void) { 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 // Initialize timer 0 as 16-bit countdown, timer 1 as 8-bit auto-reload countdown
TMOD = 0x21; TMOD = 0x21;
@@ -196,7 +187,8 @@ void timer_init(void) {
// Enable interrupts only for timer 0 // Enable interrupts only for timer 0
IE = 0x82; EA = 1;
ET0 = 1;
} }
// Initializes the serial port (along with timer 2). // 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) // Initialize timer 2 as 312500 Hz as the baud rate generator (for tulflash)
RCAP2H = 0xFF; RCAP2H = 0xFF;
RCAP2L = 0xFE; RCAP2L = 0xFC;
T2CON = 0x34; T2CON = 0x34;
// Initialize serial I/O // Initialize serial I/O
@@ -217,6 +209,7 @@ void serial_init(void) {
void serial_putchar(char c) { void serial_putchar(char c) {
while(TI == 0); while(TI == 0);
SBUF = c; SBUF = c;
TI = 0;
} }
__bit stdout_to_lcd = 0; __bit stdout_to_lcd = 0;
@@ -296,6 +289,8 @@ void main(void) {
lcd_init(); lcd_init();
serial_init(); serial_init();
delay(1);
stdout_to_serial = 1; stdout_to_serial = 1;
stdout_to_lcd = 1; stdout_to_lcd = 1;
@@ -303,7 +298,7 @@ void main(void) {
while(1) { while(1) {
lcd_clear(); lcd_clear();
printf("hellOwOrld! %d\n", i); printf("Hellorld! %d\n", i);
led_bar_set(i); led_bar_set(i);
LED_RED = !(i & 1); LED_RED = !(i & 1);

View File

@@ -24,7 +24,8 @@ pub enum ArgCommand {
Write(ArgWrite), Write(ArgWrite),
Erase(ArgErase), Erase(ArgErase),
Monitor(ArgMonitor), Monitor(ArgMonitor),
Run(ArgRun) Run(ArgRun),
Example(ArgExample)
} }
#[derive(FromArgs)] #[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.")] #[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>, 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(option, default = "0")]
#[argp(description = "Start address where to write from. Only for raw binaries, defaults to 0.")] #[argp(description = "Start address where to write from. Only for raw binaries, defaults to 0.")]
pub start: usize, pub start: usize,
@@ -77,7 +82,7 @@ pub struct ArgWrite {
pub skip_verify: bool, pub skip_verify: bool,
#[argp(switch)] #[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, pub write_individual: bool,
#[argp(switch)] #[argp(switch)]
@@ -91,18 +96,27 @@ pub struct ArgWrite {
pub struct ArgErase {} pub struct ArgErase {}
#[derive(FromArgs)] #[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")] #[argp(subcommand, name = "run")]
pub struct ArgRun { pub struct ArgRun {
#[argp(positional)] #[argp(positional)]
#[argp(description = "Path to the source C file.")] #[argp(description = "Path to the source C file.")]
pub path: OsString pub path: std::path::PathBuf
} }
#[derive(FromArgs)] #[derive(FromArgs)]
#[argp(description = "Opens the serial monitor.")] #[argp(description = "Generates a \"stub\" C file for the TUL výukový přípravek™.")]
#[argp(subcommand, name = "monitor")] #[argp(subcommand, name = "example")]
pub struct ArgMonitor {} pub struct ArgExample {
#[argp(positional)]
#[argp(description = "Path to the source C file to be generated.")]
pub path: Option<OsString>
}
pub fn parse() -> Args { pub fn parse() -> Args {
let args: Args = argp::parse_args_or_exit(argp::DEFAULT); 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::path::PathBuf;
use std::time::{Duration, SystemTime}; use std::time::{Duration, SystemTime};
use crossterm::event::{KeyCode, KeyModifiers};
use pbr::ProgressBar; use pbr::ProgressBar;
mod args; mod args;
@@ -16,7 +17,6 @@ use monitor::Monitor;
mod target; mod target;
use target::{PhysicalTarget, Target}; use target::{PhysicalTarget, Target};
use termion::event::Key;
mod utils; 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> { fn write_chip(args: ArgWrite, target: &mut PhysicalTarget) -> Result<(), Error> {
loop { loop {
if let Some(cmd) = args.cmd.as_ref() { if let Some(cmd) = args.cmd.as_ref() {
let cmd_path = &args.cmd_path;
let mut args = shlex::bytes::split(cmd.as_encoded_bytes()) let mut args = shlex::bytes::split(cmd.as_encoded_bytes())
.ok_or(Error::other("Error parsing commandline arguments"))? .ok_or(Error::other("Error parsing commandline arguments"))?
.into_iter() .into_iter()
.map(|x| unsafe { OsString::from_encoded_bytes_unchecked(x) }); .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 { let Some(name) = args.next() else {
Err(Error::other("No command provided"))? Err(Error::other("No command provided"))?
}; };
let out = std::process::Command::new(name) 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) .args(args)
.output()?; .output()?;
@@ -230,11 +237,13 @@ fn write_chip(args: ArgWrite, target: &mut PhysicalTarget) -> Result<(), Error>
if !args.monitor { break } if !args.monitor { break }
let cause = Monitor::new() let mut mon = 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())?;
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()?; 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> { fn run_chip(args: ArgRun, target: &mut PhysicalTarget) -> Result<(), Error> {
let path = PathBuf::from(&args.path).with_extension("ihx").into_os_string(); let build_dir = args.path.parent().unwrap().join("tulflash");
let cmd = Some(OsString::from(format!("sdcc \"{}\"", unsafe { String::from_utf8_unchecked(args.path.into_encoded_bytes()) })));
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 { write_chip(ArgWrite {
path, path: output_file.into_os_string(),
cmd_path,
cmd, cmd,
start: 0, start: 0,
end: 65535, end: 65535,
@@ -270,15 +288,50 @@ fn run_chip(args: ArgRun, target: &mut PhysicalTarget) -> Result<(), Error> {
}, target) }, 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() { fn main() {
let args = args::parse(); 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 start = SystemTime::now();
let port = args.port.or_else(|| { let port = args.port.or_else(|| {
for port in serial_enumerator::get_serial_list() { let ports = serial_enumerator::get_serial_list();
if let Some(vendor) = port.vendor && vendor.as_str() == "TUL" {
return Some(port.name.into()) // 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 { let Some(port) = port else {
println!("Could not find any compatible serial port. Please make sure that the target is connected."); 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!("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); std::process::exit(1);
}; };
@@ -298,7 +370,8 @@ fn main() {
ArgCommand::Write(args) => write_chip(args, &mut target), ArgCommand::Write(args) => write_chip(args, &mut target),
ArgCommand::Erase(args) => erase_chip(args, &mut target), ArgCommand::Erase(args) => erase_chip(args, &mut target),
ArgCommand::Monitor(args) => monitor_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 { if let Err(err) = res {

View File

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

View File

@@ -37,7 +37,6 @@ impl PhysicalTarget {
} }
pub fn resync_target(&mut self) -> Result<(), Error> { pub fn resync_target(&mut self) -> Result<(), Error> {
self.port.discard_buffers()?;
self.port.set_dtr(true)?; self.port.set_dtr(true)?;
self.port.set_rts(true)?; self.port.set_rts(true)?;
std::thread::sleep(Duration::from_millis(10)); std::thread::sleep(Duration::from_millis(10));
@@ -45,10 +44,12 @@ impl PhysicalTarget {
std::thread::sleep(Duration::from_millis(100)); std::thread::sleep(Duration::from_millis(100));
for i in (0..5).rev() { for i in (0..5).rev() {
self.port.discard_buffers()?;
std::thread::sleep(Duration::from_millis(10));
self.port.write(b"U")?; self.port.write(b"U")?;
let mut buf = [0u8; 1]; let mut buf = [0u8; 1];
self.port.read(&mut buf)?; let _ = self.port.read(&mut buf);
if buf[0] == b'U' { break } if buf[0] == b'U' { break }
@@ -243,10 +244,9 @@ impl Target for PhysicalTarget {
impl Drop for PhysicalTarget { impl Drop for PhysicalTarget {
fn drop(&mut self) { fn drop(&mut self) {
let _ = self.port.set_dtr(true); 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)); std::thread::sleep(Duration::from_millis(100));
let _ = self.port.set_dtr(false); let _ = self.port.set_dtr(false);
let _ = self.port.set_rts(false);
} }
} }