Fake I/O port using TCL?

Por Creepy

Champion (335)

Imagen del Creepy

26-06-2017, 21:38

I'm looking for a way to return data to the emulated MSX via an I/O port for debugging en testing purposes. I was hoping to use a TCL script for that, but my knowledge of TCL is not that great, and at this time I do not even have a clue if this is possible.

What I want to do is testing some code that reads ascii chars from an I/O port. I would like to be able to specificy which data is returned when reading from a given I/O port. Is this possible in OpenMSX? And if not with TCL, maby it's possible with some sort of (fake) pluggable device?

Login sesión o register para postear comentarios

Por Grauw

Ascended (10565)

Imagen del Grauw

26-06-2017, 22:04

Not sure whether a watchpoint executes before or after the read but; in the former case you could set a memory watchpoint and poke your values into a RAM memory address (which is then read), in the latter case you could alter the register value (probably want to assume it is “a” or sth for simplicity).

Por Creepy

Champion (335)

Imagen del Creepy

27-06-2017, 11:23

A memory breakpoint is a good idea. The input is read into a buffer so with a breakpoint on the buffer I can change the values from the debugger.

Por NYYRIKKI

Enlighted (5918)

Imagen del NYYRIKKI

27-06-2017, 11:26

I don't have much to add... but from more technical point of view you can trigger a TCL script with:
debug set_watchpoint read_io port {additional condition} {your routine name}

You then need to figure out what is going on with "peek" and "reg PC" you can use reg-command also to return the value to Z80 or with poke to memory in case ie. INIR was executed. Indeed some Z80 emulation will be required. I've used similar yet simpler trick to load the CAS-files, but in that case I was trapping only BIOS calls.

Por xavirompe

Resident (57)

Imagen del xavirompe

04-01-2022, 14:53

Hi all,
I'm looking for a way to intercept a read to a certain port to change the returned value.
For this I am using "debug set_watchpoint read_io port {0x20 0x21} {my_routine}", when I read the port using in a, (0x20) the watchpoint call to my routine and can evaluate wich port is reading using $ :: wp_last_address, then I tryed to change the input value using for example: reg a 65, but in the MSX code I always get 0xFF as return value.
Any idea how to fix this?

What I am trying to do is simulate an IO operation with external hardware connected through the serial port, at the moment the part related to the OUT instruction works correctly.

Por xavirompe

Resident (57)

Imagen del xavirompe

05-01-2022, 12:07

Hello again,
I seem to have found a way to make it work, I have added this:

after time 0 "reg a [expr $in_data]"

And now it works properly :-)

Por Manuel

Ascended (18734)

Imagen del Manuel

05-01-2022, 12:19

What is "$in_data"??

Por xavirompe

Resident (57)

Imagen del xavirompe

05-01-2022, 15:49

$in_data is a variable with the data readed from the serial port.

Por Manuel

Ascended (18734)

Imagen del Manuel

06-01-2022, 08:26

Okay, why is there expr around the variable?

Can you share your full solution?

Por xavirompe

Resident (57)

Imagen del xavirompe

07-01-2022, 12:17

Hi Manuel,
Here is the script code, for now it only works with IN, OUT instructions, it's not completed and I will need help to finish it.
The goal is to make it work with Konamiman's Noobtocol, used to interact with Rookie drive's USB chipset attached to an arduino.
I need some help to make INI, INR, OTI , OTIR instructions work.

namespace eval rookie_noobtocol {

variable file_handle ""
variable watchpoint_read_data
variable watchpoint_read_status
variable watchpoint_write

proc rookie_noobtocol_start {} {
	variable file_handle
	variable watchpoint_read_data
	variable watchpoint_read_status
	variable watchpoint_write

	set file_handle [open //./com8 RDWR]
	fconfigure $file_handle -blocking 1 -buffering none \
        -mode 115200,n,8,1 -translation binary -eofchar {}
	
	set watchpoint_write [debug set_watchpoint write_io {0x20 0x21} {} {rookie_noobtocol::trigger_write}]
	set watchpoint_read_data [debug set_watchpoint read_io {0x20} {} {rookie_noobtocol::trigger_read_data}]
	set watchpoint_read_status [debug set_watchpoint read_io {0x21} {} {rookie_noobtocol::trigger_read_status}]
	return "Rookie drive Noobtocol"
}

proc trigger_write {} {
	variable file_handle
	set address [reg PC]
    set instr [peek $address]
	set out_port [expr $::wp_last_address]
	set out_value [expr $::wp_last_value]

	if {$out_port == 0x20} {set noobvalue  0x03} else { set noobvalue 0x01}
	puts -nonewline $file_handle [binary format c [expr {$noobvalue}]]
	puts -nonewline $file_handle [binary format c [expr {$out_value}]]
}

proc trigger_read_status {} {
	variable file_handle
	
	set out_value 0x02
	puts -nonewline $file_handle [binary format c [expr {$out_value}]]
	#read serial port 1 byte and put in in_data variable
	binary scan [read $file_handle 1] c in_data
	set in_data [expr {$in_data & 0xff}]
	after time 0 "reg a [expr $in_data]"
}

proc trigger_read_data {} {
	variable file_handle
	
	set address [reg PC]
    set instr [peek $address]

	set out_value 0x04
	puts -nonewline $file_handle [binary format c [expr {$out_value}]]
	#read serial port 1 byte and put in in_data variable
	binary scan [read $file_handle 1] c in_data
	set in_data [expr {$in_data & 0xff}]
	after time 0 "reg a [expr $in_data]"
}

proc rookie_noobtocol_stop {} {
	variable file_handle
	variable watchpoint_write
	variable watchpoint_read_data
	variable watchpoint_read_status
	if {$file_handle ne ""} {
		close $file_handle
		set file_handle ""
		debug remove_watchpoint $watchpoint_read_data
		debug remove_watchpoint $watchpoint_read_status
		debug remove_watchpoint $watchpoint_write
		return "Stopped Rookie drive Noobtocol"
	}
}

namespace export rookie_noobtocol_start
namespace export rookie_noobtocol_stop

} ; #namespace

namespace import rookie_noobtocol::*