Nop90 Posted May 11, 2019 Share Posted May 11, 2019 (edited) Developing comlynx support for Atari Lynx homebrew could seems a difficult task, but I discovered that it's easy. I think that the real reasons for so few homebrew with comlynx support are that there aren't so many examples of code available, and comlynx is not well suported on emulators, making difficoult to test the code. Sage's version of handy should support comlynx emulation over UDP, but using two PC is not the best solution while testing, and on my PCs I wasn't able to make it work (it crashes trying to connect). Since I'm developig a game that uses a 2 players mode, and I have to wait long time before to have an SD cart or an EEPROM cart programmer PI hat, I decided to modify Sage's version of the emulator to run two instances of the emulator core in the same application, than crosslinked the TX and RX comlynx interfaces of the two cores to have a comlynx test enviroment that can run on a single PC and without relying on network protocols. I made a quick and dirty tweak of the code, so don't expect an advanced version of handy. It's buggy, plays sound from both emulators core (so better disable sound) and can't emulate commercial games comlynx connection. But it works very well as a testing tools for developers. It's possible to swap betwen the two cores with CTRL+N or from the emulator menu. The active core outputs to the screen and gets the keyboard input, the other one runs silently in background. This means that it's not possible to use the two cores at the same time. Attached you can find a build for windows and a zip with the modified sources. When I have time I'll add the sources to my github and post the link here. To encourage other developers to start using the comlynx feature in their games (this can give more chances to win in the atarigamers game competition of this summer ) I'm sharing here two code examples of comlynx use. The first one is a simple piece of code from karri already posted on the forum (http://atariage.com/forums/topic/154232-cc65-link-error/?do=findComment&comment=1888745) that doesn't seem to have attracted the attention of the other developers. I slightly modified karri's code and attached it here (testcon.zip). In this example some sample text is sent over the comlynx connection at the A key press and printed on the screen of the other endpoint. The B key clears the buffer. The direction keys move an Hello world text, so that placing it in different position can help to identify the two running instances. The second example is a simple but complete game, an implementation of Tic Tac Toe using a 2 player mode over comlynx. The game uses a simple handshake protocol to start the connection that defines the two players ID (I took the handshake protocol from the comlynx code example of Championship Rally posted here http://atariage.com/forums/topic/283514-multiplayer-comlynx-from-championship-rally/) and a simple communication protocol derived from a more complex one I'm developing for another game of mine. Here are some screeshots of the game: The game code is mine (wrote it in an hour), this means two things: - It's public domain (do whatever you want with it, you can also develop from it a full game with muisic, gfx and and AI for 1P mode, but hurry up before I do it first) - It can contain bugs. I really hope to see a lot of new homebrew games with comlynx support in the next future. Happy coding Note: The code examples posted here aren't tested on real lynx yet. Please let me know if they work as expected. Handy_multi.zip handy-win32src-0.95-patched_multi.zip testcomlynx.zip tictactoe.zip Edited October 6, 2021 by Nop90 5 Quote Link to comment Share on other sites More sharing options...
Cyprian Posted May 12, 2019 Share Posted May 12, 2019 (edited) Great idea, congrats Will check it later Edited May 12, 2019 by Cyprian_K Quote Link to comment Share on other sites More sharing options...
42bs Posted May 12, 2019 Share Posted May 12, 2019 Checked it with T-Tris and SIMIS. Both seem to stuck in the rendezvous code. Do you echo the data sent also back? (I can't see where you changed the code, so I cannot judge.) Quote Link to comment Share on other sites More sharing options...
Nop90 Posted May 12, 2019 Author Share Posted May 12, 2019 The echo is already in the original code and didn't change it. the CC65 serial example from karri (that is the base from my code examples) use it to check the transmission, so I suppose it's ok. Let's wait that someone checks if the tic tac toe game works on two real lynx. About T-Tris and SIMIS, do they use the redeye protocol? Since all the commercial games that use it don't work on handy, this could be the reason. Quote Link to comment Share on other sites More sharing options...
42bs Posted May 12, 2019 Share Posted May 12, 2019 It's my own (BLL) protocol. I need to check a simple example. Quote Link to comment Share on other sites More sharing options...
sage Posted May 12, 2019 Share Posted May 12, 2019 About T-Tris and SIMIS, do they use the redeye protocol? Since all the commercial games that use it don't work on handy, this could be the reason. There is not really "one" red eye protocol. There are games which rely on the EPYX code. and other are not. Malibu for example uses something completely different. Quote Link to comment Share on other sites More sharing options...
Nop90 Posted May 30, 2019 Author Share Posted May 30, 2019 (edited) Wow, seems my tricky experiment can run Battlezone in 2P mode. Other games I tested (like checkered flag or warbirds) doesn't. EDIT: all the games from Hand Made Software with multiplayer support works Edited May 30, 2019 by Nop90 1 Quote Link to comment Share on other sites More sharing options...
Nop90 Posted May 31, 2019 Author Share Posted May 31, 2019 Tracing the exchange of data in warbirds and other games I saw that the two instances try to get in sync throwing a fixed sequence of data but with no success because the two transmissions overlaps.The first thing I did was to introduce a little delay on the start of the second instance. This way the first instance becames the manster sending the sequence, the other only listens. But the sync doesn't happen.So I had the enlightment of removing the 1024 steps every update introduced in the emulator to boost performance, and with only 1 step every update the two instances can syncronize.I think that, with low priority respect the lynx coding, I'll add the possibility to start 1 to 4 instances,and will add multiple drawing surfaces (one for every instance) and separate inputs, so to have a full multiplayer emulator.If someone wants to help, let me know. Quote Link to comment Share on other sites More sharing options...
Nop90 Posted June 27, 2019 Author Share Posted June 27, 2019 Now that I have several working Lynx and the cart programmer I solved the comlynx serial protocol problems. The most importat thing that discovered is that only ODD and EVEN parity always work. MARK and SPACE transmit only some of the values (have an idea of the reason but don't have time to investigate more now). Changing the parity option and with small fixes to the code, the tic tac toe example works. I also successfully used the protocol for one of my games in developement and I'm going to start coding the multiplayer mode for Nutmeg, my entry for the Lynx coding competition. Now I'm too busy, but in the week-end I'l post some updated code that can be used as reference for adding comlynx support to homebrews (I'm considering to add this sample code to the game template too). 2 Quote Link to comment Share on other sites More sharing options...
Nop90 Posted September 26, 2019 Author Share Posted September 26, 2019 As promised here is a polished and heavily tested version of the comlynx lib I use in 4Ttude and in ... something else tha will be released soon. You need to build a protocol over it, but I'm uploading right now the code of 4Ttude on my github where you can see the protocol I implemented. It can handle the data communicaton, music change and sync, an trigger Pause and Reset events on both sides when requested by one of the two consoles connected. The 4Ttude protocol is only for 2 lynxes, but can be easily extended to more consoles. It's an asymmetric daisy chain, where the first Lynx to start the connection act as game server and the other one is only a remote controller (other than drawing the screen with the game data sent by the server at every frame). I think the communication protocol is well coded, the rest of the game not the same, remember that I coded everything in a month while finishing Nutmeg in time for the competition deadline (the code needs a lot of polishing). net.c Quote Link to comment Share on other sites More sharing options...
42bs Posted September 26, 2019 Share Posted September 26, 2019 How can you tell which one is the first Lynx? Or do you just define, that each lynx should be started one after the other? Quote Link to comment Share on other sites More sharing options...
Nop90 Posted September 26, 2019 Author Share Posted September 26, 2019 The starting handshake is taken from the comlynx snippet posted by Songbird some time ago. To simplify things the connection needs to be started by a command of the player when the two lynx are wired (commercial Lynx games polls the comlynx connecton and switch to 2P mode as soon as the connection is phisically established, but IMHO it's a waste of CPU). If there is no comlynx cable the user can abort the connection pressing a key. I know I could check the comlynx state register, but I preferred to keep thing simple (I could improve it). When a Lynx try to connect it reads the buffer: if it reads a value (I use #1) it kwows to be number #1 in the chain (the slave) then sends a predefined ACK. if it doesn't receive anything it assumes to be #0 (the master), send a #1 value, then waits the ACK. The protocol is simple and can occour race conditions, but it's unlikely. 1 Quote Link to comment Share on other sites More sharing options...
42bs Posted September 26, 2019 Share Posted September 26, 2019 Yepp, as soon as the "human factor" is included, the RC is very unlikely. It would have been so much easier, if Mikey or Suzy would contain a serial number. (Which they surely have on the die and if only x/y position.) Quote Link to comment Share on other sites More sharing options...
+karri Posted September 26, 2019 Share Posted September 26, 2019 These cc65 serial commands use two 256 byte ring buffers for incoming and outgoing traffic. So all data is sent/received using interrupts. Unfortunately this code is very little tested by me. Just by glancing through it I already found out that the SerialStat is never cleared. So any error status remains forever ;---------------------------------------------------------------------------- ; Global variables ; .bss TxBuffer: .res 256 RxBuffer: .res 256 RxPtrIn: .res 1 RxPtrOut: .res 1 TxPtrIn: .res 1 TxPtrOut: .res 1 SerialStat: .res 1 The return value SER_ERR_OK means that the data fit in the 256 byte output buffer. If the buffer is full you will get SER_ERR_OVERFLOW and then you have to wait a while before trying a ser_put command again. The ser_get() and ser_put() interact only with the ring buffers. They never report any communication errors. There is a separate command ser_status(unsigned char *status) that will tell you if there were errors like RxParityErr|RxOverrun|RxFrameErr|RxBreak. If you want to detect a clash you should ask for the status. (That was the plan. Sorry about the bug that makes it not work.) The code will also detect a break signal that force all the receiving units to clear their ring buffers. Sending a break is a bit difficult. I believe the best way is to set the baud rate very low and send 0x00. (So to get a fresh start of a game you could first send a break. I would just pick the last 16 bits of the clock() when I receive the break as my ID. Then every Lynx just send its ID when the receive buffer is empty and last byte of the clock() is 0. Within a second or two you should know who is online. This recognition phase could go on for a while. When everyone is in someone press start game and then we go into a round robin communication method.) The receiver will also accept all characters even if the parity is wrong. If you really want to check the parity, read the status after you have got the message. After the bug mentioned at the top of the message is fixed. Sorry about the mess. The best way might be to post a fixed version as a stand alone asm file. It will automatically override the library version if you add it to the project. I try to do it later today. Quote Link to comment Share on other sites More sharing options...
LordKraken Posted September 26, 2019 Share Posted September 26, 2019 Thanks for sharing man! That is going to be very helpful Quote Link to comment Share on other sites More sharing options...
Nop90 Posted September 26, 2019 Author Share Posted September 26, 2019 1 hour ago, karri said: These cc65 serial commands use two 256 byte ring buffers for incoming and outgoing traffic Thank you. But I'd like to know also the hw buffer size. Common UART chips of the Lynx age (16550 for example) use to have only 16 bytes of FIFO buffer. Quote Link to comment Share on other sites More sharing options...
+karri Posted September 26, 2019 Share Posted September 26, 2019 22 minutes ago, Nop90 said: Thank you. But I'd like to know also the hw buffer size. Common UART chips of the Lynx age (16550 for example) use to have only 16 bytes of FIFO buffer. I highly doubt that they have anything more than a serial register for incoming bits plus a way to transfer the assembled byte to SERDAT. This implementation is a half-duplex thing. As long as you have something to send you just assume that every interrupt means that the transmit buffer is empty and you can feed in the next byte. When everything has been sent you assume that every interrupt is for incoming data. See the TxIntEnable and RxIntEnable flags. They are never active at the same time. IRQ: lda INTSET ; Poll all pending interrupts and #SERIAL_INTERRUPT bne @L0 clc rts @L0: bit TxDone bmi @tx_irq ; Transmit in progress ldx SERDAT lda SERCTL and #RxParityErr|RxOverrun|RxFrameErr|RxBreak beq @rx_irq tsb SerialStat ; Save error condition bit #RxBreak beq @noBreak stz TxPtrIn ; Break received - drop buffers stz TxPtrOut stz RxPtrIn stz RxPtrOut @noBreak: lda contrl ora #RxIntEnable|ResetErr sta SERCTL lda #$10 sta INTRST bra @IRQexit @rx_irq: lda contrl ora #RxIntEnable|ResetErr sta SERCTL txa ldx RxPtrIn sta RxBuffer,x txa inx @cont0: cpx RxPtrOut beq @1 stx RxPtrIn lda #SERIAL_INTERRUPT sta INTRST bra @IRQexit @1: sta RxPtrIn lda #$80 tsb SerialStat @tx_irq: ldx TxPtrOut ; Has all bytes been sent? cpx TxPtrIn beq @allSent lda TxBuffer,x ; Send next byte sta SERDAT inc TxPtrOut @exit1: lda contrl ora #TxIntEnable|ResetErr sta SERCTL lda #SERIAL_INTERRUPT sta INTRST bra @IRQexit @allSent: lda SERCTL ; All bytes sent bit #TxEmpty beq @exit1 bvs @exit1 stz TxDone lda contrl ora #RxIntEnable|ResetErr sta SERCTL lda #SERIAL_INTERRUPT sta INTRST @IRQexit: clc rts Quote Link to comment Share on other sites More sharing options...
LordKraken Posted September 29, 2019 Share Posted September 29, 2019 Hi Mop90 Just wondering because I didn't see anything in 4tude source, you don't do any sort of checksum when you receive the data? Haven't you experience loss or corrupted data? Quote Link to comment Share on other sites More sharing options...
Nop90 Posted September 29, 2019 Author Share Posted September 29, 2019 No, games work fine and made some test programs that send number sequences, never had corrupted data. Quote Link to comment Share on other sites More sharing options...
Nop90 Posted October 8, 2019 Author Share Posted October 8, 2019 There is a strange behaviour in the serial driver code. When two lynx are connected with a Comlynx cable, if you turn on only one of them the code runs slowly, about at half speed. Turning on the second Lynx everything goes fine, even if you turn off one of the two lynx. The only thing I do a startup is to load the serial driver and turn on the connection: ser_install(&lynx_comlynx); CLI(); ser_open(¶ms); Any hint on why this happens? Is there a solution to this? Quote Link to comment Share on other sites More sharing options...
42bs Posted October 8, 2019 Share Posted October 8, 2019 I guess the UART "sees" a BREAK due to the pull-up of the line. Quote Link to comment Share on other sites More sharing options...
Nop90 Posted October 8, 2019 Author Share Posted October 8, 2019 Guessing something like this me too. Quote Link to comment Share on other sites More sharing options...
+karri Posted October 8, 2019 Share Posted October 8, 2019 Receiving a break empties the ring buffers. There could be a bug here. Perhaps the pointers have to circle through 255 bytes after the break or something? Or perhaps the Lynx that is on believes that it has to send 255 characters to ComLynx? Quote Link to comment Share on other sites More sharing options...
SlidellMan Posted October 21, 2019 Share Posted October 21, 2019 You, sir, have done the homebrew community a favor. 1 Quote Link to comment Share on other sites More sharing options...
Nop90 Posted December 31, 2019 Author Share Posted December 31, 2019 On 10/8/2019 at 11:23 AM, karri said: Receiving a break empties the ring buffers. There could be a bug here. Perhaps the pointers have to circle through 255 bytes after the break or something? Or perhaps the Lynx that is on believes that it has to send 255 characters to ComLynx? @karri could you help me to check if there is a bug or not. I have to fix ol this problem before Carl starts the production of Biniax2 cart. The problem is not blocking, but if I can solve it I'm happier. Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.