WRITING OWN NETWORK GAMES To create your own network game with the use of my networking module, you need to write a set of game-specific subroutines described below, to define a few labels, and to include the file "B" from the supplied source of MULTI WORMS. (The source texts are in the format for MAE assembler.) All the label names used inside the networking module begins with the character "N" to avoid problems with duplicate labels - you can use any other names in your part. All your routines must be written in the style of common interrupt routines, ie. there may not be any long loops: The routine should allways quickly (<<1 frame) execute one operation, and exit (using variables to keep the info about overall situation). You can do all you want with the screen (ANTIC/GTIA programming), and with the first two sound channels, but setting up interrupts or anything around the serial I/O is not your business (that's the job of my networking module). You can enable or disable DLI, but VBI must be allways active (so be sure that you allways write $40 or $C0 to NMIEN at $D40E). During the game-play, you may not read any input data (such as joysticks, random numbers, collision registers, timers, keyboard etc.) directly from hardware: You should use only the data provided by the networking system. You must also remember that it'll all work with the Operating System banked out. Maybe that someone don't know how it looks like, so let me to say a few words about the situation with no O.S.: The RAM is free between $400 and $CFFF, and also between $D800 and $FFF9. Page 1 is still the stack, and also the hardware registers between $D000 and $D7FF are still in place, but since the OS-variables are gone, we can use a great part of pages 0, 2 & 3 for our own variables. But however, we can't overwrite the areas used by RESET routines - that's $00-$0F, $244, $33D-$33F, and $3ED-$3FF - otherwise the computer may lock-up after RESET. Because the game will be RESET-proof, we must also remember that RESET allways clears the areas $10-$7F, $200-$3EC, and $BC20-$BFFF. The last six bytes of memory are interrupt-vectors: $FFFA,B is address of NMI routine, and $FFFE,F is IRQ address (the $FFFC,D RESET-vector is not working, because the ROM is allways banked-in by the RESET button). The most important thing for your routines is that the so called Shadow registers are not working, so that you should write everything directly to the hardware (for example the background color to the GTIA register at $D01A, and not to the shadow register at $2C8). Because of the Terminal-booting process, the game-program (including all the pre-defined data like graphics, sound, fonts etc.) must be located somewhere in the normal system-memory ($580-$BC1F), while the upper RAM above $C000 is ideal for screen memory, buffers, game-maps and such. The position of program-data in memory is defined by a few important labels, as shown below (higher addresses at the top): Variables, screen memory etc. ----------------------------- L Master-specific game routines L ----------------------------- L Netw.module (Master-specific) L NBOOEND LB Networking module (main part) LB ----------------------------- LB Main part of game routines LB ----------------------------- LB Data (sound, graphics etc.) LB NDATA ----------------------------- B (Space for the Boot-loader) B NBOOTER ( = NDATA - $100 ) ----------------------------- Variables, screen memory etc. The letter "L" shows the area initially loaded from disk while starting the game on Master, and "B" is the area transferred to the Terminals. All important routines and pre-defined data for the game must be between the labels NDATA and NBOOEND (which is contained inside the networking module), otherwise it'll not be transferred to the terminals. (The "Master-specific" part contains the things necessary only for the Master computer - such as creating the game-map, or some of the communication routines - which needn't to be transferred.) Within the menu, your routines may not do any changes in the terminal booting area (marked with "B" above), otherwise the booting process will be impossible: There is a checksum over this area. But however, the game-part may be self modifying (for example some animation inside font data), because the booting checksum is updated while re-entering the menu. So, the memory allocation is the first thing you must define: The NDATA label (plus NBOOTER which should be $100 bytes lower). Also the areas for variables of the networking module must be located somewhere: NMZPG (2 bytes in the zero page), and NVRS (385 bytes anywhere). Another thing to define are the two Game-identification bytes called GIDBYT1 and 2. These bytes should be some unique code idetifying your game. You can use the game name's initials, random numbers, or the date of your birthday - simply anything you want. But don't use the stupid combinations like $0000, $FFFF, $8080 etc. - otherwise we'll have lots of games with the same code, resulting in problems while booting one network game over another at a meeting. You should also define the Macro called GBOOVRM containing 40 bytes of screen memory (a text in ATASCII screen codes) to be displayed on the terminal's screen while booting. There are a few variables of the networking system containing some informations useful for your routines (remember that you may only read them, not write): --- NTERM contains the number of terminal (0=Master). Beside of the identification, on which computer in the network your routine currently works, it's also useful for detecting local players (for example while re-drawing player's screens or making sound-effects connected to the game actions - look at MULTI WORMS): The numbers of local players are allways NTERM*2 and NTERM*2+1. --- NEXIST (16 player-bytes; 0=Exist) - here you'll find the information whether a certain player exists in the game (current or just finished) or not. This is useful while starting the game (putting players at their initial positions), during the game (to avoid random collisions with non-existent players), and also while printing game-results to the screen (to skip senseless lines). --- NRND (2 bytes) - If you need random numbers in your game, you should read it from this variable. The random numbers here are transferred via network, so that the same numbers occur in all the computers. (Note that the numbers are updated only once per game-step, ie. they are the same for all the player movements and other operations of the step. It works only during the game, ie. not in menu or initializations.) --- NALLNAM (128 bytes) contains the names of all players playing the current or just finished game - 8 bytes per player. It's mostly used for printing game-results to the screen, but it might be also useful during the game for some kinds of score presentation. --- NMYNAM (16 bytes, writing possible) - this buffer contains the local player's names (8 bytes per player); when the game is loaded, it's initialized to names like "PLAYER x" using the screen-code ATASCII. Your menu-routines should allow the user to edit the names before start of the game. --- NJOYTYP (2 bytes, writing possible) - the settings of controller type for the two local players: 1-2=Sticks, 3=Keyboard XL, 4=Keyboard XE, 0=Player will not play. Your menu-routines should allow the user to change these settings before start of the game. --- NACTIVE (Master only) is a bit-oriented list of logged-in terminals (highest bit is #0 (Master), lowest is terminal #7). It's useful for screen-presentation of network status on Master. --- NBOOFLG (Master only; 0=Not) - a flag indicating that some terminal is currently booting (for screen-presentation on Master). --- NIOBUF (128 bytes) - the main buffer for data transfer, which is important for the GINFOUT and GINFIN routines. The game-routines you should create are listed below - all the routines excepting GDLI must end with the RTS instruction. (It's a good idea to examine how the example game MULTI WORMS is done, before writing your own routines.) --- GRESINI - This is the very first initialization after loading the game, or after RESET, which is executed with black screen and no interrupts active. You should set all your menu-settings to the default (plus clear the game-results list), and initialize variables for a safe start of your interrupt routines (but NOT enable the interrupts youself). This is the only routine with no time-limit, so you should also execute all really long (over 5 seconds) initializations required for your game. Note that the networking-system variables like NTERM are not set while executing this routine. --- GWTSCR - This should set up a simple screen with a kind of "Please wait" message. It's used for example while a terminal executes its Log-in, or while the game-start preparation works. --- GMNUINI - This is executed while entering the menu-screen. You should initialize the menu (cursor-positions and such), set up your menu-screen, and redraw its contents. The A register contains zero when you should show the basic menu-screen (first entry or RESET), or 128 while the game-results page is required (re-entering menu after game). The list of game results should be based on NEXIST, NALLNAM, and your own score-variables. Mostly there are some central settings in a network game, so that you'll probably need to check NTERM, and display a little different menu on the Master. --- GMNUEXE - The main subroutine for the menu (executed inside the networking module's main menu-loop), allowing the user to edit menu-settings. When the A register is negative (>127), the game-start is allowed/required, so that on a terminal you should display a kind of "Get ready" message. At the end, this routine should set or clear the Carry-flag to indicate whether the user clicked on a "Start the game" or "Ready for the game" option, or not. This routine should be allways executed quickly, otherwise it'll slow down the terminal booting process. It's also nice to show a sort of network-status line on your menu-screen, ie. on terminal its number, and on Master also the list of logged-in terminals, and the flag of boot process (read from NTERM, NACTIVE, and NBOOFLG). --- GVBMNUI and GVBGAMI - The immediate-VBI routines for menu and game (should be short). This is useful for setting-up screen colors, running your own VBI-timers (during the game only for screen-presentation!), and such. --- GVBMNUD and GVBGAMD - The deferred-VBI routines for menu and game. It's useful for larger VBI-operations, such as keyboard-reading (in the menu), screen animation, or playing music. --- GDLI - The main routine for the DLI interrupt used for both menu and game. This is a real interrupt routine (no subroutine), so that you should store registers to the stack, and exit with RTI at the end. Also the detection whether menu- or game-screen is active is on you. Note that this routine is executed almost one scanline lower than a normal DLI (after one STA WSYNC instruction), so you should make your screen-settings immediately, or call the DLI one scanline higher from the Display List. You should also remember that this routine must be short (6 scanlines is the maximum - otherwise the serial data transfer will crash), and that more DLI's on a screen must be identified using VCOUNT (see above). --- GINITM - Initialize the game-map (this is executed only on the Master computer). You should prepare all the central game-data such as the map, player's and creatures' starting positions etc. - everything what will be tranferred inside the Info blocks. This routine may be relatively long (up to about 10 seconds). --- GIBLKS - This is another label you should define: GIBLKS = The number of so called Info-blocks used in your game. Each of these blocks contains 120 data-bytes, which may be used to transfer the game-map, central settings, and other such data from Master to all the terminals. You must transfer all the data affecting the game-situation, unless it's initialized the same way in all the computers, or pre-defined in the game-program. --- GINFOUT - Create Info-block (executed only on Master). In the A register you'll get a number of block between 0 and GIBLKS-1, and you should store the block's data at NIOBUF+2 (up to 120 bytes). --- GINFIN - Store Info-block (executed on terminals). This is reverse to the above: You should take the Info-block data from NIOBUF+2, and store into your game-data structures. --- GINITP - Initialize game-routines. This is the first initialization of the game itself, which is executed before setting up the game-data transfer, and so it may be relatively long (up to 5 seconds). Since all the Info-blocks are already transferred, this routine is useful for decoding game-maps or filling tables with pre-calculated values, and you should also initialize the variables for a safe start of your game-VBI routines. --- GINITS - Start the game. Now the NEXIST flags are already filled with correct player-existence flags, so you should initialize the variables like player's positions, scores etc., and then set up your game-screen. You can also execute some "count down" presentation before the actual game start as a part of this routine: Just make a loop with JSR call to a routine NWTJOY inside - this routine is a network-synchronized 0.1 sec. waiting. --- GPLREXE - Move one player. The X register is number of player (0-15), and A register contains the joystick of that player in the following format: b7 b6 b5 b4 b3 b2 b1 b0 Remove Fire Right Left Back Forwards The active state of all the bits is "1". Once the "Remove" bit is active, you should delete the player from the game - the corresponding computer was disconnected from network, and so the player no more exists (this routine will not be executed for that player anymore). If you need it, you may read random numbers from NRND. --- GOTHEXE - Execute game-operations other than player-moving. This routine is executed at the end of each game-step, allowing you to move monsters, set up new objects, count the time (0.1 sec. per game-step), and so on. (You can use NRND if necessary.) You should also check whether the game is over or not (all players dead, a time limit, or a similar condition), and set the Carry-flag when it's the end of game. --- GREDRAW - This is the routine for transfering game-situation to the screen, when it takes a longer time. (For example MULTI RACE is re-drawing both the 3-D screens in this routine.) It's executed only when there's enough time left for a longer operation, so this routine may be relatively long - the maximum is about 10 frames. But since this routine is executed without of a safe network-synchronization, it may not affect the game situation! --- GFINGAM - Finish the game. This is executed at the time of game-over, just before the ending 0.8 sec. pause before removing game-screen. This routine gives you a chance for the last update of scores, and for sorting the game-results before return to menu. You may also execute some final presentation, such as animation-end, total explosion of the game-field etc. - this may be a loop with the JSR NWTJOY call inside (synchronized 0.1 sec. waiting). You should also initialize variables for a safe start of menu-VBI routines if necessary, and clear all sounds you've used within the game. FINAL WORDS With the last routine, we've finished the game, and I think that it's the right time to finish also this article. Maybe you need further information, but since this areticle is long enough already, I must only say: You should read the source texts of MULTI WORMS for more info. And the last request: While using my networking module in your own games, it would be nice if you include a short notice about the source of this module (ie. "Networking code from BEWESOFT" or something like that). Thank you, and happy coding! Jiri Bernasek - BEWESOFT