• Please review our updated Terms and Rules here

new FAST DOS VNC-like remote control server

Mike Chambers

Veteran Member
Joined
Sep 2, 2006
Messages
2,621
thought there would be some pretty good interest in this, i see the topic mentioned somewhat often.

firstly, i've been writing my own TCP/IP stack for DOS (MiniTCP) in 100% assembly. i wanted to try writing one that's a TSR, and to try and keep it as small/fast as possible. it doesn't actually do TCP yet, but it does perfectly handle the ARP, IP, and UDP layers (and it does properly forward packets for outside the local subnet out through a gateway) plus responds to ICMP pings. to test out the UDP code thoroughly, i thought it'd be cool to write a new VNC-like remote control app for DOS. it's MUCH faster than Tiny and telnetd. not only the network stack, but also the actual remote control server is in assembly as well.

so far, MiniTCP itself uses only 18 KB total memory as a TSR (including buffers) at this point, but it will definitely increase a bit once i add the TCP layer. the remote control app takes an additional 5 KB of RAM as TSR, so you're looking at a total of only 23 KB RAM usage for the whole setup here.

when the remote control is running, i only see about a 30-40% slowdown on my IBM 5150. it could use significantly less CPU if i were to have it check less lines on the screen for updates per clock tick. screenshot of the cheap little test client i wrote in VB6 (because it was fast to throw together just to see if the server worked)

remoter.png


^that's connected to the 5150 running the server.

the VB6 client is pretty slow (gotta love PSET) but tomorrow i was going to redo it proper in C, and plan to upload everything for everybody to try out then.

for some raw UDP performance numbers, my 12 MHz 286 with an NE2000-compatible NIC receives at 560 KB/s. :)

EDIT: as i posted later on, you can download it now:
server for DOS:
http://rubbermallet.org/remoter-server.zip

client for windows:
http://rubbermallet.org/remoter-client.zip


the IP stack doesn't do DHCP yet, so before you run minitcp.com you'll need to run setip.exe which will let you modify the IP, subnet mask, and gateway IP it will use. run minitcp.com first, then remoter.com

for the client, once the server is running on a DOS machine just run remoter.exe with the IP address of the server on the command line. oh, and just a note... it only supports 80x25 text mode so far, and it wont work with an MDA.
 
Last edited:
This is super interesting. Not just the TCP aspect, but a VNC for DOS. Who would have thought it. I could have used something like that back in the day.. I did some other remote-control stuff back then, but the idea of VNC wasn't in the mindset back in the eighties.

-Tor
 
Please, not another TCP/IP stack starting with an 'm' ;-0

You've had great progress since you've started on this.

I am guessing that at 18KB that you have room for 10 incoming packets and the rest is code. (This is assuming maximum Ethernet MTU of 1514.) It does not have to get a lot larger with TCP added - uTCP (Contiki/Adam Dunkels) remains quite small. What you trade off in space affects performance though - you can't get a reasonable sized TCP/IP sliding window without more memory, and that is needed for both sending and receiving. The ability to resend packets automatically, handle multiple concurrent sockets, and the ability to accept incoming sockets adds a lot of code too. And then there is the entire IP fragment function, which is often not needed but is required for completeness.

A 30 to 40% slowdown seems pretty harsh to handle. Instead of scanning the video buffer for updates I would just let the client send a UDP packet saying 'I need a refresh'. Upon receipt of that packet you send the contents of the screen back to the client. The lag time is slightly longer (the time it takes to send the first UDP packet), but you get rid of the background scanning. I'd gladly trade a millisecond or two of lag time for getting back to reasonable performance levels. Then you let the client decide how often to refresh the screen - if they need real-time updates then the machine will be slow, and if they don't the machine will be nearly full speed.

I don't think I have tested my UDP numbers since 2006 when I first got it running. My UDP implementation is designed for low bandwidth DNS processing. Back when I did use it for file transfers I was getting a few hundred KB per second on a 386-40, but the timer management code and the checksumming routing have improved quite a bit since 2006. A large part of the time was spent doing the file reading/writing, so it's not directly comparable.

The real speed measurement to shoot for is FTP over TCP/IP, with file I/O. That is what people do - they transfer files. I await your results ..


Mike
 
I'd consider making two versions -- one that only traps BIOS calls, and one that checks the video RAM. A good deal of software only relies on the BIOS video routines (or can be set to not write directly), so for those programs don't waste time with scanning video memory and only send data when the program sends data.

Hell, my old alloy slave network ran that way... the dumb serial terminals only working with text-mode software that called BIOS... that was a fun setup -- five 8088's "on a card" sharing the host AT machine's hard drive, with serial as their only video and input.
 
Please, not another TCP/IP stack starting with an 'm' ;-0

You've had great progress since you've started on this.

I am guessing that at 18KB that you have room for 10 incoming packets and the rest is code. (This is assuming maximum Ethernet MTU of 1514.) It does not have to get a lot larger with TCP added - uTCP (Contiki/Adam Dunkels) remains quite small. What you trade off in space affects performance though - you can't get a reasonable sized TCP/IP sliding window without more memory, and that is needed for both sending and receiving. The ability to resend packets automatically, handle multiple concurrent sockets, and the ability to accept incoming sockets adds a lot of code too. And then there is the entire IP fragment function, which is often not needed but is required for completeness.

A 30 to 40% slowdown seems pretty harsh to handle. Instead of scanning the video buffer for updates I would just let the client send a UDP packet saying 'I need a refresh'. Upon receipt of that packet you send the contents of the screen back to the client. The lag time is slightly longer (the time it takes to send the first UDP packet), but you get rid of the background scanning. I'd gladly trade a millisecond or two of lag time for getting back to reasonable performance levels. Then you let the client decide how often to refresh the screen - if they need real-time updates then the machine will be slow, and if they don't the machine will be nearly full speed.

I don't think I have tested my UDP numbers since 2006 when I first got it running. My UDP implementation is designed for low bandwidth DNS processing. Back when I did use it for file transfers I was getting a few hundred KB per second on a 386-40, but the timer management code and the checksumming routing have improved quite a bit since 2006. A large part of the time was spent doing the file reading/writing, so it's not directly comparable.

The real speed measurement to shoot for is FTP over TCP/IP, with file I/O. That is what people do - they transfer files. I await your results ..


Mike

heh, i'll try to think of a different name. for the buffers, there are actually 2 for incoming raw packets, 1 for IP receive, and 1 for IP send. i can just do away with multiple raw buffers i think, thats how i set it up when i first starting writing this but when a packet comes in, i'm just keeping interrupts disabled until all the processing on it is finished. i also reserved something like 4 KB for the stack which is MAJOR overkill so yeah i definitely can trim some fat off of it. and yes, raw UDP performance numbers aren't all that important. it's just a good way to measure the stack's performance without extra overhead.

the bit about letting clients determine how often to update the screen is not a bad idea, but for now i think i'm just going to drop how many lines it checks per clock tick from 10 to 5 and see how it runs for now. i plan to start working on TCP tonight btw.
 
I'd consider making two versions -- one that only traps BIOS calls, and one that checks the video RAM. A good deal of software only relies on the BIOS video routines (or can be set to not write directly), so for those programs don't waste time with scanning video memory and only send data when the program sends data.

Hell, my old alloy slave network ran that way... the dumb serial terminals only working with text-mode software that called BIOS... that was a fun setup -- five 8088's "on a card" sharing the host AT machine's hard drive, with serial as their only video and input.

this is something i was considering already. what i probably will do is a hybrid of both methods.. drop the amount of lines it checks per tick in half, plus also do immediate updates on whatever line the cursor is on whenever there is a function 0Eh call to int 10h.
 
heh, i'll try to think of a different name. for the buffers, there are actually 2 for incoming raw packets, 1 for IP receive, and 1 for IP send. i can just do away with multiple raw buffers i think, thats how i set it up when i first starting writing this but when a packet comes in, i'm just keeping interrupts disabled until all the processing on it is finished. i also reserved something like 4 KB for the stack which is MAJOR overkill so yeah i definitely can trim some fat off of it. and yes, raw UDP performance numbers aren't all that important. it's just a good way to measure the stack's performance without extra overhead.

the bit about letting clients determine how often to update the screen is not a bad idea, but for now i think i'm just going to drop how many lines it checks per clock tick from 10 to 5 and see how it runs for now. i plan to start working on TCP tonight btw.

You can name it whatever you like - don't change the name on the account of me. But having two guys named Mike both do new TCP/IP stacks for old PCs and then having them both start with an 'm' is going to lead to confusion, guaranteed. ;-0

With just two buffers you have more code there than I would have thought. I wouldn't disable interrupts while a packet is being processed; disabling interrupts should be the technique of last resort. Ideally your code can be interrupted most of the time and you deal with packet storms by having more available buffers. Unless you design goal is truly to be as small as possible - in that case you willingly trade space for performance.

Having the client control the refresh rate is the way to go. Otherwise, you are just burning time checking for screen updates when probably nobody will care. You can probably get by with 200ms between updates easily; if a user can't tolerate that, then they are not going to like the performance penalty for being remote either. Trapping calls to INT 10 is a good idea too, but not just to force an update - you still want those batched. If everytime somebody writes a character you have to send the whole screen it's going to be really chatty. (Of course you could detect what they are doing with INT 10 and then just send the deltas .. but now you are trading better performance for space again.)


-Mike
 
while i mull over all these suggestions, you can try the program as it is now if you want. i think it works fairly well. the only thing is certain program (MSD.EXE for example) require int 9h to actually be pressed to register a keystroke and it doesn't do that. most programs are fine though.

server for DOS:
http://rubbermallet.org/remoter-server.zip

client for windows:
http://rubbermallet.org/remoter-client.zip


the IP stack doesn't do DHCP yet, so before you run minitcp.com you'll need to run setip.exe which will let you modify the IP, subnet mask, and gateway IP it will use. run minitcp.com first, then remoter.com. right now it requires the packet driver to be on int 60h.

for the client, once the server is running on a DOS machine just run remoter.exe with the IP address of the server on the command line. oh, and just a note... it only supports 80x25 text mode so far, and it wont work with an MDA.




also, i know UDP isn't all that useful like TCP is, but if anybody wanted to know the interrupt calls for the MiniTCP TSR, right now they are:

Code:
MiniTCP interrupt calls
=======================

Call:
  AH = 00h (function: get local information)

Return:
  DS:SI = Pointer to 18-byte network info array:
          Offset 0: Local IP address (4 bytes)
          Offset 4: Subnet mask (4 bytes)
          Offset 8: Gateway IP address (4 bytes)
          Offset 12: Local MAC address (6 bytes)

=======================

Call:
  AH = 01h (function: disable and uninstall MiniTCP)

Return:
  Nothing.

======================

Call:
  AH = 02h (function: send UDP packet)
  BX = Destination port
  CX = Length of data in bytes (up to 1472 bytes per pkt)
  DS:SI = Pointer to data buffer
  DX:DI = Pointer to 4-byte destination IP address
  ES = Source port

Return:
  Nothing.

=======================

Call:
  AH = 03h (function: define callback pointer for UDP receive)
  BX:CX = Callback handler pointer (both NULL to disable)

Return:
  Nothing.

On callback:
  AX = Source port
  BX = Destination port
  CX = Length of Data in bytes
  DS:SI = Pointer to data buffer
  DX:DI = Pointer to 4-byte source IP address

=======================

the UDP callback is entered using the interrupt style, as in it pushes the flags, CS, and IP.. so when you return to need to use an iret. MiniTCP sits on int 61h.
 
Last edited:
Just tried this out on real hardware. Modifying the IP address info worked fine, loading the TCP stack worked fine and starting the remoter TSR worked fine. The problem occurred when I tried to run the remoter client. It connects and starts displaying info, but it does so very slowly; the remoter client takes forever to update and it causes extreme lag on my PS/2.
Also, remoter-client, while it does display info, it doesn't display what's currently on the screen. It just shows this:


So um, I think something is broken.
 
i see the updated screenshot you pasted in my IRC channel, which looks better. you said you are using a 10 mbit NIC plugged into a gigabit switch. unsure, but that could cause a lot of packet loss i think? i need to add auto-refreshing of the whole screen periodically to make up for problems like this. can you try plugging the PS/2 into the client machine directly with a crossover cable and give them static IPs, and try again? that's a lot of work, but it'd be interesting to see what happens.

here my 5150 and other older machines are plugged into a 100 mbit hub, and i lose a few packets but very little. the server/client works pretty nicely on it.
 
added this bit of code to trick programs that require int 9h to be hit before it recognizes a keypress... i set the flag before calling the "fake" int 9h and unset it after it returns.. it tricks the offending programs (like EDIT.COM/QBASIC and MSD)

Code:
int9_flag db 0
fake_int9:
  cmp cs:[int9_flag], 1
  je skip_int9
    db 0EAh
    old_int9_ptr dw 0
    old_int9_seg dw 0
  skip_int9:
    iret

woohoo, that was easy enough.
 
i took care of packet drops by having the server look for an acknowledgement packet for each line upate sent to the client. it waits intervals of 1 second to see if an ack was received, if not it resends and waits again until it gets one. works much better. same url.

http://rubbermallet.org/remoter-server.zip
http://rubbermallet.org/remoter-client.zip

i will try mike's suggestion of just having the client request a full screen every 200 ms and see how that works. should be much less convoluted on both ends. another nice feature of the client requesting the screen is that you could use different refresh rates depending on the speed of the server. with a 286 or faster you could get away with 100 ms refresh rates without too much of a performance drop.

also thinking about support for graphics modes.... it would suck on an 8088, but probably okay on a 386 for 486 for a lot of stuff.
 
Last edited:
wow, mike you were totally right about that. it's extremely fast now. i don't even notice a slow down in text mode on a 4.77 mhz box, at all using 200 ms refresh intervals. it can handle CGA graphics now too, btw. ;)

on a 12 MHz 286, it can play ms. pacman remotely at over 12 frames per second, without slowing the game down. i am starting to really love assembly.

remoter-cga.png


i'll upload the new stuff tomorrow. it's kind of awesome if i do say so myself, never expected any kind of remote control on an 8088 could be so quick.
 
Mike, your programming skills are quite something.

Really looking forward to trying this - just need an 8-bit NIC (and enough courage to leave a 27-year old PC running unattended... in my wooden workshop...!)
 
This new client seems to work better than the first one. I think there's still an issue of dropped packets, cause the PS/2 is currently paused at a blinking cursor just after doing a cls through remoter. Meh Gigabit.
 
Is this running on TCP? Offering a window size of one frame might sort it. Other options that might be worth exploring are back-pressure or sending ICMP source quench?
 
Mike. sry, my last post got ate, so I'll try and get in all the details. Worked exactally as it should have. Sorry I only
have a 'non vintage' pc to test it with ATM)

Client: Dell D620 (Intell pro/wireless 3945abg wireless card, windows XP, useing G mode over WPA-PSK)

Network: Wireless connect to 192.156.2.1 (Belkin n750 w/gigibit switch)
Wired connection to 3com 3C39036

Server: P233mmx ralink 2x rtl8139pci ATI mach64 pci video card

Other Network stack: Novell client 32 (IPX and TCP/IP nlm's loaded, NFS app in use) No interference noticed.

OS: Dos 7/win9x (mindows build) Tested and verified with both rtl8139 packet drivers (From Ralink site and smaller freedos one)

Configured minitcp.com with setip.exe. Program location: Root of C drive. Ip used: 192.168.2.17

Results: Did quick keyboard test in edit. Worked fine, needed to toggle caps-lock occosionally to return to lower case. ALT keys appear fine
backspace needs to be pressed repetitively to erase. All other functions appear normal, even after remoter client minimised and restored.

Video: Beyond current scope of legacy ms-dos remote control app. Blank screen on client. Selected window and typed.

odd issue with old MOD playing program (keys did not respond) ( Inertia player 1.22, assembly version) Did not crash eithor client or server. will
provide program as this issue occurs with another remote client/server app.

Booted into win9x (Mindows install). No key response, but did not crash eithor client or server. Upon exit, remoter resumed its duties.
Tested under XtreeGold v3.0. No prolbems.

Will test under other programs (Desqview, win3.11, various menu systems, bbs server,etc) and report results.

Again, mike, I salute you. Even for my overly complicated setup, Your tcp/ip stack and client are truely something to behold. I'll catch you on #wtfpwnt .

Sara.
 
Last edited:
Back
Top