i was thinking if anybody actually does some more work on this, it would help if i explained the protocol that is used. as mentioned before, it's UDP-based to keep the overhead minimal on 8088s. the protocol is extremely simple. the server listens on port UDP 9001. it sends data back to the client on port UDP 9002.
this is taken from the end of the server asm source. i added comments just now. this below is the packet format that the DOS machine running the server sends upon the client requesting a screen update.
Code:
pkt_vid_ptr dw 0 ;the offset inside the video memory segment where the 1400 data bytes in this packet begin at
pkt_cursor dw 0 ;tells client where cursor is on screen, column is first byte, row second. filled in by DX result from int 10h, AH=3 on update requests
pkt_vidmode db 3 ;tells client current video mode
pkt_port3d9 db 0 ;video card status port's value
pktbuf dw 1400 dup (0) ;actual data from video memory is here
below is the code that runs when the server receives an update request packet from the client, which fills in the packet data structure, including looping until all of the visible video memory data has been sent. there is a table elsewhere in the code that has the total visible video memory lengths for each BIOS video mode number, so it uses that to determine how much is sent.
Code:
do_update:
cld
mov ax, 40h ;BIOS data area
mov ds, ax
mov bh, [62h] ;get active video page
mov ah, 3
int 10h
mov cs:[pkt_cursor], dx
mov dx, 3D9h
in al, dx
mov cs:[pkt_port3d9], al
mov ah, 0Fh ;get video mode
int 10h
mov cs:[pkt_vidmode], al
xor ah, ah
mov di, ax
shl di, 1 ;table of visible video memory sizes is of 16-bit words, double video mode value for indexing into this table.
mov bx, offset mode_size
mov ax, cs:[bx+di]
mov cs:[vid_mem_top], ax
mov bx, offset mode_seg
mov ax, cs:[bx+di]
mov cs:[vid_mem_seg], ax
mov cs:[vid_mem_ptr], 0
update_loop:
mov ax, cs:[vid_mem_ptr] ;get current position of video memory pointer
cmp ax, cs:[vid_mem_top] ;compare it to the total size we need to send according to our current video mode
ja update_done ;if limit is passed, we're done sending this screen update
mov ax, cs:[vid_mem_seg] ;otherwise get segment of beginning of current mode's video memory (usually B800h)
mov ds, ax ;shove it into DS
mov ax, cs ;get our CS (because our packet data structure is in it)
mov es, ax ;and shove it into ES
mov si, cs:[vid_mem_ptr] ;set source index to current video memory offset pointer
mov cs:[pkt_vid_ptr], si ;set it in our outgoing data packet
mov di, offset pktbuf ;set destination index to data area of outgoing packet
mov cx, 700 ;1400 bytes, so 700 words
rep movsw ;copy it
mov cs:[vid_mem_ptr], si ;update our current offset pointer based on new source index
call send_pkt ;send it out!
jmp update_loop ;do it all again
update_done:
so, thats the format of the update packets the server sends to the client. as for the packets the client sends to the server, it's very simple. there are 4 different types of packets the server can send. they all contain only 2 bytes of data, except for the "new keypress" packet which is 3 bytes.
the first 2 bytes are a 16-bit word, possible values are: (these values are decimal, not hex)
1000 = "set IP" packet. it instructs the server to read the client's IP from the UDP header, save it, and only process future packets coming from it.
1001 = "clear IP" packet. the server nulls the previously saved client IP. the client sends it when it is closed.
1002 = "new keypress" packet. this packet has one more byte, which is simply the keyboard scancode of the key pressed. they are exactly the same as the scancodes the BIOS would read from the real keyboard controller on port 60h. in fact, there is assembly code in the server directly taken from the keyboard interrupt (int 9h) code in the generic XT BIOS available at phatcode.net. it's just modified to use the byte from the packet as if it were the keyboard controller, rather than reading it from port 60h.
1003 = "screen update/refresh request". the server immediately sends the data from visible video memory back to the client over several packets. (using the code shown above)
the set IP and clear IP stuff is not intended to be any sort of security or authentication. the only purpose of it is to keep the server from processing the incoming packet at all if it isn't from the client's IP. this is just to save CPU cycles on the server if some kind of random UDP packet gets received from somewhere. (just on the extremely off-chance that said random packet begins with a 16-bit value of 1002/1003. i guess this really is kind of pointless anyway, if the packet had a 1000 it would interpret it as "set IP" heh)
that's about it. feel free to make it more robust if you work on it, that certainly couldn't hurt. i came up with the whole thing pretty hastily, just eager to get something working properly.
one very useful/important thing that could be added is support for monochrome adapters. it currently only supports video memory for text modes beginning at segment B800h. the only other segment it knows of is A000h if the video mode is 13h. (MCGA 320x200, speaking of which it does not support transferring palette information yet so MCGA modes will almost certainly look screwed up)
the easy way of going about monochrome support would be to detect video type when the server is first started, and in the update_loop code always set the values of words
vid_mem_top to 4000 bytes, and
vid_mem_seg to B000h if it's a monochrome adapter.