######## ################## ###### ###### ##### ##### #### #### ## ##### #### #### #### #### #### ##### ##### ## ## #### ## ## ## ### ## #### ## ## ## ##### ######## ## ## ## ##### ## ## ## ## ## ##### ## ## ######## ## ## ## ### ## ## #### ## ## ##### #### #### #### #### ##### #### #### #### #### #### ###### ##### ## ###### ###### Volume 1, Issue #4 ################## October 5, 1992 ######## ----------------------------------------------------------------------------- Editor's Notes: by Craig Taylor (duck@pembvax1.pembroke.edu) My apologies about this issue being posted later than was mentioned in a preview post on comp.sys.cbm newsgroup. Due to some problems with coding, school and Murphy's law the issue had to be delayed until now. I have asked the system admin's at my site concerning a mail-server but they said they did not have enough man-power (go figure) to get somebody to run it. I will be implementing a mail-server system in my account in the near future for retrieval of programs and back-issues. I'll post descriptions of how to use it it the next issue of C= Hacking as well as on the newsgroup comp.sys.cbm when I finish writing it. In this issue of C= Hacking we also start on an ambitious task: Developing a game for both the C128 and C64 modes that includes all of the features found in commercial games. Take a look in the Learning ML Column for more information. Also, The article concerning the 1351 mouse has _again_ been delayed due to time constraints. Rest assured that it will be in the next issue of C= Hacking. If you are interested in helping write for C= Hacking please feel free to mail duck@pembvax1.pembroke.edu (or duck@handy.pembroke.edu). We're always looking for new authors on almost any subject, software or hardware. ================================================================================ Also note that this issue and prior ones are available via anonymous ftp from ccosun.caltech.edu under pub/rknop/HACKING.MAG. ================================================================================ NOTICE: Permission is granted to re-distribute this "net-magazine", in whole, freely for non-profit use. However, please contact individual authors for permission to publish or re-distribute articles seperately. *** AUTHORS LISTED BELOW RETAIN ALL RIGHTS TO THEIR ARTICLES *** ================================================================================ In this issue: Learning ML - Part 4 In the next issue we'll embark on a project of making a space invaders style game for the C=64/128 from scratch using custom characters, interrupt-driven music, animation, using the joystick, mouse or keyboard. The C64 and C128 versions will be developed con-currently, each program taking advantage of the machine's capabilities. This is the first in a series - written by Craig Taylor. The Demo Corner: FLI - more color to the screen All of us have heard complaints about the color constraints on C64. FLI picture can have all of the 16 colors in one character position. What then is this FLI and how it is done ? Written by Pasi 'Albert' Ojala. RS-232 Converter This article details plan for a User port TO RS232 connector using just ONE IC and 4 capacitors. The circuit is included, and suggestions on alternative chips and parts are examined. Written by Warren Tustin Introduction to the VIC-II This article examines the VIC-II chip in detail and provides an explanation of the various registers associated with the chip. Written by Pasi 'Albert' Ojala. LITTLE RED READER: MS-DOS file reader for the 128 and 1571/81 drives. This article presents a program that reads MS-DOS files and the root directory of MS-DOS disks. This program copies files from disk to disk so two disk drives are required to use it (or a "virtual" drive). This scheme imposes no limit on the maximum size of a file to be transferred. The user-interface code is written in BASIC and presents a full-screen file selection menu. The grunt-work code is written in assembly language and operates at maximum velosity. Complete, explained code lisitings are included. By Craig Bruce. ============================================================================= Learning Machine Language - Part 4 by Craig Taylor (duck@pembvax1.pembroke.edu) +---------------------------+ | Space Invasion - Part 1 | | | | Programming: Craig Taylor | | Graphics : Pasi Ojala | | Music/Sound: | | | +---------------------------+ I. Introduction ------------ In this and future Learning Machine Language's we will develop a game called Space Invasion. The game will be similair to Space Indvaders and will run on the Commodore 64 or the Commodore 128 in 80 columns. It will feature all the "features" and "parts" that are found in commercial games with interrupt- driven music, custom character definitions, 100% machine language, multi-level game play, and input from the keyboard, joystick or mouse. Note | I am looking for someone to help aid music composition that will -----+ be introduced in a later issue. Programming of the 6502 is helpful but not a requirement. Please email me at duck@pembvax1.pembroke.edu if you are interested. Many thanks to Pasi Ojala for his work with the graphics in this program. Also please note: This entire program has been assembled sucessfully with the Buddy-128 assembler for both the C=128 and C=64 version. Due to the length of the source files (over 1,500 lines) I'm not sure if Buddy-64 will handle it. Thus if you get errors during assembly, all I can say is: sorry. If this is the case then the next issue will handle dividing the program and data up into segments which can then each be loaded seperatly. II. Machine Notes ------------- The Commodore 64 and 128 programs for Space Invasion will differ slightly, mostly in the following areas: - custom character definition - memory initialization / setup - sound / music Because the actual game play and the changes nesscesary between the areas listed above, we will use the Buddy Assembler notation for conditional assembly to allow the development of only one file containing the source code. In addition to conditional assembly most of the routines will be written as one with jumps to subroutines containing the C64 or 128 direct code as the algorithims are usually the same for each. In addation there will be several source files and some miscellaneous include files for graphics and sound. For those of you who are or will be converting the assembly source over to a different assembler the conditional assembly directives .if (condition) will only be true if the condition is non-zero. Ie: if the symbol computer is defined as 128 then the following example illustrates it: computer = 128 .if computer-64 ; non-zero answer so therefore ; 128 code goes here .else ; 64 code goes here .ife ; end the .if condition. Also note that for much of the program we will _not_ be using the computer routines and instead be developing our own. In addition the program will show you how to use IRQ interrupts to simplify programming. We will be using them to play music in the background on three voices (sound effects will temporarily pre-empt the third voice from playing). Also animation of characters will be done via the IRQ. A little background on interrupts for those of you who are a bit hazy on what they are or have never seen them before (Also try taking a look at Rasters: What they are and how to use them in C= Hacking #3 - While this does not necessarily cover what we are going to be using interrupts for it does describe them quite well.) Basically the computer generates an interrupt every 1/60th a second from a timer on the computer (usually from the CIA chip or the screen for those of you who are curious). The computer will save all the registers, jump to a subroutine - perform the instructions there (usually updating time, scanning the keyboard etc...) and then recall all the registers and return to the user program. This is an interrupt. An IRQ interrupt describes an interrupt that we can allow to be "turned on" and "turned off" - ie: we can temporarily disable it if we have to. A NMI interrupt describes an interrupt which we can _not_ temporarily disable -- we will not be using NMI interrupts in this program. III. The Process ----------- Part of what this series of articles is focused at is the development of being able to analyze programming tasks and break them down into smaller workable problems. Once these problems or subroutines are completed your original problem is solved. Let's take this approach to Space Invasion: Problem Statement: Build a Space Invader program called Space Invasion. ----------------- Usually, given a problem you have to re-work the problem statement to encompass all of what you want. Let's try again: Problem Statement: Develop a Space Invader program called Space Invasion ----------------- utilizing the 64 or 128 screen with interrupt driven music / sound, and allowing input from the keyboard, joystick or mouse. Hmmm... The problem statement listed above is better but it has no real order; we have no clear idea of where to start and what we need to do. It does however tell us that we have the following sections: - 64 / 128 Screen Handling - Music / Sound - Input Handling - Game Driver (implied) Let's think a bit more about each of these sections and what each will involve: 128 / 64 Screen Handling: - Putting characters on screen. ------------------------ - Initializing the Screen / Registers. - Setting up the Custom Characters. - Handling any Animation. Music / Sound: - Setting up the Sound Chip Registers. ------------- - Playing a note read from Memory. - Executing a Sound Effect. Input Handling: - Device Selection (keyboard, mouse, joystick). -------------- - Keyboard Scanning. - Mouse Scanning. - Joystick Scanning. Game Driver: - Title Screen. ----------- - Initialization of Memory. - Level Setup. - Movement of Aliens. - Movement of Missles. - Movement of Player. - Collision Checking. - Collision Handling. - End of Level. - Score Updating. - End - Game handling. - High Score Update. Shrew! Long list 'eh? - Now you may have thought of some not listed above, and we may have possibly overlooked some crucial routines -- that's fine -- the above is just intended as a building block - a place to start coding from. If we think of these as subroutines we can build a skeleton outline of the program - yet we need some order in how we call them. Obviously we aren't going to move the player until we scan the input and that requires prior device selection etc... Hmm... Taking order into account we can re-state the problem as: Problem Statement: Develop a game similair to Space Invaders called Space ----------------- Invasion by initializing memory, the display device, setting up Custom Characters, setting up the Music Registers and displaying the title screen. From there, select the input device and after that setup the current level. Next, while playing music in the background and scanning the input device, move the aliens, missles and player checking for collisions and taking appropriate action as required (player dies, score increases etc or what-not). After each level display if the player is dead, or set-up for the next level and repeat. When the game has ended update the high score if necessary. Try saying that five times real fast! :-) But that problem statement is a whole lot better than the one we had at the beginning which simply said to develop a game. IV. Not All At One Time - What We're Doing This Time ------------------------------------------------ Now this program is too complex, (as seen by the problem statement above) to have in one article so this issue we'll concentrate on the basic main loop and the initialization of the Custom Characters and the title screen. Originally, I was planning on updating and listing the revised code in each issue. However, due to space limitations and the enormity of the program currently (1,500+ lines!!) it will be placed for anonymous ftp at ccosun.caltech.edu under the directory: pub/rknop/HACKING.MAG. V. The Main Loop ------------- What is a main loop? Basically it's where everything gets done. It calls other subroutines and keeps repeating until certain criteria are met - usually when the player requests to exit the game. However, inside you'll find inner loops for level play etc. Our main loop for this program will be: ------------------------------------------------------------------------------- ;; * Main Loop - This should be the last section in the source code. ; ; Main Loop ; main'loop = * jsr memory'setup ; Set-Up memory. jsr display'setup ; " " display. jsr char'setup ; " " custom character display. jsr music'setup ; " " music chip. jsr title'screen ; Display the title screen. jsr select'input ; Select Input Device. level'loop = * jsr play'music ; Start the music playing. jsr setup'level ; Setup the current level. - jsr alien'move ; Move aliens jsr missle'move ; " missles jsr player'move ; " player jsr check'collision ; Check for collisions ldx collision'flag ; Check collision flag. beq - dex ; Decrease .X by 1 so if X was 1 then beq player'die ; it's now 0 so we know player died. dex ; Decrease .X again so if X was 2 then beq alien'die'sound ; it's now 0 so we make alien death. jsr end'level ; If we got here - than end of level. jsr wait'next ; Wait for next keypress. jsr increase'level ; increase level #. sec ; And go back.... bcs level'loop alien'die'sound = * jsr make'alien'sound ; make alien sound. sec ; set carry bcs - ; and jump back. player'die jsr show'player'die ; Show it on-screen. lda lives ; Check # of lives. beq end'of'game ; If 0 the end-of-game. bne level'loop ; go back and re-start level. brk ; If we get here - than an error. end'of'game jsr end'game'screen ; Show end-of-game screen. jsr high'score'update ; Update the high score if need-be. jsr wait'next ; Wait for next-game selection. lda quit beq + jsr setup'level'1 ; Set-Up first level. sec bcs level'loop ; and start playing it. + jmp quit'game ; ; End of Main Loop ; ------------------------------------------------------------------------------- Some of the routines listed above we will later replace with actual code. It's much easier to see: inc level than to see a jsr increase'level and try to hunt down the code. I've included them in for now so that we can have a better idea of what is going on. In the file: invasion.src most of the statements above are commented out. Once we write the routines we'll un-comment them. For now, this serves to still remind us of the routines we need to write. Also there are a couple of programming tricks that I used in the main loop that probably need some clarifying. When handling the collisions the .X register is loaded with the result of the collision checking - $00 = no collisions, $01 = player died, $02 = alien died, $03 = end of level. Anytime a load to a register is done the flags are automatically set as if you had compared it to 0 - hence we can ldx the collision flag and immediately branch if equal to zero for no collisions. In addition to the load anytime the .X or .Y registers are incremented or decremented an implicit comparison to zero is performed. So if the .X register is 1 previously, we decrement it then it will be zero and our BEQ instruction will branch. If it's two then it will be one and we can continue like this. [NOTE: Technically it's not a real comparison to zero but calling it a comparison to zero servers our purpose here. The only significant difference would be in the effect of the carry flag which is insignificant in our code segment here.] Also in several locations are the two instructions: sec bcs [label] What these are doing are simply programming style - they could be substituted with JMP [label] - however they offer advantages over JMP. They take up the a larger amount of execution time, however they are relocatable so any mucking around / moving sections of code during debugging will be less likely to crash. Using other flags are also valid -- the use of which flag (I prefer the carry flag) is usually dependent on the programmer. Geos defines a similair macro called BRA (branch always) which is equivlent to: clv bvc [label] Note that the above is just programming style, held over from my programming in assembly days. The use of JMP is probably preferable in terms of execution and also in being able to branch more than 127 bytes away (the branch instructions only have a range of +128/-127). VI. Custom Characters ----------------- Since we're writing for each of the seperate modes (64 mode, 128 mode) we have to take a look at the differences between the VIC chip (64 mode) and the 8563 chip in the 128. The Vic-Chip ------------ The character sets in the VIC chip are defined as in the example below of the character code $00 "@" (all references are to screen "poke" codes - not print codes). .byt #%00111100 Try holding the page (or moving away from the .byt #%01100110 screen) and taking a look at the patterns the 1's .byt #%01101110 and 0's make. Each character is thus defined as .byt #%01101110 eight bytes who's bit patterns define it. Having a .byt #%01100000 total of 256 characters available makes it .byt #%01100010 neccesary to set aside a total of 2,048 bytes. .byt #%00111100 .byt #%00000000 Now, instead of designing all 256 character sets we'll just take advantage of the fact that the letters and numbers we want will already be there -- we'll just copy them from the ROM set into RAM, modify some of the other characters to reflect what we want and then tell the VIC chip to look at RAM to get the character set definitions. There are some problems with copying the 'system' characters, however. The Commodore 64 usually masks out the character set and typically it is only available to the VIC chip so that more space can be present for user programs and such. It also takes up the section of memory that the I/O block in $d000-$dfff does so that switching it in while interrupts are enabled is sure to result in a crash. We're also going to be doing a few things that you may not expect -- instead of copying all 256 characters - we're gonna _just_ copy the first 128. This will give us all of the normal characters as the last 128 are the reverse- video counterparts to the first 128 characters. We're doing this to conserve space and because we really don't need that many characters defined. Also location $01 contains what $d000-$dfff holds and we will have to modify bit 2 to switch the character ROM in. Hence, the following program code is used to copy the character set: ------------------------------------------------------------------------------- copy'chars = * ; must be run w/ interrupts disabled lda $01 ; register 1 = the control to switch in the char. ; rom. pha ; save it as we'll later need to sta' it back. and #%11111011 ; Bit 2 controls it - clear it to switch it in. sta $01 ; and make it so we can read it in. lda #>$3000 ; move chars to $3000 sta dest+1 lda #>$d800 ; from $d800 (start of char set) (lower-case) sta src+1 ldy #$00 ; lo-bytes of both src, dest = $00. sty src sty dest ldx #$10 ; copy 2k of data. - lda (src),y ; copy byte. sta (dest),y iny bne - ; continue until .Y = 0. inc src+1 ; increase source & dest by 256 inc dest+1 dex ; decrease .X count. bne - ; if non-zero then continue copying, else pla ; restore value of $01 sta $01 ; and put back. lda $d018 ; set VIC-chip address. and #$f1 ; to show char set. ora #$0c sta $d018 ; and finally tell VIC where the char set is... rts ; and return. ------------------------------------------------------------------------------- Note that we still need to change the actual characters we're gonna be using. That will be handled in the section after next: Changing the Characters as there is a great deal of similarity between the 128 and 64 implementations. The 8563 Chip ------------- The 8563 80-Column chip usually has 16k or 64k Ram attatched to the chip which the CPU does not have direct control over. It has to direct the 8563 to store and retrieve values to that memory. What makes control over that memory all the more difficult is the fact that the 8563 only has two lines or addresses that the CPU can control. The 8563 has a character set in much the same way the VIC chip does, save one exception - each character set can have up to 16 lines. Normally, the last eight lines are filled with $00 and are not shown. (Provisions can be made to have 8x16 characters but it is not needed for this game and thus, will not be shown - For more information See C= Hacking Issue #2: 8563: An In-Depth Look.) Thus the algorithim is similair to the C=64 but 8 zero-bytes will need to be written at the end of every eight bytes read. However, the 8563 does make things easier for us! - When the computer is first turned on a copy of the Character Set from ROM is copied into the 8563. The 8563 has no ROM Character Set associated with it and thus we are able to just simply modify the character set that is in the 8563 memory instead of copying it over. Because of this no routine will be presented to copy a character set into the 8563 memory, rather the discussion of copying individually defined characters will take place in the next section. The C=128 also makes life even easier for us at the end when we will exit the program, modifying the character set back to the "standard" Commodore character set by a routine in the KERNAL that will copy the characters back. We'll take a look at it closer when we write the exit routine. Also note that since the 8563 chip supports the 80 column screen we will be defining two characters that can be placed side by side for each alien so that the playing field will be similair to the C64 version. However, for the title screen we will be switching the 8563 into a "40 column" mode to make programming easier, in addition to expanding the character bit-mapped logo. Changing the Characters ----------------------- A lot of the times you'll find yourself re-using subroutines and code that you have previously created, gradually, over a period of time building up a library of routines. When thinking through the purpose and intent of this routine I thought about possibly building it so it would read a table and change the character set based on that table. The 64/128 character sets would be the same - this routine would automatically generate the eight additional bytes needed by the 8563 if need-be and it would call the appropriate storage routine - store to either the 8563 or the computer memory. Now you may be asking why would you want to store to the computer memory in 128 mode? Why not just have two seperate versions? - Yes - that could be possible but I'm implementing it this way because in the future I may see a need to define custom characters in 128 mode for the 40 column screen. This way I can just extract the routine, pop it into my program and I've got that section of the code complete. This is what I was thinking of for the data table: .byt 1 = 8563, 0 = comp. memory. .word address ; address base of char-set in computer or 8563 memory. .byte char # ; (to start) .byte # of chars to define .byte # of characters to define .byte data,data,....,data8 ; character data. .byte data,data,....,data8 ; character data. etc.... . . . Entrance into the routine will consist of .AY holding the location of the table. We will keep the address of the table and keep incrementing it as we go along in z-page locations. ------------------------------------------------------------------------------- install'char = * sta zp1 ; save .ay in table address sty zp1+1 ldy #$00 ; read computer mode. jsr get'byte sta mode jsr get'byte ; get address base. sta adr jsr get'byte sta adr+1 jsr get'byte ; get number of characters to copy. sta numb jsr get'byte ; get next character #. sta wrk ; save in temp. location. lda #$00 sta wrk+1 asl wrk ; shift left x3 times = *8 rol wrk+1 asl wrk rol wrk+1 asl wrk rol wrk+1 lda mode ; if for 8563 then multiply 1 more time. beq + asl wrk rol wrk+1 + lda adr ; add character address in. clc adc wrk sta wrk lda adr+1 adc wrk+1 sta wrk+1 ; address now calculated jsr setadrs ; set address in proper chip loop'install ldx #$08 ; copy 8 bytes. - jsr get'byte jsr writebyte ; write out byte. dex bne - lda mode ; if 128 then fill out 8 more $00 bytes. beq + lda #$00 ldx #$08 - jsr writebyte dex bne - + dec numb bne loop'install rts ------------------------------------------------------------------------------- What? We have thre subroutines : writebyte, setadrs, and get'byte that we haven't examined yet. These are going to be the routines that are dependant on the computer type. Also, writebyte will require that .XY not be disturbed; setadrs requires that .Y not be disturbed hence the following: ------------------------------------------------------------------------------- setadrs tya ; save .yx pha txa pha lda mode ; check computer type. beq + ; if C=64, then jump ahead. ldx #18 ; VDC register - current memory address hi lda wrk+1 ; get address hi jsr wr'vdc ldx #19 ; VDC register - current memory address lo lda wrk ; get address lo jsr wr'vdc + pla ; restore .XY tax pla tay rts ; and return. ------------------------------------------------------------------------------- Note that we really don't need a setadrs for the C=64 -- we can just index off (wrk) in the writebyte routine which follows: ------------------------------------------------------------------------------- writebyte sta temp ; save as we need it later. txa ; Save .XY pha tya pha lda mode ; now check computer type. beq + ; if c64 jump ahead lda temp ; recall temp. jsr wr'vram sec bcs ++ ; jump ahead + ldy #$00 ; C64 / y-index = $00 lda temp ; get value sta (wrk),y ; store inc wrk ; now increase address bne + inc wrk+1 + pla ; now return after recalling .XY tay pla tax rts ; and return. ------------------------------------------------------------------------------- Note that the following routine is fairly short but it is called numerous times within the routines that use data tables such as install'char, write'txt and write'col. ------------------------------------------------------------------------------- get'byte = * lda (zp1),y iny bne + ; if zero then increase zp1 hi inc zp1+1 + rts ------------------------------------------------------------------------------- Not bad 'eh? A quick note: The instructions: PLA, TAY, PLA, TAX, PHA, etc.. are routines that Push or Pull (pha,pla) the .A onto the stack. The TAY, TAX, TXA, TYA are instructions that transfer a register to another (ie: the TAY transfers the A register to .Y, TXA transfers .X to .A etc...) By using the combination of these with the stack we can save the registers and later re-call them so that they are the same when we entered the routine. The stack is usually a "mystery" item to new programmers of the 6502 series. Basically it's just like any other stacks in the real world - the last item thrown (I'm non-practicing perfectionist so I throw stuff.. ;-) ) or pushed on the stack will the first item removed or pulled from the stack. For example I've got a stack of books sitting near me : Mapping the Commodore 128 128 Internals and I'm holding Mapping the Commodore 64 in my hands. If I push (or toss) the book onto the stack (and hopefully hit the stack instead of the floor) I'll have the following stack: Mapping the Commodore 64 Mapping the Commodore 128 128 Internals and it should be easy to see that if I "pull" the next book off the stack that I'll get the Mapping the Commodore 64 book. The next book to be "pull"ed after that would be the Mapping the Commodore 128 book. This idea can be applied to the 6502 stack -- It will keep storing values (up to 256) when you "push" them on (via the PHA instruction) and will retrieve the last value stored when you "pull" them off (via the PLA instruction). Another PLA instruction would return the next value that had been stored. The Character Bitmaps --------------------- Pasi Ojala is to be credited with all the graphics and many thanks go out to him. The game logo is made up of 120 custom defined characters that will be printed in the following manner (on the 128 screen they will be centered). (in reverse video)... ABCDEFGH . . . [up to 40 characters] IJKLMNOP QRSTUVWX and everything will line up. So that it will look like a "mini-bitmap". We could have used bitmap mode and made a very nice looking title screen but that would have involved switching and allocating memory for the bitmap, etc . . . On both the 8563 and the VIC that involves a bit more work and so custom characters will be used for the title screen. The regular letter and numeric characters will be available so that we can display credits and game instructions below the logo. Now - in the program listing we could list them as binary #'s and that would make editing them very easy but we're gonna use their decimal representation in the program listing. The characters are defined similair to the logo except they are treated as single characters. In the 128 version due to the 80 column screen we are going to use two characters side by side to simulate one alien so that the playing field will be similair to the C64 version. In addation, during the main loop we will modify the character sets to support animation of the aliens. In the data listing there is a reference to "frames" - for each of the aliens there are 8 differant frames. Oh! - There will be more characters defined in the future. Right now I'm mainly interested in getting some base characters down so you can see how custom characters are implemented. When we start setting up different levels and such we'll add more characters then. Currently the custom characters are not used - only the characters for the logo. For those of you who are curious try installing the characters via install'char and taking a look at the aliens. VII. Title Screen ------------ The title screen is usually a lead-in to the actual game and it's aim is to tell the player how to play the game, any available options and p'haps present a nice graphic or two to "wow" the user into playing. In addation, the main musical theme can be introduced here to unify the game-playing. The discussion below does not take into account color but rest-assured we will be using varying colors in the title screen. The format for the color data will be almost identical to the title screen format except it will be structured via the following: .word address .byte num_of_chars to put color ($00= end of data) .byte color_value The routine (color'text) can be found in the source listings at the end of this article. Because of the similarity between it and write'text it is not discussed in this article. Title Screen BackGround ----------------------- The title screen I envisoned as a bordered screen (using the normal C= character set - ie: C= A,S,Z,X on the keyboard) with our bitmap in the middle and under-neath it a short description of the game and game-play instructions. Now this is my idea of the screen layout (rough drawing as we're not using the actual screen dimensions): +-------------------------------------------------------------+ | -LOGO ----------------------------------------------------- | | --------------x 3 lines------------------------------------ | | ----------------------------------------------------------- | | | | Space Invasion C64/128 | | Programming : Craig Taylor | | Graphics : Pasi Ojala | | Sound : ???????????? | | | | ----------------------------------------------------------- | | To Play: | | Use joystick in port 2, mouse in port 1 or keyboard: | | A - Left, Z - Right Space - Fire | | F1 - Restart | | | +-------------------------------------------------------------+ Title Screen Formatting ----------------------- We come into a problem here -- the screen is some 1000 characters on the C64, and 2000 characters for the C=128. It would be extremely wasteful to store that many characters in memory just to reproduce a title screen - and most of them consisting of spaces at that!! What we'll do is to just specify the address on screen, the # of characters and then list the characters. It will be similair to our custom character table driver above but will be different enough that a new routine is warrented. We will however use the two subroutines writebyte and setadrs that were developed in the previous routine. The data will look like the following: .word address .byte num_of_chars ($00= end of data) .ascii "text" .byte address .... etc.... and we'll enter with .AY containing the address of the table. So basically we come up with the following: ------------------------------------------------------------------------------- write'txt = * sta zp1 ; save .ay in table address sty zp1+1 ldy #$00 loop'w'text = * jsr get'byte ; set address. sta wrk jsr get'byte sta wrk+1 jsr get'byte ; get # of chars to write out. cmp #$00 beq + ; if zero then exit. tax jsr setadrs ; set address to wrk,wrk+1 - jsr get'byte jsr writebyte ; write out byte. dex bne - sec bcs loop'w'text ; this is an absolute jump to loop + rts ; return. ------------------------------------------------------------------------------- This is similair to our previous routine, and was in fact copied and modified from the previous routine. VIII. Debugging --------- Now, not all programs are perfect, and during the development of this portion of the game there were several errors found. Tracing an error in Machine/Assembly-Language is like trying to find a grammatical error in a language you don't know. ;-) But seriously, there are several ways to track down errors in your code. 1 - Try tracing it through by hand playing "What if I were the computer" and following what each register does. 2 - Are you switching the LoHi order of variables? Ie: is it lda #< or lda #>?? 3 - Set BRK points