diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..488c678 --- /dev/null +++ b/Makefile @@ -0,0 +1,52 @@ +-include Makelocal.mk +PATH_SDCC ?= /usr/bin +PATH_HEX2BIN ?= /usr/bin +PATH_BIN := bin +BUILD_ALL := "Use 'make help' for possible targets." +BUILD_HELP := "Use one of the following build targets;" +BUILD_VERSION ?= 1.0 +BUILD_NAME := omsxctl-$(BUILD_VERSION) +.SUFFIXES: + +.PHONY: all +all: + @echo $(BUILD_ALL) + +BUILD_HELP += \\n\\t* help +.PHONY: help +help: + @echo $(BUILD_HELP) + +BUILD_HELP += \\n\\t* clean +.PHONY: clean +clean: + rm bin -rf + +bin/omsxctl: + mkdir -p bin/omsxctl + +bin/omsxctl/%.rel: src/%.asm | bin/omsxctl + $(PATH_SDCC)/sdasz80 -g -l -c -o $@ $< + +bin/omsxctl/omsxctl.hex: bin/omsxctl/omsxctl.rel + $(PATH_SDCC)/sdcc -mz80 --no-std-crt0 --code-loc 0x0100 -o $@ $< + +BUILD_HELP += \\n\\t* bin/omsxctl/omsxctl.com +bin/omsxctl/omsxctl.com: bin/omsxctl/omsxctl.hex + cd bin/omsxctl && $(PATH_HEX2BIN)/hex2bin -e com omsxctl.hex + +bin/dist: + mkdir -p bin/dist + +bin/dist/$(BUILD_NAME): | bin/dist + mkdir -p bin/dist/$(BUILD_NAME) + +bin/dist/$(BUILD_NAME)/omsxctl.com: bin/dist/$(BUILD_NAME) | bin/omsxctl/omsxctl.com + cp bin/omsxctl/omsxctl.com bin/dist/$(BUILD_NAME)/omsxctl.com + +bin/dist/$(BUILD_NAME)/omsxctl.tcl: bin/dist/$(BUILD_NAME) + cp src/omsxctl.tcl bin/dist/$(BUILD_NAME)/omsxctl.tcl + +BUILD_HELP += \\n\\t* bin/dist/$(BUILD_NAME).tar.gz +bin/dist/$(BUILD_NAME).tar.gz: bin/dist/$(BUILD_NAME)/omsxctl.com bin/dist/$(BUILD_NAME)/omsxctl.tcl + cd bin/dist && tar -czvf $(BUILD_NAME).tar.gz $(BUILD_NAME) diff --git a/Makelocal.mk.tpl b/Makelocal.mk.tpl new file mode 100644 index 0000000..1342dfe --- /dev/null +++ b/Makelocal.mk.tpl @@ -0,0 +1,7 @@ +# Local included makefile fragment. +# Save this file without tpl suffix and custumize it. +# +# Change to local installations if needed; + +#PATH_SDCC ?= /usr/bin +#PATH_HEX2BIN ?= /usr/bin diff --git a/README.md b/README.md index 018a700..2c3e8b2 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,26 @@ # omsxctl -OpenMSXConTroL Utility and script \ No newline at end of file +'OpenMSXConTroL' is an utility to send control commands to openMSX from within the emulated msx. + +Original idea by 'immetoo' implemented with help from 'm9710797' +(see https://github.com/openMSX/openMSX/issues/1133) + +## Usage + +Pass the script on the openMSX command line: + + openmsx -script /omsxctl.tcl +And then in MSX-DOS execute something like: + + omsxctl set throttle off + ... + omsxctl exit + +## build + +Copy Makelocal.mk.tpl to Makelocal.mk and setup your lib paths and type; + +``` +make help + +``` diff --git a/src/omsxctl.asm b/src/omsxctl.asm new file mode 100644 index 0000000..13385e6 --- /dev/null +++ b/src/omsxctl.asm @@ -0,0 +1,132 @@ +; MSX-DOS utility to execute openMSX commands from within the MSX. +; +; This can be used to e.g. integrate a MSX program in a build chain +; on the host computer. +; +; For example start openMSX from the command line like this: +; openmsx -script omsxctl.tcl -diska my_directory +; +; And inside my_directory/ put the a AUTOEXEC.BAT with content: +; omsxctl after time 100 "exit 1" +; my_tool my_data.in my_data.out +; omsxctl exit 0 + +bdos .equ 0x5 +PutChr .equ 0x2 +ArgSize .equ 0x0080 +ArgData .equ 0x0081 +ScriptPort .equ 0x2d + +.area _CODE + ; Detect whether the 'omsxctl.tcl' script is active. + ld hl,#ZeroStr ; empty command (doesn't produce output) + scf + out (ScriptPort),a ; script should change clear carry flag + jr c,ErrNoScript + + ; Process command line. + ld a,(ArgSize) + or a + jr z,ErrNoArgs + ld hl,#ArgData + add a,l + ld l,a + ld (hl),#0 ; write 0-terminator (needed for DOS1?) + + ; Execute command. + ld hl,#ArgData+1 ; skip first space + ld de,#ResultBuf + ld bc,#ResultBufSize + out (ScriptPort),a + + ; Did the command give an error? + jr nc,NoError + ld hl,#TxtCmdError + push bc + call Print + pop bc +NoError: + + ; Was the result string truncated? + ld a,b + and c + inc a ; BC == 0xffff ? + jr nz,NotTruncated + ld hl,#TxtTruncated + call Print + ld bc,#ResultBufSize +NotTruncated: + + ; Print the result string and translate \n to \r\n. + ld hl,#ResultBuf +ResultLoop: + ld a,b + or c + jr z,Done + push hl + push bc + ld a,(hl) + cp #10 + jr nz,Not10 + ld e,#13 + ld c,#PutChr + call bdos + ld a,#10 +Not10: + ld e,a + ld c,#PutChr + call bdos + pop bc + pop hl + inc hl + dec bc + jr ResultLoop + +Done: + ld hl,#TxtNewLine + jr Print + +ErrNoScript: + ld hl,#TxtErrNoScript + jr Print + +ErrNoArgs: + ld hl,#TxtErrNoArgs + ; print + +Print: + ld a,(hl) + or a + ret z + ld e,a + ld c,#PutChr + push hl + call bdos + pop hl + inc hl + jr Print + + +TxtErrNoScript: + .str "The 'omsxctl.tcl' script is not active." + .db 0 + +TxtErrNoArgs: + .str "No arguments given." + .db 0 + +TxtCmdError: + .str "ERROR: " + .db 0 + +TxtTruncated: + .str "[truncated]" +TxtNewLine: + .db 13, 10 +ZeroStr: + .db 0 + +ResultBuf: +ResultBufSize .equ 0x4000 + +.area _DATA diff --git a/src/omsxctl.tcl b/src/omsxctl.tcl new file mode 100644 index 0000000..95cc571 --- /dev/null +++ b/src/omsxctl.tcl @@ -0,0 +1,57 @@ +# omsxctl -- execute openMSX commands from within an MSX program +# +# Typically used in combination with the MSX-DOS 'omsxctl.com' utility. +# +# But can also be used from any other MSX program with a code snippet like +# this: +# ld hl,command ; pointer to the to be executed command +# ld de,result ; the result of the command is written here +# ld bc,resultSize ; size of the result buffer (set to 0 if result is not needed) +# out (#2d),a ; the value of A doesn't matter +# jp c,error ; carry-flag set if there was an error executing the command +# ; BC is set to the actual length of the result string, +# ; or set to 0xffff when the result buffer was too small, +# ; in that case the result is still written to [DE] but truncated. +# +# Limitations: +# Strings containing characters >=128 are not handled well: +# - For passing the command string from the MSX to openMSX this could be +# fixed (but is it worth it?). +# - For passing the result from openMSX to the MSX I currently don't see a +# solution that handles both arbitrary utf-8 strings and binary data (e.g. +# created via the 'binary format' Tcl command). The current implementation +# chooses to handle binary data (and of course pure ASCII strings work fine +# as well). + +proc trigger_omsxctl {} { + # Read the command string from memory. + set hl [reg HL] + set cmd "" + while 1 { + set c [peek $hl] + incr hl + if {$c == 0} break + append cmd [binary format c $c] + } + + # Execute the command. + set err [catch [list uplevel #0 $cmd] result] + + # Write the error status to the carry flag. + reg F [expr {([reg F] & 0xfe) | $err}] + + # Write the result back to memory. + set len [string length $result] + set de [reg DE] + set bc [reg BC] + for {set i 0} {$i < $bc && $i < $len} {incr i} { + binary scan [string index $result $i] c c + poke [expr {$de + $i}] $c + } + + # Write the actual result-length to BC. + reg BC [expr {($len <= $bc) ? $len : 0xffff}] +} + +# Register callback on write to IO-port 0x2d. +debug set_watchpoint write_io 0x2d 1 trigger_omsxctl