@@ -25,8 +25,11 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
2525
2626#include <rvvm/rvvm.h>
2727
28+ #include <util/threading.h>
2829#include <util/utils.h>
2930
31+ #include <stdio.h> // parport sink/source use FILE* on all targets
32+
3033#include <core/rvvm_isolation.h>
3134#include <core/gdbstub.h>
3235#include <core/rvvm_user.h>
@@ -37,6 +40,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
3740#include <devices/i2c-oc.h>
3841#include <devices/ns16550a.h>
3942#include <devices/nvme.h>
43+ #include <devices/parport-pci.h>
4044#include <devices/pci-bus.h>
4145#include <devices/pci-vfio.h>
4246#include <devices/riscv-aclint.h>
@@ -199,6 +203,9 @@ static void rvvm_print_help(void)
199203 " -ata ... Explicitly attach storage image as ATA (IDE) device\n"
200204 " -nogui Disable display GUI\n"
201205 " -nosound Disable sound support\n"
206+ " -parport_test Attach an emulated NetMos 9900 PCI parallel port\n"
207+ " -parport_out ... File to receive parport output (default: /tmp/rvvm-parport0.out)\n"
208+ " -parport_in ... File/fifo to source parport reverse-channel input from\n"
202209 " -nonet Disable networking\n"
203210 " -serial ... Add more serial ports (Via pty/pipe path), or null\n"
204211 " -dtb ... Pass custom Device Tree Blob to the machine\n"
@@ -212,6 +219,42 @@ static void rvvm_print_help(void)
212219 print_stderr (help );
213220}
214221
222+ // Bridge for the -parport_test default backend: each strobed byte from the
223+ // guest's parallel port is appended to a host file. user_data carries the
224+ // FILE* opened in rvvm_cli_configure. Called outside the device's lock so
225+ // the write may safely block briefly.
226+ static void parport_main_write_fn (void * user_data , uint8_t byte )
227+ {
228+ FILE * fp = (FILE * )user_data ;
229+ if (fp ) fputc (byte , fp );
230+ }
231+
232+ // Reader thread for -parport_in: blocks on read() from a host file or
233+ // fifo and pushes each byte into the device's reverse-channel ring.
234+ // EOF / error ends the thread, leaving any further guest reads to see
235+ // nFault asserted (end-of-data) on Status.
236+ typedef struct {
237+ pci_dev_t * dev ;
238+ FILE * fp ;
239+ } parport_in_ctx_t ;
240+
241+ static void * parport_in_thread (void * arg )
242+ {
243+ parport_in_ctx_t * ctx = arg ;
244+ int c ;
245+ while ((c = fgetc (ctx -> fp )) != EOF ) {
246+ // Spin until the ring has space. Slow guest readers shouldn't
247+ // burn the CPU, so back off when full — sched_yield is plenty
248+ // for ringbuffer pacing.
249+ while (!parport_pci_inject_byte (ctx -> dev , (uint8_t )c )) {
250+ rvvm_sched_yield ();
251+ }
252+ }
253+ fclose (ctx -> fp );
254+ free (ctx );
255+ return NULL ;
256+ }
257+
215258static bool rvvm_cli_configure (rvvm_machine_t * machine , const char * bios , tap_dev_t * tap )
216259{
217260 UNUSED (tap );
@@ -374,6 +417,39 @@ static int rvvm_cli_main(int argc, char** argv)
374417 sound_hda_init_auto (machine );
375418 }
376419
420+ if (rvvm_has_arg ("parport_test" )) {
421+ // Default backend writes guest parport output to a host file.
422+ // Override path with -parport_out <path>; /dev/stdout dumps to
423+ // the controlling terminal.
424+ const char * path = rvvm_getarg ("parport_out" );
425+ if (path == NULL ) path = "/tmp/rvvm-parport0.out" ;
426+ FILE * fp = fopen (path , "wb" );
427+ pci_dev_t * parport_dev = NULL ;
428+ if (fp ) {
429+ setvbuf (fp , NULL , _IONBF , 0 ); // unbuffered — bytes appear immediately
430+ rvvm_info ("parport: writes go to %s" , path );
431+ parport_dev = parport_pci_init_auto (machine , parport_main_write_fn , fp );
432+ } else {
433+ rvvm_warn ("parport: failed to open %s, attaching with no backend" , path );
434+ parport_dev = parport_pci_init_auto (machine , NULL , NULL );
435+ }
436+ // Reverse channel: feed bytes from -parport_in <path> into the
437+ // ring. Useful for guest-side dd/cat tests of bidirectional 1284.
438+ const char * in_path = rvvm_getarg ("parport_in" );
439+ if (parport_dev && in_path ) {
440+ FILE * in_fp = fopen (in_path , "rb" );
441+ if (in_fp ) {
442+ rvvm_info ("parport: reverse-channel input from %s" , in_path );
443+ parport_in_ctx_t * ctx = safe_new_obj (parport_in_ctx_t );
444+ ctx -> dev = parport_dev ;
445+ ctx -> fp = in_fp ;
446+ rvvm_thread_detach (rvvm_thread_create (parport_in_thread , ctx ));
447+ } else {
448+ rvvm_warn ("parport: failed to open %s for reverse channel" , in_path );
449+ }
450+ }
451+ }
452+
377453 tap_dev_t * tap = NULL ;
378454#ifdef USE_NET
379455 if (!rvvm_has_arg ("nonet" )) {
0 commit comments