• Goog Blog

  • by Goog

Hi, I'm Goog. Creator and founder of CommodoreServer.com, the Comet64 Internet Modem and the Portland Commodore Users Group in Portland, Oregon USA (PDXCUG.org). I am excited about all things Commodore, but my passion lies with the Commodore 64.

Aug
29

How To Build a Chat Program for CommodoreServer Using V-1541 and CSIP

The best way to show how to use the client SDK and some of the CSIP commands is by doing a project. This tutorial will show you how to make a very simple chat program.

Why Use V-1541?

V-1541 should be used for a variety of reasons:

  • V-1541 supplies a set of API calls to handle Internet communication
  • V-1541 provides a consistent way of making calls, so that any device can be used
  • V-1541 fixes the built-in RS-232 bugs which exist in every Commodore 64 (for RS-232 devices)
  • V-1541 can operate at various speeds, depending on the device (i.e. 2400 baud and 38K baud will both work - you do not need to write code to handle various RS-232 speeds - you just load the proper version of V-1541 and your calls to it will still work the same way)
  • When V-1541 is available on other hardware platforms, your code will still work - at least on the Internet communications level. Of course you will still have to write exceptions in your code to handle other platforms. If using BASIC, impact is negligible.
  • V-1541 has the added advantage of accessing disks and files if you need to LOAD or SAVE data on CommodoreServer, or use pre-made programs, fonts, graphics, sound or other data.

In an effort to keep things simpler, I am going to write this in C64List format - which is essentially BASIC without line numbers. Labels will be used instead when GOSUBs and GOTOs are needed. For more information about writing BASIC programs with C64List, visit http://www.commodoreserver.com/downloads.asp. I highly recommend using this tool for quick BASIC programming. At the end, I’ll include the C64List command to build it to a .PRG file so you can test it in VICE. Please note that in order to use VICE, you must configure it to connect to CommodoreServer. Please see the article on CommodoreServer.com (at the bottom of the page) called “Access the Internet Using V-1541 in VICE”.

Now, fire up your favorite notepad program and start typing in this simple tutorial. I use Notepad2, which has syntax highlighting for BASIC. Very nice - no line numbers and syntax highlighting! What more could a modern-day Commodore BASIC programmer wish for? Note I use this symbol ↩ to indicate that a line continues on the next line. You should just keep typing, keeping the next line of text on your same line.

Downloads

You can download the files here in their labelized and built formats:

chat.lbl

chat.prg

Make sure V-1541 is enabled

The first step is to make sure that V-1541 is loaded and ready for use so that our function calls will process without a problem.

V-1541 is usually installed in $8000 (32768) or $C000 (49152). Future versions may be installed elsewhere, so the best method is to check for its existence in V-1541’s installation address register at $295 (661) and $296 (662). This location includes the address of where V-1541 is installed in low-byte/high-byte form.

{:checkV1541}
  'Check for the correct version (2.0 or higher) of V-1541.

  'If not correct, LOAD it.
  L=PEEK(661):H=PEEK(662):V2=L+H*256

  IF H<>0 THEN {:v1541CheckVersion}
  PRINT "V-1541 IS NOT RUNNING. PLEASE LOAD AND ENABLE V-1541 2.2 ↩
    AND TRY AGAIN."

  END

V2 now holds the memory location of V-1541.

Check the Version of V-1541

The next step is optional, but allows you to determine if a specific version is installed. This is necessary for our purposes because we wish to use some of the advanced features of the later versions of V-1541.

V-1541 has a built-in function to get or set “settings”, including the version number. We can request the version by POKEing values into the “settings” register of V-1541 to “ask” for a value. The results will be returned in the .A and .X registers. The .A register is available to BASIC in location $30C (780) and the .X in location $30D (781). We tell V-1541 that we wish to read the version, which is setting #0. This is done by setting .X (781) to 0 (for setting #0), .Y (782) to 0 (for a GET). .A (780) does not have to be set since we are only reading. The function will return the result in .A and .X. Once the .X and .Y registers are set, we must make the SYS call to V-1541 to perform the GET. In this example, I multiply the revision number by 10 and the major version number by 100 to get a decimal value of the version number. We want to make sure we are using version 2.2 (or 220 after the math is done) or higher.

{:v1541CheckVersion}

 POKE 781,0:POKE 782,0:SYS V2+6:IF(PEEK(780)*100) ↩
  + (PEEK(781)*10) > 219 THEN {:v1541Installed}

{:v1541NotInstalled}
 'V-1541 not installed - tell the user.
 PRINT"V-1541 2.2 OR HIGHER IS REQUIRED, BUT IS NOT PRESENT. PLEASE ↩
  LOAD AND ENABLE V-1541 2.2 AND TRY AGAIN."
 END

{:v1541Installed}
 'All is good. Proceed with program.

Check Connection to CommodoreServer

Now that we know V-1541 is installed and running, it might be a good idea to double-check to make sure the computer is connected to CommodoreServer. I like to use the CSIP command WHOAMI so that I can find out if the user is logged-in.

I am including a simple function {:sendCommand} that will use the global variables TX$, RX$, CS%, and CS$. This function will send a command and wait for the response from CommodoreServer. All responses end with the EOT character - CHR$(4). The function will wait for the response and return the answer in RX$, the error number in CS%, and the error message in CS$. Error condition 0 is good (no error).

You might also notice that I included a commant to show player names with the PLAYERNAMES command.  When a person enters a room, they become a "player" and receive a player number.  A player is the term used for a person when in the room because rooms are also used for playing games.  The PLAYERNAMES command just tells CommodoreServer to display the nicknames of people (with GM - see below) instead of numbers.  Numbers are useful for keeping track of lists of people, especially when playing games, but we won't use them for this simple example.

{:checkLoginStatus}

 'See if this user is already logged-in
TX$="WHOAMI":GOSUB {:sendCommand}:IF CS%=0 THEN {:userLoggedIn}

{:userLogin}
'User not logged-in. ask user to login here.
PRINT"PLEASE LOG IN."
INPUT"USER:";U$
INPUT"PIN:";P$
TX$ = "LOGIN "+L$+","+P$:GOSUB{:sendCommand}:IF CS%<>0 THEN {:error}

{:userLoggedIn}
'User is logged-in.
REM INSERT POST-LOGIN CODE HERE

TX$="PLAYERNAMES 1":GOSUB{:sendCommand}

We’ll also take this opportunity to enter the room "c64". I am just hard-coding the room name here, but you might wish to present your user with an option to go into another room.

The ENTERROOM command tells CommodoreServer to put the currently-logged-in user into the chat room (room C64 in our example). You’ll see a couple of commas there - there is a missing parameter for password-protected rooms. Since there is no password on the C64 room, none is supplied. The last parameter is your application’s name and version, so people can see what program you are using.

{:enterRoom}
 TX$="ENTERROOM C64,,MYCHATAPP v1.0":GOSUB {:sendCommand} ↩
  :IF CS%<>0 THEN {:error}
 PRINT RX$

Main Loop

OK, so now the user is logged-in, has entered the room, and is ready to chat. Chat consists of a) looking for messages by others and b) sending the user’s chat message to everyone else in the room.

Ideally, it would be nice to process the message checking in the background and be able to allow the user to input text during the cycle. This is an advanced topic and is not covered in this tutorial. I’ll just use a simple GET statement. When the user types a key, it will pause the message checking to allow input. When the user presses RETURN from the input routine, the message will be sent and incoming messages will continue to be processed.

Here we use the CSIP command GM (or GETMSG). This asks CommodoreServer for the next oldest message in the public chat channel since login (channel 1 is always the public chat channel in every room).

Then we print the response if there was no error. A ?500 - NO MESSAGE error will occur if there is no message waiting so we don’t want to print those every time.

Finally, we look for user input. If a character is in the keyboard buffer, we go to a routine to accept more user input (and thus pausing the GMs). After the input is accepted, post it to the chat channel with SAY.

{:mainLoop}

 TX$="GM 1":GOSUB {:sendCommand}:IF CS%=0 THEN GOSUB ↩
  {:printMessage}

 GET I$:IF I$<>"" THEN PRINT I$;:GOSUB {:getUserMessage}:TX$="SAY "+I$↩
  :GOSUB {:sendCommand}:I$=""

 GOTO {:mainLoop}

{:getUserMessage}
GET A$:IF A$="" THEN {:getUserMessage}
IF A$=CHR$(13) THEN PRINT:RETURN
PRINTA$;:I$=I$+A$:GOTO {:getUserMessage}

{:printMessage}
PRINT RX$:RETURN

Sending Commands / Receiving Reply

Above, you've seen the frequent use of {:sendCommand} - This is a very simplified routine for sending a command to CommodoreServer and waiting for a response.

SYSV2+36 flushes the incoming buffer to clear out any remaining garbage. Probably not necessary, but I put it in there to keep the response synched up with the command we’re sending.

SYSV2+12,TX$ will transmit data in TX$ to CommodoreServer.

SYSV2+15,RX$ will receive data from CommodoreServer into RX$. This function will also timeout after the default timeout setting if no data is received in this call. If it times out, we make up our own error message.

{:sendCommand}

 'Default to OK. If errors occur, we will change the response.
 CS%=0:CS$="":RX$="":SYS V2+36:SYS V2+12,TX$

{:scBuildResponse}
 SYS V2+15,RX$

 'Check for a timeout condition.
 IF LEN(RX$)=0 THEN CS%=500:CS$="TIMEOUT"

 'If there is an error, report it
 IF LEFT$(RX$,1)="?" THEN CS%=500:CS$=MID$(RX$,8)

 RETURN

{:error}
 PRINT"AN ERROR OCCURRED. WE NEED BETTER ERROR HANDLING."
 END

Going a Step Further

There are a few other CSIP commands that might be useful when dealing with chat.  I'll list them here, but it is up to you to see what you can do with them.

LOGIN NAME,PIN
Logs in the user with their PIN

LOGOUT
Logs the user out of the system

EXITROOM
Removes the user from the room but remains logged-in.

WHOIS PLAYER #
Requests the name of a specific player #

ROOMS [# of LINES]
Displays a list of rooms

PEOPLE [# of LINES]
Displays a list of people in the room.

MSG NICK, MESSAGE
Sends a private message to the person with the nickname

Building Your Program

Save your program as "chat.lbl" - we use the .lbl extension to remind us that it is written in C64List's "labelized" format.

Now just build your program with C64List at the command prompt. NOTE: -ovr will overwrite chat.prg file if it exists.

c64list.exe chat.lbl -prg:chat.prg -crunch -ovr

Now get in that chat room and tell me about your programming experience. I’ll be there, waiting for you.

Goog

Leave a Comment

You must be signed-in to post comments.

Responses

HowlinAl 8/31/2013

This is great stuff.

kentsu 9/7/2013

Nice work, Goog! Very well explained. Can't wait to see what some people do with this start that you have provided.

Pinacolada 9/14/2013

This is a great blog post, I didn't know it was here!