magazines:chacking3
no way to compare when less than two revisions
Differences
This shows you the differences between two versions of the page.
— | magazines:chacking3 [2015-04-17 04:34] (current) – created - external edit 127.0.0.1 | ||
---|---|---|---|
Line 1: | Line 1: | ||
+ | < | ||
+ | ######## | ||
+ | ################## | ||
+ | ###### | ||
+ | ##### | ||
+ | ##### #### #### ## ##### #### | ||
+ | ##### ## ## #### ## ## | ||
+ | ##### | ||
+ | ##### ## ## ######## | ||
+ | ##### #### #### #### #### ##### #### | ||
+ | ##### ## | ||
+ | ###### | ||
+ | ################## | ||
+ | ######## | ||
+ | ============================================================================= | ||
+ | Editor' | ||
+ | by Craig Taylor (duck@pembvax1.pembroke.edu) | ||
+ | |||
+ | Here's over 3000 lines of hacking articles & such... Sorry about the length | ||
+ | as some of the articles ran a bit overboard. | ||
+ | the KERNAL routines, an examination of Rasters, and a package of burst routines | ||
+ | for use in your own programs. If you've got any ideas for articles etc that | ||
+ | you'd like to see or want to hear more on a certain topic feel free to email | ||
+ | me. | ||
+ | |||
+ | I'm pleased to introduce the Demo Corner where each month we'll report | ||
+ | how to achieve some of the graphic and sound effects that are present in | ||
+ | many demos that are wondered about. | ||
+ | |||
+ | Note: The article concerning programming and usage of the 1351 mouse has | ||
+ | been delayed until the next issue due to time and length constraints. | ||
+ | |||
+ | This file is available via anonymous ftp at tybalt.caltech.edu under | ||
+ | pub/ | ||
+ | |||
+ | **************** WARNINGS, UPDATES, BUG REPORTS, ETC... ********************** | ||
+ | |||
+ | OOPS - In the last issue of C= Hacking in Mark Lawrence' | ||
+ | inadvertantly got chopped off. The following code should be fixed between the | ||
+ | comments that are listed: | ||
+ | |||
+ | [. | ||
+ | . | ||
+ | | ||
+ | { Make EXTENSION a string representation of COUNT, to be added to the | ||
+ | OutFileName to make things a tad easier} | ||
+ | |||
+ | OutFileName := Concat(NewFile,' | ||
+ | | ||
+ | |||
+ | { Create filename based on which part we're up to } | ||
+ | [. | ||
+ | . | ||
+ | .] | ||
+ | |||
+ | ============================================================================= | ||
+ | Note: Permission is granted to re-distribute this " | ||
+ | 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 3 | ||
+ | |||
+ | In this edition we take a look at reading and writing commands to the disk | ||
+ | drive, including reading the disk directory and error channel. This article | ||
+ | parallels the discussion of the C=128 and C=64 KERNAL jump tables of available | ||
+ | routines. Written by Craig Taylor. | ||
+ | |||
+ | The Demo Corner: Missing Cycles | ||
+ | |||
+ | Everybody knows that there are 63 cycles available to the C64 processor | ||
+ | on each scan line, except for one which only provides 23 cycles. But what | ||
+ | happens when we add sprites and why ? Written by Pasi ' | ||
+ | |||
+ | KERNAL 64/128 | ||
+ | |||
+ | The C=128 and C=64 jump table points to many valuable system routines is | ||
+ | discussed and examined in detail. Written by Craig Taylor. | ||
+ | |||
+ | 64K VDC RAM and an alternate GEOS128 Background Screen | ||
+ | |||
+ | Standard GEOS only uses the first 16K of your VDC screen. | ||
+ | of VDC RAM, and want to write an 80-column only application, | ||
+ | of the additional VDC RAM to use as a replacement for the standard GEOS | ||
+ | background screen. | ||
+ | application FrontRAM to use! Written by Robert Knop. | ||
+ | |||
+ | GeoPaint File Format | ||
+ | |||
+ | Written by Bruce Vrieling, this article provides an in depth description of | ||
+ | exactly how geoPaint stores its graphic images on disk. It examines the | ||
+ | concept of VLIR files, how graphics data is laid out on screen (from both | ||
+ | geoPaint and the VIC's perspective), | ||
+ | techniques. | ||
+ | |||
+ | Rasters - What They Are and How to Use Them | ||
+ | | ||
+ | Written by Bruce Vrieling, this article provides an introduction to creating | ||
+ | special on-screen effects using the technique of raster interrupts. The | ||
+ | basics are examined, including what they are, and how to program them. This | ||
+ | article should provide a good starting point for someone wanting to get | ||
+ | their feet wet in raster programming. | ||
+ | |||
+ | Bursting Your 128: The Fastload Burst Command | ||
+ | |||
+ | Written by Craig Bruce this article covers the Fastload burst command of the | ||
+ | 1571 and 1581 disk drives. | ||
+ | discussed and a package for using the Fastload command to read regular | ||
+ | sequential files at binary program loading speeds is presented. | ||
+ | the package, a file word counting utility is implemented and the " | ||
+ | code is included. | ||
+ | |||
+ | ============================================================================ | ||
+ | </ | ||
+ | ====== Learning ML - Part 3 ====== | ||
+ | < | ||
+ | by Craig Taylor (duck@pembvax1.pembroke.edu) | ||
+ | |||
+ | Last time we used a routine at $FFD2 which would print out the character code | ||
+ | contained within the accumalator. | ||
+ | out regardless of VIC-20, C=64, C=128 and even PET because Commodore decided | ||
+ | to set up some locations in high memory that would perform routines that are | ||
+ | commonly needed. | ||
+ | |||
+ | Take a look now at the KERNAL 64/128 article and glance over some of the | ||
+ | routines and their function / purpose. This article is meant to be a companion | ||
+ | to that article so you may want to flip back and forth as the discussion | ||
+ | of the program listed below is discussed. | ||
+ | |||
+ | Note that I've borrowed Craig Bruce' | ||
+ | extract the source that follows enter the following command on a Unix system: | ||
+ | |||
+ | grep ' | ||
+ | |||
+ | .@001! ; | ||
+ | .@002! ; Set up computer type for computer-dependant code / | ||
+ | .@003! ; Only used in displaying # routine / start of assembly setting. | ||
+ | .@004! ; BUDDY format. | ||
+ | .@005! ; | ||
+ | .@006! computer = 128 ; Define as either 64 or 128. | ||
+ | |||
+ | For both c64 and c128 users the following code works. | ||
+ | conditional assembly which means it will work on either computer assuming that | ||
+ | the computer is equal to either 128 or 64. | ||
+ | |||
+ | .@007! | ||
+ | .@008! .if computer-64 | ||
+ | .@009! | ||
+ | .@010! | ||
+ | .@011! .else ;** else if _is_ c64, then | ||
+ | .@012! | ||
+ | .@013! .ife ;** end of computer-dependant code. | ||
+ | |||
+ | Because of this (the source is in BUDDY format) the C64 and C128 are set to | ||
+ | assemble at different memory locations. On the C64, $c000 is 49152. On the C128 | ||
+ | it is at 4864. Note for the C128 it is necessary to do a BANK15 before executing | ||
+ | the code. | ||
+ | | ||
+ | .@014! | ||
+ | |||
+ | This tells the assembler to actually put the code into memory. | ||
+ | |||
+ | .@015! | ||
+ | .@016! ;; | ||
+ | .@017! ;; KERNAL EQUATES | ||
+ | .@018! ;; | ||
+ | .@019! | ||
+ | .@020! setnam = $ffbd | ||
+ | .@021! setlfs = $ffba | ||
+ | .@022! open = $ffc0 | ||
+ | .@023! close = $ffc3 | ||
+ | .@024! chkin = $ffc6 | ||
+ | .@025! chrin = $ffcf | ||
+ | .@026! bsout = $ffd2 | ||
+ | .@027! clrch = $ffcc | ||
+ | .@028! | ||
+ | |||
+ | These are the KERNAL routines we will actually be using. Their actual | ||
+ | use will be documented when we come across them within the code. | ||
+ | |||
+ | .@029! ;; | ||
+ | .@030! | ||
+ | .@031! temp = 253 | ||
+ | .@032! charret = $0d | ||
+ | .@033! space = $20 | ||
+ | .@034! | ||
+ | |||
+ | Temp is set up to just be a temporary location in zero-page. Location 253 on | ||
+ | both the C64 and C128 is unused. | ||
+ | character and is the equivlent of a chr$(13). Space stands for the code for a | ||
+ | space (a chr$(32)) | ||
+ | |||
+ | .@035! ;; | ||
+ | .@036! | ||
+ | .@037! start = * | ||
+ | .@038! | ||
+ | .@039! | ||
+ | .@040! | ||
+ | .@041! | ||
+ | |||
+ | You'll see code like this a lot -- Basically we're building what is known as a | ||
+ | jump table. That way if we add more code to the directory or error routine we | ||
+ | don't have to worry about our SYS call's changing. To read the directory just | ||
+ | SYS base, to read the error channel just SYS base+3 (where BASE is 49152 on the | ||
+ | C64, 4864 on the 128)... | ||
+ | |||
+ | Also the JSR JMP combination may seem a little strange but what we are doing | ||
+ | is treating the directory routine as a subroutine and then JUMPING to the | ||
+ | error routine. Once we do that the RTS in read' | ||
+ | |||
+ | .@042! ;; | ||
+ | .@043! | ||
+ | .@044! read' | ||
+ | .@045! | ||
+ | .@046! ; Opens and reads directory as a basic program. | ||
+ | .@047! ;== | ||
+ | .@048! ; Basic programs are read in as follows: | ||
+ | .@049! ; [Ptr to Next Line]:2 [Line #]:2 [Text]:.... [$00 byte] | ||
+ | .@050! ; ^^^^^^^^^^^REPEATS^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ||
+ | .@051! ; The end of a program is signifed by the $00 byte signifying end of text | ||
+ | .@052! ; and the ptr's also being = $00. | ||
+ | .@053! ;== | ||
+ | |||
+ | There are several ways to read the directory in machine language. | ||
+ | doing here is taking advantage of the drive' | ||
+ | directory as a basic program -*except*- we aren't loading it per se. We' | ||
+ | gonna grab each byte as it comes from the drive and interpret it ourselves | ||
+ | instead of putting it in memory as would normally be done. | ||
+ | |||
+ | Basic programs are stored as the following: A 2 byte pointer, a 2 byte | ||
+ | line #, the basic text, and a null terminator and then starting over | ||
+ | from the 2 byte pointer. | ||
+ | of blocks the file takes up and the TEXT is the program name and file type. We | ||
+ | know when we're finished on the line by checking for a $00 byte. | ||
+ | |||
+ | .@054! | ||
+ | .@055! | ||
+ | .@056! | ||
+ | .@057! | ||
+ | .@058! | ||
+ | .@059! | ||
+ | |||
+ | Okay, first we need to simulate opening the directory as a program file. | ||
+ | SETNAM sets up the filename for the open command. | ||
+ | basic syntax of open file#, | ||
+ | |||
+ | .@060! | ||
+ | .@061! | ||
+ | .@062! | ||
+ | .@063! | ||
+ | |||
+ | Here we specify the device #, file #, channel # in preperation for the open. | ||
+ | |||
+ | .@064! | ||
+ | |||
+ | Open up the file. This is the routine that does the real work. SETNAM and | ||
+ | SETLFS were preparatory routines for this. | ||
+ | |||
+ | .@065! | ||
+ | .@066! | ||
+ | .@067! | ||
+ | .@068! | ||
+ | .@069! | ||
+ | |||
+ | Now we need to specify the input file # and tell the computer that all further | ||
+ | chrin' | ||
+ | keyboard unless we had this here). | ||
+ | |||
+ | .@070! | ||
+ | .@071! | ||
+ | |||
+ | Skip the starting address -- When reading the directory it is not relevant | ||
+ | so read the bytes and discard them. | ||
+ | |||
+ | .@072! | ||
+ | |||
+ | Now we skip the pointer for the next line. This is only used when loading | ||
+ | basic programs to re-link the lines. When listing the directory they are not | ||
+ | needed. | ||
+ | |||
+ | .@073! | ||
+ | |||
+ | This is still part of the routine that skips the pointer to the next line, yet | ||
+ | it has a label used below that allows us to check for end of file more easily. | ||
+ | |||
+ | .@074! | ||
+ | .@075! | ||
+ | .@076! | ||
+ | |||
+ | Here we get the line # as the next 2 bytes in the file. | ||
+ | |||
+ | .@077! | ||
+ | .@078! .if computer-64 | ||
+ | |||
+ | Unfortunately C= did not provide a nice routine in the KERNAL to display | ||
+ | numeric values - however - by exploring inside the operating system a way to | ||
+ | display numbers is there. | ||
+ | does just rest assured it will print out the line # correctly. | ||
+ | | ||
+ | .@079! | ||
+ | .@080! | ||
+ | .@081! | ||
+ | .@082! | ||
+ | .@083! | ||
+ | .@084! | ||
+ | .@085! | ||
+ | .@086! | ||
+ | .@087! | ||
+ | .@088! | ||
+ | .@089! | ||
+ | .@090! | ||
+ | |||
+ | This is the C128 version which uses some of the MONITOR routines to display | ||
+ | the numeric block size. | ||
+ | |||
+ | .@091! .else ; * else if c64 | ||
+ | .@092! | ||
+ | .@093! | ||
+ | .@094! .ife ; * end of computer dependant code. | ||
+ | |||
+ | This is the C64 code to display a numeric value (notice how much simplified it | ||
+ | is over the C128)... | ||
+ | |||
+ | .@095! | ||
+ | .@096! | ||
+ | .@097! | ||
+ | |||
+ | Let's print a space between the filename and the block size. | ||
+ | |||
+ | .@098! | ||
+ | .@099! | ||
+ | .@100! | ||
+ | .@101! | ||
+ | .@102! | ||
+ | .@103! | ||
+ | |||
+ | Now we start getting a character (line #98), if zero we branch out of the loop | ||
+ | (line #100), else we display the character (#101), and jump back (#102-03). | ||
+ | |||
+ | .@104! | ||
+ | .@105! | ||
+ | |||
+ | Ah, we got to a null byte so that's the end of this line - display a car/ret. | ||
+ | |||
+ | .@106! | ||
+ | .@107! | ||
+ | .@108! | ||
+ | |||
+ | This is where we branch back -- we are checking here for 2 null bytes on | ||
+ | input. | ||
+ | it's not the end of the directory so we jump back to discard the second byte at | ||
+ | line #73. | ||
+ | |||
+ | .@109! | ||
+ | .@110! | ||
+ | |||
+ | This is a continuation of the checking above. This time we're getting the | ||
+ | 2nd byte and checking for 0. If it's not we jump back to get and display the | ||
+ | line # etc. If it is 0 then that means we had $0000 for the next pointer which | ||
+ | means that it's the end of the directory. | ||
+ | |||
+ | .@111! | ||
+ | .@112! | ||
+ | .@113! | ||
+ | .@114! | ||
+ | .@115! | ||
+ | .@116! | ||
+ | .@117! | ||
+ | .@118! | ||
+ | .@119! | ||
+ | |||
+ | We then close the file by specifying the file # and calling close. We then | ||
+ | tell the computer to reset all the default input / output devices by calling | ||
+ | clrch (remember we changed the default input channel??). And then we can return | ||
+ | to where this routine was called from. | ||
+ | |||
+ | .@120! ; FILENAME string | ||
+ | .@121! dir .asc " | ||
+ | |||
+ | This is the string that is pointed to by the SETNAM call. Note that a search | ||
+ | pattern could be set by | ||
+ | line# | ||
+ | and by changing the length set in .A in the call in line #56. | ||
+ | |||
+ | .@122! | ||
+ | .@123! ;; | ||
+ | .@124! | ||
+ | .@125! read' | ||
+ | .@126! | ||
+ | .@127! ; This routine simply grabs bytes from a channel 15 it opens up until | ||
+ | .@128! ; a car/ret byte is found. Then it closes and returns. | ||
+ | .@129! | ||
+ | |||
+ | Reading the error channel is much much more simpler than reading the | ||
+ | directory. | ||
+ | repeatadly get bytes until a car/ret is found. | ||
+ | |||
+ | .@130! rderr lda #$00 ; | ||
+ | .@131! | ||
+ | |||
+ | Setup so we don't specify a name (length = 0). | ||
+ | |||
+ | .@132! | ||
+ | .@133! | ||
+ | .@134! | ||
+ | .@135! | ||
+ | |||
+ | Do the equivlent of open 15,8,15. | ||
+ | |||
+ | .@136! | ||
+ | |||
+ | Open it. | ||
+ | |||
+ | .@137! | ||
+ | .@138! | ||
+ | .@139! | ||
+ | |||
+ | Now set up file # 15 as input so we can start getting, displaying etc until | ||
+ | a car/ret is found. | ||
+ | |||
+ | .@140! | ||
+ | .@141! | ||
+ | .@142! | ||
+ | .@143! | ||
+ | .@144! | ||
+ | |||
+ | Read in and display the characters from the error channel until a char/ret is | ||
+ | found. | ||
+ | |||
+ | .@145! | ||
+ | .@146! | ||
+ | .@147! | ||
+ | .@148! | ||
+ | |||
+ | And once it is, we close the file and restore the default i/o settings. | ||
+ | |||
+ | .@149! | ||
+ | .@150! | ||
+ | |||
+ | And return to our caller, in this case - basic. | ||
+ | |||
+ | ============================================================================ | ||
+ | [ The Demo Corner is going to be a column where each month we'll be | ||
+ | introduced to a new feature (some people call them bugs, we'll call them | ||
+ | features) of the Commodore 64 or 128 in the Video and Sound areas that | ||
+ | have commonly been shown on demos but with no mention of how to accomplish | ||
+ | them. Note that readers may also want to take a look at the introduction | ||
+ | to Rasters elsewhere in this magazine.] | ||
+ | </ | ||
+ | ====== The Demo Corner: Missing Cycles ====== | ||
+ | < | ||
+ | by Pasi ' | ||
+ | Written on 15-May-91 | ||
+ | |||
+ | |||
+ | Missing Cycles | ||
+ | -------------- | ||
+ | [all timings are in PAL, the principle applies to NTSC too] | ||
+ | |||
+ | Everybody knows that there are 63 cycles available to the C64 processor on | ||
+ | each scan line, except for one which only provides 23 cycles (later referred | ||
+ | to as a " | ||
+ | |||
+ | In the C64, the VIC (video interface controller) has much more to do than | ||
+ | just showing graphics on the screen. It also handles the memory refresh. | ||
+ | On each scanline, it has to refresh five rows in the memory matrix and | ||
+ | fetch fourty bytes of graphics data. | ||
+ | |||
+ | The VIC does all of this during the cycles (phase 1) that the processor is | ||
+ | not using the memory. | ||
+ | VIC also needs to access the character and color codes for the next row. | ||
+ | The memory bus can't be used by the CPU and the VIC at the same time, so CPU | ||
+ | access to the bus must be denied to allow the VIC to fetch its data. | ||
+ | Fortunately, | ||
+ | color (4 bits) codes to be fetched at the same time. | ||
+ | |||
+ | |||
+ | _Understanding how sprites work_ | ||
+ | |||
+ | If there are sprites on the screen, the VIC needs even more cycles to fetch | ||
+ | all of the graphics data. Scan lines are time divided so that there is | ||
+ | enough time for all action during one line. On each line, the sprite | ||
+ | image pointers are fetched during phase 1. If the sprite is to be displayed | ||
+ | on that line, the three bytes of image data are fetched right after that. | ||
+ | Out of these three fetches, two take place during phase 2 of the clock, | ||
+ | so the processor will lose these. On average, two clock cycles are lost | ||
+ | for each sprite that is displayed on that line. | ||
+ | |||
+ | But how is it possible for all eight sprites to only take 16-19 cycles | ||
+ | (depending on the timing) when we have observed that one sprite requires | ||
+ | three cycles? And why do sprites 0, 2, 4, 6 and 7 together take up as many | ||
+ | cycles as all eight sprites ? The answer may be found in the way the VIC | ||
+ | tells the CPU that it needs additional cycles. | ||
+ | |||
+ | |||
+ | _The BA signal_ | ||
+ | |||
+ | When the VIC wants to use the bus, the BA (Bus Available) signal goes | ||
+ | inactive. This will happen three cycles before the bus must be released ! | ||
+ | During these three cycles, the CPU must complete all memory accesses or | ||
+ | delay them until it has the bus again. | ||
+ | |||
+ | The CPU either completes the current instruction in the remaining cycles | ||
+ | or sits and waits for the bus to become available again. It can't execute | ||
+ | a new instruction as long as it doesn' | ||
+ | seem to be lost (besides those stolen directly for the sprites). Usually, | ||
+ | all 8 sprites take 17 cycles while one sprite takes three cycles. However, | ||
+ | the CPU may continue to execute an instruction if it does not use the bus. | ||
+ | |||
+ | |||
+ | _Theory and speculation_ | ||
+ | |||
+ | Let's suppose that all the sprites are enabled and on the same scan line. | ||
+ | Then, the VIC steals 16 cycles (2 cycles for each sprite) for the memory | ||
+ | fetches and 3 cycles as overhead for the BA signal, for a total of 19 cycles. | ||
+ | However, it will be usually less because the CPU will use some of the cycles | ||
+ | when the bus request is pending. | ||
+ | |||
+ | If we now disable sprite 4, no cycles are released for the CPU's use. This | ||
+ | is because during the previous sprite 4 data fetch, the VIC already signals | ||
+ | that it needs the bus for the sprite 5 data fetch and BA stays low (Refer | ||
+ | to the timing chart). Thus, the CPU never sees BA go high during sprite 4 | ||
+ | and 2 cycles are still lost. | ||
+ | |||
+ | Accordingly, | ||
+ | from the VIC. So in time-critical raster routines, always use sprites in | ||
+ | order. | ||
+ | |||
+ | |||
+ | _What can we do with this feature ?_ | ||
+ | |||
+ | How can this be useful? A good use is for synchronization. Normally, | ||
+ | before the CPU starts to execute the raster interrupt code, it's executing | ||
+ | an instruction of undefined cycle-length. This execution time varies from | ||
+ | two to seven cycles. | ||
+ | |||
+ | With a sprite, you can do the synchronization with a minimal effort using | ||
+ | a DEC or INC instruction in the right place. If the processor is early, | ||
+ | it has to wait for the bus, otherwise it will continue to execute cycles | ||
+ | from the instruction. | ||
+ | |||
+ | I have never experimented with any other instruction than DEC/INC, but | ||
+ | some others should work also. You need an instruction which has a cycle that | ||
+ | do not need the bus to be available. e.g. INC $3fff will increase the | ||
+ | value during the fifth cycle and do not need the bus for that. | ||
+ | |||
+ | |||
+ | _A demo program_ | ||
+ | |||
+ | The enclosed program includes a short raster color routine to demonstrate | ||
+ | this strict timing and synchronization. The background color is changed | ||
+ | 12 times on each line. The electron beam runs over eight pixels during | ||
+ | one cycle, so the timing must be precise. | ||
+ | |||
+ | -------------------------------------------------------------------------- | ||
+ | _Table for PAL VIC timing for the Missing cycles_ | ||
+ | |||
+ | |||
+ | 012345678901234567890123456789012345678901234567890123456789012 cycles | ||
+ | |||
+ | Normal scan line, 0 sprites | ||
+ | ggggggggggggggggggggggggggggggggggggggggrrrrr | ||
+ | phi-2 VIC | ||
+ | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx phi-2 6510 | ||
+ | 63 cycles available | ||
+ | |||
+ | Normal scan line, 8 sprites | ||
+ | ggggggggggggggggggggggggggggggggggggggggrrrrr | ||
+ | | ||
+ | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxXXX | ||
+ | 46-49 cycles available | ||
+ | |||
+ | Normal scan line, 4 sprites | ||
+ | ggggggggggggggggggggggggggggggggggggggggrrrrr | ||
+ | | ||
+ | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxXXX | ||
+ | 48-51 cycles available | ||
+ | |||
+ | Bad scan line, 0 sprites | ||
+ | ggggggggggggggggggggggggggggggggggggggggrrrrr | ||
+ | cccccccccccccccccccccccccccccccccccccccc | ||
+ | xxxxxxxxxxxxxxxxxxxxxxx phi-2 6510 | ||
+ | 23 cycles available | ||
+ | |||
+ | Bad scan line, 8 sprites | ||
+ | ggggggggggggggggggggggggggggggggggggggggrrrrr | ||
+ | cccccccccccccccccccccccccccccccccccccccc | ||
+ | xxxxXXX | ||
+ | 4-7 cycles available | ||
+ | |||
+ | |||
+ | g= grafix data fetch (character images or graphics data) | ||
+ | r= refresh | ||
+ | p= sprite image pointer fetch | ||
+ | c= character and color CODE fetch during a bad scan line | ||
+ | s= sprite data fetch | ||
+ | x= processor executing instructions | ||
+ | X= processor executing an instruction, | ||
+ | |||
+ | Observe! The left edge of the chart is not the left edge of the screen nor | ||
+ | the left edge of the beam, but the sprite x-coordinate 0. If you | ||
+ | have opened the borders, you know what I mean. A sprite can be | ||
+ | moved left from the coordinate 0 by using x-values greater than 500. | ||
+ | | ||
+ | | _______ | ||
+ | ||| | | | ||
+ | ||| | ||
+ | ||| | | | ||
+ | |||_______| | | ||
+ | || | | ||
+ | ||__________| | ||
+ | ^ Sprite coordinate 0 | ||
+ | |||
+ | |||
+ | -------------------------------------------------------------------------- | ||
+ | Demonstration program for missing cycles | ||
+ | |||
+ | |||
+ | COLOR0= $CE00 ; Place for color bar 0 | ||
+ | COLOR1= $CF00 ; Place for color bar 1 | ||
+ | RASTER= $FA ; Line for the raster interrupt | ||
+ | DUMMY= $CFFF ; Timing variable | ||
+ | |||
+ | *= $C000 | ||
+ | SEI ; Disable interrupts | ||
+ | LDA #$7F ; Disable timer interrupts | ||
+ | STA $DC0D | ||
+ | LDA #$01 ; Enable raster interrupts | ||
+ | STA $D01A | ||
+ | STA $D015 ; Enable Sprite 0 | ||
+ | LDA #< | ||
+ | STA $0314 | ||
+ | LDA #>IRQ | ||
+ | STA $0315 | ||
+ | LDA #$1B | ||
+ | STA $D011 | ||
+ | LDA # | ||
+ | STA $D012 | ||
+ | LDA # | ||
+ | STA $D001 ; | ||
+ | |||
+ | LDX #51 | ||
+ | LDY #0 | ||
+ | STA $D017 ; No Y-enlargement | ||
+ | LOOP0 LDA COL,X ; Create color bars | ||
+ | PHA | ||
+ | AND #15 | ||
+ | STA COLOR0,X | ||
+ | STA COLOR0+52,Y | ||
+ | STA COLOR0+104, | ||
+ | STA COLOR0+156, | ||
+ | PLA | ||
+ | LSR | ||
+ | LSR | ||
+ | LSR | ||
+ | LSR | ||
+ | STA COLOR1,X | ||
+ | STA COLOR1+52,Y | ||
+ | STA COLOR1+104, | ||
+ | STA COLOR1+156, | ||
+ | INY | ||
+ | DEX | ||
+ | BPL LOOP0 | ||
+ | CLI ; Enable interrupts | ||
+ | RTS ; Return | ||
+ | |||
+ | |||
+ | IRQ | ||
+ | NOP | ||
+ | NOP | ||
+ | NOP | ||
+ | LDY #103 ; 104 lines of colors (some of them not visible) | ||
+ | ; Reduce for NTSC, 55 ? | ||
+ | INC DUMMY ; Handles the synchronization with the help of the | ||
+ | DEC DUMMY ; | ||
+ | ; Add a NOP for NTSC | ||
+ | |||
+ | FIRST LDX COLOR0, | ||
+ | SECOND | ||
+ | STA $D020 | ||
+ | STX $D020 | ||
+ | STA $D020 | ||
+ | STX $D020 | ||
+ | STA $D020 | ||
+ | STX $D020 | ||
+ | STA $D020 | ||
+ | STX $D020 | ||
+ | STA $D020 | ||
+ | STX $D020 | ||
+ | STA $D020 | ||
+ | STX $D020 | ||
+ | ; Add a NOP for NTSC (one line = 65 cycles) | ||
+ | LDA #0 ; Throw away 2 cycles (total loop = 63 cycles) | ||
+ | DEY | ||
+ | BPL FIRST ; Loop for 104 lines | ||
+ | |||
+ | STA $D020 | ||
+ | LDA #103 ; For subtraction | ||
+ | DEC FIRST+1 | ||
+ | BPL OVER | ||
+ | STA FIRST+1 | ||
+ | OVER SEC | ||
+ | SBC FIRST+1 | ||
+ | STA SECOND+1 | ||
+ | |||
+ | LDA #1 ; Ack the raster interrupt | ||
+ | STA $D019 | ||
+ | JMP $EA31 ; Jump to the standard irq handler | ||
+ | |||
+ | COL BYT $09, | ||
+ | BYT $08, | ||
+ | BYT $07, | ||
+ | BYT $3F, | ||
+ | ; Two color bars | ||
+ | |||
+ | -------------------------------------------------------------------------- | ||
+ | Basic loader for Missing cycles example program (PAL) | ||
+ | |||
+ | 1 S=49152 | ||
+ | 2 DEFFNH(C)=C-48+7*(C> | ||
+ | 3 CH=0: | ||
+ | 4 FORF=0TO31: | ||
+ | 5 CH=CH+Q: | ||
+ | 6 PRINT" | ||
+ | 100 DATA 78A97F8D0DDCA9018D1AD08D15D0A9578D1403A9C08D1503A91B8D11D0A9FA8D, | ||
+ | 101 DATA 12D0A9E68D01D0A233A0008D17D0BDACC048290F9D00CE9934CE9D68CE999CCE, | ||
+ | 102 DATA 684A4A4A4A9D00CF9934CF9D68CF999CCFC8CA10D95860EAEAEAEAA067EEFFCF, | ||
+ | 103 DATA CEFFCFBE18CEB94FCF8D20D08E20D08D20D08E20D08D20D08E20D08D20D08E20, | ||
+ | 104 DATA D08D20D08E20D08D20D08E20D0A9008810D18D20D0A967CE64C010038D64C038, | ||
+ | 105 DATA ED64C08D67C0EE19D04C31EA0990099B00992B0890298B089C2089AB089C2F80, | ||
+ | 106 DATA A9FB089C2F87A0F97B180C6F076140096B48EC0F6741E130096B48EC3F771111, | ||
+ | 200 DATA END,0 | ||
+ | |||
+ | -------------------------------------------------------------------------- | ||
+ | Uuencoded C64 executable version (PAL) | ||
+ | |||
+ | begin 644 missing.64 | ||
+ | M`0@-" | ||
+ | MLC`ZAT$D+$$ZF4$D.HM!)+(B14Y$(J> | ||
+ | MI#, | ||
+ | M*0" | ||
+ | M2U-532!%4E)/ | ||
+ | M-40P03DU-SA$, | ||
+ | M`%L)90"# | ||
+ | M, | ||
+ | M-$$T03E$,# | ||
+ | M045!03`V-T5%1D9# | ||
+ | M1# | ||
+ | M+" | ||
+ | M, | ||
+ | M140V-$, | ||
+ | M0C`X.4, | ||
+ | M, | ||
+ | ; | ||
+ | `` | ||
+ | end | ||
+ | size 747 | ||
+ | |||
+ | -------------------------------------------------------------------------- | ||
+ | Uuencoded C64 executable version (NTSC) | ||
+ | |||
+ | begin 644 missing.64 | ||
+ | M`0@-" | ||
+ | MLC`ZAT$D+$$ZF4$D.HM!)+(B14Y$(J> | ||
+ | MI#, | ||
+ | M*0" | ||
+ | M2U-532!%4E)/ | ||
+ | M-40P03DU-SA$, | ||
+ | M`%L)90"# | ||
+ | M, | ||
+ | M-$$T03E$,# | ||
+ | M045!03`S-T5%1D9# | ||
+ | M1CA$, | ||
+ | M+" | ||
+ | M,# | ||
+ | M0S`S.$5$-C5#,# | ||
+ | M,# | ||
+ | M1C@W03!& | ||
+ | M14, | ||
+ | M,# | ||
+ | ,; | ||
+ | `` | ||
+ | end | ||
+ | size 822 | ||
+ | |||
+ | ============================================================================ | ||
+ | </ | ||
+ | ====== Kernal 64 / 128 ====== | ||
+ | < | ||
+ | by Craig Taylor (duck@pembvax1.pembroke.edu) | ||
+ | |||
+ | | ||
+ | | Introduction | | ||
+ | | ||
+ | |||
+ | When Commodore introduced the PET ages ago before the Vic-20 and Commodore 64, | ||
+ | 128 they set in the highest memory locations a series of jumps to other routines | ||
+ | so that users didn't need bother checking if any revisions had been made. They | ||
+ | were assured that the address they were jumping to, would indeed, be the address | ||
+ | that would print out a character or whatnot. | ||
+ | |||
+ | The KERNAL has grown since Commodore first introduced it, the C=128 KERNAL has | ||
+ | fifty-seven seperate routines which are available to programmers. These routines | ||
+ | handle functions relating to the serial devices (the bulk of them), the screen | ||
+ | and miscellanous system routines such as scanning the keyboard, updating and | ||
+ | reading the system clock (TI$). | ||
+ | |||
+ | +-------------------+ | ||
+ | | Table of Routines | | ||
+ | +-------------------+ | ||
+ | |||
+ | The following table lists the available routines, their function, address, | ||
+ | their name, and registers affected upon exit. In addation, on the left of each | ||
+ | line are the group that I have catagorized them under: Video(Vid), System(Sys), | ||
+ | and Serial(Ser). | ||
+ | |||
+ | --------+---------+---------+---------------------------------------+----------- | ||
+ | | | ||
+ | Address | NAME | A X Y F | Descritption | ||
+ | --------+---------+---------+---------------------------------------+----------- | ||
+ | FF47/ | ||
+ | FF4A/128| CLOSEALL| * * * | Close all files on a device | ||
+ | FF4D/128| C64MODE | | Switches to C=64 mode | ||
+ | FF50/128| DMACALL | * * | Send DMA command to REU | ||
+ | FF53/128| BOOTCALL| * * * | Attempts to run boot sector | ||
+ | FF56/128| PHOENIX | * * * | Initalizes external/ | ||
+ | FF59/128| LKUPLA | ||
+ | FF5C/128| LKUPSA | ||
+ | FF5F/128| SWAPPER | * * * | Switches betten 40 / 80 column screen |*** | ||
+ | FF62/128| DLCHAR | ||
+ | FF65/128| PFKEY | * * * * | Installs a function key definition | ||
+ | FF68/128| SETBNK | ||
+ | FF6B/128| GETCFG | ||
+ | FF6E/128| JSRFAR | ||
+ | FF71/128| JMPFAR | ||
+ | FF74/128| INDFET | ||
+ | FF77/128| INDSTA | ||
+ | FF7A/128| INDCMP | ||
+ | FF7D/128| PRIMM | ||
+ | //////// | ||
+ | FF81 | CINT | * * * | Setup VIC,screen values, 8563... | ||
+ | FF84 | IOINIT | ||
+ | FF87 | RAMTAS | ||
+ | FF8D | VECTOR | ||
+ | FF90 | SETMSG | ||
+ | FF93 | SECND | * | Sends secondary address after LISTN | ||
+ | FF96 | TKSA | * | Sends secondary address after TALK | *** *** | ||
+ | FF99 | MEMTOP | ||
+ | FF9C | MEMBOT | ||
+ | FF9F | KEY | ||
+ | FFA2 | SETMO | ||
+ | FFA5 | ACPTR | * | Grabs byte from current talker | ||
+ | FFA8 | CIOUT | * | Output byte to current listener | ||
+ | FFAB | UNTLK | * | Commands device to stop talking | ||
+ | FFAE | UNLSN | * | Commands device to stop listening | ||
+ | FFB1 | LISTN | * | Commands device to begin listening | ||
+ | FFB4 | TALK | * | Commands device to begin talking | ||
+ | FFB7 | READSS | ||
+ | FFBA | SETLFS | ||
+ | FFBD | SETNAM | ||
+ | FFC0 | OPEN | * * * * | Opens up a logical file. | *** | ||
+ | FFC3 | CLOSE | * * * * | Closes a logical file. | *** | ||
+ | FFC6 | CHKIN | * * * * | Set input channel | ||
+ | FFC9 | CHKOUT | ||
+ | FFCC | CLRCH | * * | Restore default channels | ||
+ | FFCF | BASIN | * * | Input from channel | ||
+ | FFD2 | BSOUT | * * | Output to channel (aka CHROUT) | ||
+ | FFD5 | LOAD | * * * * | Load data from file | ||
+ | FFD8 | SAVE | * * * * | Save data to file | ||
+ | FFDB | SETTIM | ||
+ | FFDE | RDTIM | * * * | Reads internal (TI$) clock | *** | ||
+ | FFE1 | STOP | * * | Scans and check for STOP key | *** | ||
+ | FFE4 | GETIN | * * * * | Reads buffered data from file | ||
+ | FFE7 | CLALL | * * | Close all open files and channels | ||
+ | FFEA | UDTIM | * * | Updates internal (TI$) clock | *** | ||
+ | FFED | SCRORG | ||
+ | FFF0 | PLOT | * * * | Read or set cursor position | ||
+ | FFF3 | IOBASE | ||
+ | --------+---------+---------+---------------------------------------+----------- | ||
+ | |||
+ | | ||
+ | +--------------------------+ | ||
+ | | The Routines Themselves. | | ||
+ | +--------------------------+ | ||
+ | |||
+ | A. Error handling | ||
+ | |||
+ | For the routines in the KERNAL that return status codes (indicated by the FL | ||
+ | status in the chart) the carry is set if there is an error. | ||
+ | carry returned is clear. | ||
+ | accumalator: | ||
+ | | ||
+ | .A |Meaning | ||
+ | ----+------------------------------ | ||
+ | 0 | Stop Key pressed | ||
+ | 1 | Too Many Open Files | | ||
+ | 2 | File Already Open +------------------------------------ | ||
+ | 3 | File Not Open | ||
+ | 4 | File Not Found | ||
+ | 5 | Device Not Present | ||
+ | 6 | File Was Not Opened As Input | ||
+ | 7 | File Was Not Opened As Output | ||
+ | 8 | File Name Not Present | ||
+ | 9 | Illegal Device Number | ||
+ | 41 | File Read Error | ||
+ | |||
+ | |||
+ | B. Device Numbers: | ||
+ | |||
+ | The following table lists the " | ||
+ | |||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | |||
+ | C. Routine Descriptions. | ||
+ | |||
+ | Due to space limitations a fully-detailed, | ||
+ | routines is not feasible. | ||
+ | routine does, expected parameters and any notes on C=128/C=64 differences as | ||
+ | well as notes to clarify any possibly confusing details. | ||
+ | |||
+ | | ||
+ | |||
+ | Routine | ||
+ | | ||
+ | | ||
+ | | ||
+ | .C = 1 -> SPOUT (output) | ||
+ | | ||
+ | | ||
+ | |||
+ | Routine | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | |||
+ | Routine | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | |||
+ | Routine | ||
+ | | ||
+ | | ||
+ | | ||
+ | NOTE: REU registers must have been previously setup. | ||
+ | | ||
+ | | ||
+ | |||
+ | Routine | ||
+ | | ||
+ | | ||
+ | | ||
+ | .X = device # | ||
+ | | ||
+ | | ||
+ | |||
+ | Routine | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | |||
+ | Routine | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | .X = Logical File #, | ||
+ | .Y = Logical Secondary #. | ||
+ | .C =1 if not found. | ||
+ | | ||
+ | |||
+ | Routine | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | |||
+ | Routine | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | |||
+ | Routine | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | |||
+ | Routine | ||
+ | | ||
+ | | ||
+ | | ||
+ | .Y = length, .X = key # (9 = Shift RUN/STOP, 10 = HELP). | ||
+ | | ||
+ | .A, .X, .Y destroyed. | ||
+ | | ||
+ | |||
+ | Routine | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | |||
+ | Routine | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | |||
+ | Routine | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | |||
+ | Routine | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | $03 - PC high | ||
+ | $04 - PC lo | ||
+ | $05 - .S (Processor status) | ||
+ | $06 - .A | ||
+ | $07 - .X | ||
+ | $08 - .Y | ||
+ | | ||
+ | | ||
+ | |||
+ | Routine | ||
+ | | ||
+ | | ||
+ | | ||
+ | .X - Bank (0-15), .Y - Index. | ||
+ | | ||
+ | | ||
+ | |||
+ | Routine | ||
+ | | ||
+ | | ||
+ | | ||
+ | .X - Bank (0-15), .Y - Index. | ||
+ | | ||
+ | | ||
+ | |||
+ | Routine | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | |||
+ | Routine | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | [ . . . ] | ||
+ | JSR $FF7D ; JSR to primm, / print following string. | ||
+ | .ASC "Hi World!" | ||
+ | .BYT $00 ; IMPORTANT: Null Terminated. | ||
+ | [ . . . ] | ||
+ | |||
+ | | ||
+ | |||
+ | Routine | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | |||
+ | Routine | ||
+ | | ||
+ | | ||
+ | | ||
+ | | 0 - Full Setup. | ||
+ | | 1 - Partial Setup. (no 8563 char) | ||
+ | | ||
+ | | ||
+ | | ||
+ | NTSC system and sets PALCNT accordingly for use in the | ||
+ | time routines. | ||
+ | |||
+ | Routine | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | |||
+ | Routine | ||
+ | | ||
+ | | ||
+ | | ||
+ | .XY = address of vectors | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | |||
+ | C128: IRQ, | ||
+ | | ||
+ | C64 : IRQ, | ||
+ | | ||
+ | |||
+ | Routine | ||
+ | | ||
+ | | ||
+ | | ||
+ | bit 6 = KERNAL Error | ||
+ | | ||
+ | | ||
+ | ... KERNAL Error messages are I/O ERROR # messages which are | ||
+ | | ||
+ | |||
+ | Routine | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | |||
+ | Routine | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | |||
+ | Routine | ||
+ | | ||
+ | | ||
+ | | ||
+ | | .XY = top of memory | ||
+ | | ||
+ | | ||
+ | | ||
+ | BANK 1 RAM. | ||
+ | |||
+ | Routine | ||
+ | | ||
+ | | ||
+ | | ||
+ | | .XY = bottom of memory. | ||
+ | | ||
+ | | ||
+ | | ||
+ | not, BANK 1 RAM. | ||
+ | |||
+ | Routine | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | |||
+ | Routine | ||
+ | | ||
+ | | ||
+ | of the KERNAL on the C64, Vic-20 and C128. Thus it is of no | ||
+ | | ||
+ | |||
+ | Routine | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | |||
+ | Routine | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | |||
+ | Routine | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | |||
+ | Routine | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | |||
+ | Routine | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | |||
+ | Routine | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | |||
+ | Routine | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | |||
+ | Routine | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | |||
+ | Routine | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | |||
+ | Routine | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | .C = 1 if error. | ||
+ | | ||
+ | |||
+ | Routine | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | .C = 1 if error | ||
+ | | ||
+ | |||
+ | Routine | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | .C = 1 if error | ||
+ | | ||
+ | |||
+ | Routine | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | .C = 1 if error | ||
+ | | ||
+ | |||
+ | Routine | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | |||
+ | Routine | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | character is ready. | ||
+ | | ||
+ | | ||
+ | | ||
+ | individually upon each call until all characters | ||
+ | have been passed ($0d is the EOL). | ||
+ | | ||
+ | | ||
+ | | ||
+ | |||
+ | Routine | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | |||
+ | Routine | ||
+ | | ||
+ | | ||
+ | | ||
+ | .XY = load address (if secondary address = 0) | ||
+ | | ||
+ | .XY = ending address | ||
+ | | ||
+ | |||
+ | Routine | ||
+ | | ||
+ | | ||
+ | | ||
+ | .XY = end address | ||
+ | | ||
+ | .XY = used. | ||
+ | | ||
+ | |||
+ | Routine | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | |||
+ | Routine | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | |||
+ | Routine | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | .A -> | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | ||
+ | KEY: |STOP |Q |C= | ||
+ | |||
+ | Routine | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | .XY = used. | ||
+ | | ||
+ | |||
+ | Routine | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | this routine. | ||
+ | |||
+ | |||
+ | Routine | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | |||
+ | Routine | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | .Y - Window Col Max | ||
+ | .A - Screen Col Max (128 only, 64 unchanged) | ||
+ | | ||
+ | |||
+ | Routine | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | .Y = Row | ||
+ | | ||
+ | .X = Current Col | ||
+ | .Y = Current Row | | ||
+ | | ||
+ | |||
+ | Routine | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | |||
+ | ============================================================================ | ||
+ | </ | ||
+ | ====== 64K VDC RAM and an alternate GEOS128 Background Screen ====== | ||
+ | < | ||
+ | by Robert A. Knop Jr. (rknop@tybalt.caltech.edu, | ||
+ | |||
+ | I. Introduction | ||
+ | |||
+ | GEOS, both the 64 and 128 varieties, uses bitmapped screens for its output. | ||
+ | In 40 columns, this means 8K of system memory is set aside for the main | ||
+ | screen. | ||
+ | GEOS allocates a second 8K as a " | ||
+ | an intact copy of the foreground (FG) screen. | ||
+ | number of reasons; one, it can be used as an undo buffer, as it is in | ||
+ | geoPaint. | ||
+ | eraser across it, the effects of the eraser are only written to the FG screen. | ||
+ | A click of the UNDO button brings the BG screen, with the pre-eraser version | ||
+ | of your painting, back to the fore. Another use is for buffering the contents | ||
+ | of the screen when something like a dialog box or a menu is written over it. | ||
+ | When a dialog box is erased, and you see whatever had been underneatg it | ||
+ | magically reappear, the graphics underneath are being pulled from the BG | ||
+ | screen. | ||
+ | |||
+ | Applications have the option not to use the BG screen. | ||
+ | vectors and flags, and you can use the 8K BG screen for application RAM. | ||
+ | (This is very convenient, since the BG screen is directly above the normal 22K | ||
+ | of application RAM in the GEOS memory map.) Of course, the application then | ||
+ | has to provide some way of redrawing blocks of the screen hidden by menus and | ||
+ | dialog boxes. | ||
+ | from, a dialog box in geoWrite, there is briefly a blank rectangle on the | ||
+ | screen before the text is redrawn on the screen. | ||
+ | |||
+ | Under GEOS128 in 80 columns, the bitmap screen is now twice as large: 640x200 | ||
+ | instead of 320x200. | ||
+ | used for both the 40 column FG and 40 column BG screen is used for the 80 | ||
+ | column BG screen. | ||
+ | |||
+ | GEOS128 was written for, and runs on, 128's with only the usual 16K of VDC | ||
+ | RAM. And, it uses basically all 16K of this RAM. However, if you have 64K of | ||
+ | VDC RAM (as is the case with 128D' | ||
+ | upgraded), you've got an additional 48K of VDC RAM that the GEOS system | ||
+ | doesn' | ||
+ | Then, if you are writing an 80-column only application, | ||
+ | the 40-column BG screen at $6000 and the 40-column FG screen at $a000, in main | ||
+ | memory which your application can use however it sees fit. | ||
+ | |||
+ | |||
+ | II. Support Routines | ||
+ | |||
+ | Only a small number of routines actually need to be written to implement this | ||
+ | scheme; moreover, these routines are realatively straightforward. | ||
+ | we are simply copying memory from one part of VDC RAM to another. | ||
+ | block copy feature of the VDC is very helpful in this endeavor. | ||
+ | Taylor' | ||
+ | file vdc-bg.sfx, associated with this issue of the Hacking Mag, is a | ||
+ | self-extracting archive with a number of geoProgrammer source files (in | ||
+ | geoWrite 2.1 format) and a small dippy demonstration program. | ||
+ | contains the following routines: | ||
+ | |||
+ | InitVDC | ||
+ | VDCImpLine | ||
+ | VDCRecLine | ||
+ | VDCImpRect | ||
+ | VDCRecRect | ||
+ | |||
+ | Each Imprint routine actually uses most of the same code as the corresponding | ||
+ | Recover routine; all that differs is the offset to the " | ||
+ | " | ||
+ | for the BG screen is $4000.) | ||
+ | non-VDC Imprint and Recover routines as documented in the Hitchhiker' | ||
+ | (You will note, however, that for whatever reason the standard GEOS | ||
+ | ImprintLine and RecoverLine routines were only implemented for Apple GEOS.) | ||
+ | Briefly, these are: | ||
+ | |||
+ | Routine: | ||
+ | |||
+ | Pass: Nothing | ||
+ | |||
+ | Return: | ||
+ | |||
+ | Destroys: | ||
+ | |||
+ | Note: This routine should be called at the very beginning of your | ||
+ | | ||
+ | |||
+ | ------------------------------------------------------------------------------ | ||
+ | |||
+ | |||
+ | Routine: | ||
+ | | ||
+ | |||
+ | Pass: r3 -- left edge of line to imprint/ | ||
+ | | ||
+ | r11L -- y coordinate of line to imprint/ | ||
+ | |||
+ | Return: | ||
+ | |||
+ | Destroys: | ||
+ | |||
+ | ------------------------------------------------------------------------------- | ||
+ | |||
+ | Routine: | ||
+ | | ||
+ | |||
+ | Pass: r3 -- x-coordinate of upper-left corner (word) | ||
+ | | ||
+ | | ||
+ | | ||
+ | |||
+ | Return: | ||
+ | |||
+ | Destroys: | ||
+ | |||
+ | ------------------------------------------------------------------------------ | ||
+ | |||
+ | |||
+ | To discuss the imprint and recover line routines, consider the ASCII diagram | ||
+ | of a portion of a line on the VDC screen. | ||
+ | the line to be copied. | ||
+ | I is the msb of the corresponding byte in the VDC bitmap. | ||
+ | horizontally across the screen; there is no card structure found in the 80 | ||
+ | column bitmap screen.) | ||
+ | |||
+ | | ||
+ | | ||
+ | ^ \______________/ | ||
+ | | ||
+ | residual | ||
+ | |||
+ | The line moving routine needs to figure the location in VDC RAM of the | ||
+ | leftmost full byte, the number of full bytes, and the location of the two | ||
+ | residual bytes; additionally, | ||
+ | with bits set corresponding to pixels to be copied. | ||
+ | create the proper combination of data from the source and destination screens | ||
+ | in the two residual bytes. | ||
+ | |||
+ | Once it knows all this, all the line routines do is (1) submit a VDC block | ||
+ | copy to copy the full bytes, (2) read the left residual byte from the source, | ||
+ | mask out the appropriate pixels, and OR it with the appropriate pixels from | ||
+ | the destination, | ||
+ | byte. | ||
+ | |||
+ | The rectangle routines simply call the line copy routines repeatedly, | ||
+ | (r2H)-(r2L)+1 times. | ||
+ | from a coding time point of view < | ||
+ | need calculate the residual bit masks and the locations once. Thereafter, | ||
+ | locations can be updated by adding 80 (the length of a VDC line) for each new | ||
+ | line. The changes to the code to implement this are not difficult, and it | ||
+ | isn't clear why I didn't make them....) | ||
+ | |||
+ | |||
+ | III. Use of the Routines | ||
+ | |||
+ | In a word, you use these routines whenever you would have used the normal GEOS | ||
+ | imprint/ | ||
+ | |||
+ | First of all, you need to set the flag dispBufferOn to ST_WR_FORE. | ||
+ | system graphic routines think that the BG screen is in main memory, and thus | ||
+ | will not correctly use your new VDC BG screen. | ||
+ | that you can't just blithely go drawing graphics, assuming that they' | ||
+ | buffered for recall when needed. | ||
+ | a call to VDCImpRect either right after you've made some graphic change, or | ||
+ | right before something potentially hazardous will happen (e.g. a call to | ||
+ | DoDlgBox). | ||
+ | Submenus which call VDCImpRect before opening the submenu. | ||
+ | |||
+ | Second, you should load recoverVector with VDCRecRect. | ||
+ | the same parameters as RecoverRectangle, | ||
+ | Once you set this vector, all menus and dialog boxes erased from the screen | ||
+ | automatically restore the destroyed region from your VDC BG screen. | ||
+ | |||
+ | Both of these are demonstrated in the test program included in vdc-bg.sfx. | ||
+ | |||
+ | |||
+ | IV. Another 32K | ||
+ | |||
+ | The alert reader will have noticed that the VDC BG screen only takes as much | ||
+ | memory as the VDC FG screen, i.e. 16K. Thus, even with this scheme, there is | ||
+ | still another 32K of free memory in VDC RAM. Quadruple buffering, anyone? | ||
+ | |||
+ | A more tantalizing prospect would be to implement a 640x400 interlaced screen | ||
+ | for GEOS128. | ||
+ | that terrible flicker. | ||
+ | polarizing filters (in layman' | ||
+ | choices. | ||
+ | argum`>: | ||
+ | with those routines. | ||
+ | faced with re-writing all of the graphics routines. | ||
+ | 8K you've freed up at $a000, I suppose.) | ||
+ | would require 32K of memory for the bitmap, you could still have a VDC | ||
+ | Background screen. | ||
+ | |||
+ | [ Note: The code discussed within this article is available via anonymous | ||
+ | FTP at tybalt.caltech.edu under the directory pub/ | ||
+ | vdc-bg.sfx. This will dissolve into the GEOWRITE source files.] | ||
+ | |||
+ | ============================================================================ | ||
+ | </ | ||
+ | ====== GeoPaint File Format ====== | ||
+ | < | ||
+ | -------------------- | ||
+ | by Bruce Vrieling (bvrieling@undergrad.math.waterloo.edu) | ||
+ | |||
+ | GeoPaint is an excellent graphics program written for the GEOS environment. Its | ||
+ | disk access is relatively quick, compared it to what a comparable program would | ||
+ | do on a non-GEOS equipped C64. Part of this accomplishment can be attributed to | ||
+ | the diskTurbo that is an integral part of GEOS. However, the special GeoPaint | ||
+ | file-saving scheme deserves some of the credit. | ||
+ | |||
+ | |||
+ | VLIR | ||
+ | ---- | ||
+ | |||
+ | GeoPaint files are always stored in Variable Length Indexed Recording files. | ||
+ | VLIR files offer advantages not available without GEOS. Generally speaking, VLIR | ||
+ | is the ultimate in RELATIVE files. | ||
+ | |||
+ | The format of a VLIR file is not that difficult to figure out. While in a | ||
+ | regular C64 file, the two bytes directly following the FILETYPE byte in the | ||
+ | directory would point to the data file following, VLIR files use these two bytes | ||
+ | to point to a VLIR HEADER BLOCK (don't confuse the VLIR HEADER block with the | ||
+ | INFO block). The first two bytes of this block are $00/FF, as the header block | ||
+ | is a MAXIMUM of one block long. (This is why when you VALIDATE a GEOS disk from | ||
+ | C64 mode, GeoPaint pictures are lost. Only the header block is recognised as | ||
+ | being part of the file. The rest of the picture gets deallocated in the BAM). | ||
+ | The remaining 254 bytes in the block are divided into 127 2-byte pointers to | ||
+ | tracks/ | ||
+ | the VLIR file, which may be ANY number of blocks long. The VLIR track/ | ||
+ | pointers in the VLIR header block only point to the FIRST block of the chain. | ||
+ | From then on, the sectors chain themselves together using the normal format ie. | ||
+ | the first two bytes of each block point to the following block. | ||
+ | |||
+ | A sample GeoPaint VLIR header might look like this: | ||
+ | |||
+ | 0000:00 FF 03 11 03 05 03 01 ........ | ||
+ | 0008:04 03 00 FF 00 FF 00 FF ........ | ||
+ | 0010:04 07 00 00 00 00 00 00 ........ | ||
+ | | ||
+ | |||
+ | The first two bytes, $00/FF, tell the drive that this is the last (and only) | ||
+ | block in this VLIR HEADER SECTION (will never be more than 1 block, as was | ||
+ | mentioned earlier). The next pair of bytes, $03/11, points to the first VLIR | ||
+ | record. The next two, $03/05, point to the second record. | ||
+ | |||
+ | You will notice that 5th record contains the values $00/FF. This means that for | ||
+ | this record, there is no picture data. We will get into exactly what the data | ||
+ | held in the records mean in a minute. The $00/FF entries indicate an empty | ||
+ | record. Finally, the 9th entry, $00/00 indicates the end of the GeoPaint file. | ||
+ | There is no more data beyond this point. | ||
+ | |||
+ | One note should be made. GeoPaint is not always consistent in its handling of | ||
+ | the data in a header block. Sometimes, it will show quite a few $00/ | ||
+ | combinations before finally terminating with a $00/00. When reading the file, | ||
+ | I always read the entire header, as I don't trust the end of file method | ||
+ | mentioned above. Just remember that any track/ | ||
+ | $00/FF or $00/00 is a valid record, with picture data. | ||
+ | |||
+ | |||
+ | Layout on Screen | ||
+ | ---------------- | ||
+ | |||
+ | GEOS orients the data in a GeoPaint file slightly differently than in a | ||
+ | PhotoScrap or an Icon. A photoscrap stores the bytes corrosponding to a screen | ||
+ | which looks like this: | ||
+ | |||
+ | 001 002 003 004 005 ....0012 | ||
+ | 013 014 015 016 017 ....0024 | ||
+ | |||
+ | Consecutive bytes are placed BESIDE each other. However, if you are at all | ||
+ | familiar with the layout of a C64 hi-res screen, you will know this is very | ||
+ | different from the layout that the VIC chip sees data. GeoPaint uses a format | ||
+ | identical to the VIC chip layout on screen. | ||
+ | |||
+ | GeoPaint pictures are stored in the following format: | ||
+ | |||
+ | 001 009 017 025 033 ..... 313 | ||
+ | 002 010 018 026 034 ..... 314 | ||
+ | 003 011 019 027 035 ..... 315 | ||
+ | 004 012 020 028 036 ..... 316 | ||
+ | 005 013 021 029 037 ..... 317 | ||
+ | 006 014 022 030 038 ..... 318 | ||
+ | 007 015 023 031 039 ..... 319 | ||
+ | 008 016 024 032 040 ..... 320 | ||
+ | |||
+ | 321 329 ..... | ||
+ | 322 330 ..... | ||
+ | 323 331 ..... | ||
+ | 324 332 ..... | ||
+ | 325 333 ..... | ||
+ | 326 334 ..... | ||
+ | 327 335 ..... | ||
+ | 328 336 ..... | ||
+ | |||
+ | As you can see, this is very different from the PhotoScrap format. Consecutive | ||
+ | bytes are NOT stored on the screen beside each other. Rather, they are stored | ||
+ | underneath each other into groups of 8 bytes. This makes moving the data from | ||
+ | the disk onto the screen that much faster, as the decompacted bytes can just be | ||
+ | stored on the screen after each other. Of course, this makes porting GEOS pics | ||
+ | to the 128's VDC that much more difficult, as the VDC conforms to the | ||
+ | PhotoScrap format. | ||
+ | |||
+ | |||
+ | Compression Method | ||
+ | ------------------ | ||
+ | |||
+ | GEOS uses an excellent compression method to store files on disk. You may have | ||
+ | noticed that nearly empty pictures on disk consume very little disk space. This | ||
+ | can be credited to GeoPaint' | ||
+ | |||
+ | Basically, the format of the compression has one COMMAND byte followed by one | ||
+ | or more DATA bytes. The COMMAND byte tells GEOS what to do with following DATA | ||
+ | bytes. There are 4 commands for compression: | ||
+ | |||
+ | 1) If the COMMAND byte is less than 64, this indicates that there are ' | ||
+ | many DATA bytes following that are to be taken as individual bytes. This is | ||
+ | the least effective method of compression, | ||
+ | |||
+ | 2) If the COMMAND byte ranges from 65 to 127, then this is a special type of | ||
+ | | ||
+ | This DATA is used to make an 8*8 ' | ||
+ | ' | ||
+ | is done. ' | ||
+ | down to, is repeating the 8 byte DATA stamp ' | ||
+ | | ||
+ | |||
+ | 3) If the COMMAND byte is 129 or greater, then the following DATA byte is | ||
+ | | ||
+ | byte is called in, and simply repeated. #1 called in ' | ||
+ | | ||
+ | |||
+ | 4) If the COMMAND byte is ZERO, we have reached the end of the VLIR record for | ||
+ | the GeoPaint picture. | ||
+ | |||
+ | It should be noted that the COMMAND byte will NEVER be 64 or 128. If it is, | ||
+ | there has been an error. | ||
+ | |||
+ | |||
+ | Format of Data After Decompacting | ||
+ | --------------------------------- | ||
+ | |||
+ | After the data has been decompacted, | ||
+ | VLIR record holds 16 scanlines of data, or 2 character lines (different ways of | ||
+ | looking at the same thing). | ||
+ | |||
+ | The format of the data is as follows: | ||
+ | |||
+ | | ||
+ | line (8 scanlines). Remember, GeoPaint pictures are 640 pixels across. 640 | ||
+ | | ||
+ | comes to 640 bytes. | ||
+ | |||
+ | These bytes are followed by the 640 bytes for the second chacacter line | ||
+ | (next 8 scanlines). This is followed by 8 garbage bytes that accidentaly | ||
+ | | ||
+ | zero. | ||
+ | |||
+ | | ||
+ | | ||
+ | line. To wrap things up, the VLIR record is terminated by a zero byte. | ||
+ | |||
+ | The next VLIR record will hold the data for the NEXT 16 scanlines, and so | ||
+ | on. | ||
+ | |||
+ | |||
+ | Conclusion | ||
+ | ---------- | ||
+ | |||
+ | That about wraps up this discussion on GeoPaint format for files. We' | ||
+ | discussed the format of VLIR files on disk, layout of picture data on screen, | ||
+ | compression methods used in GeoPaint files, and the format of the data once | ||
+ | decompacted. I hope this information will come in handy for someone. | ||
+ | |||
+ | ============================================================================== | ||
+ | </ | ||
+ | ====== Rasters - What They Are and How to Use Them ====== | ||
+ | < | ||
+ | by Bruce Vrieling - (bvrieling@undergrad.math.waterloo.edu) | ||
+ | |||
+ | Anyone who has fiddled around with interrupts on the Commodore 64 has | ||
+ | undoubtedly heard at one time or another of the concept of rasters being | ||
+ | mentioned. Rasters are the ' | ||
+ | so they say. What is a raster? And how does one go about writing a program to | ||
+ | use them? | ||
+ | |||
+ | Overview of what Interrupts are all about | ||
+ | ----------------------------------------- | ||
+ | |||
+ | A raster is sort form for the concept of a ' | ||
+ | going into rasters, perhaps a brief review of interrupts is in order. | ||
+ | |||
+ | Interrupts are events generated by the CIA timer in the C64 to perform certain | ||
+ | tasks. 60 times a second, the CIA chip signals an interrupt is due to be | ||
+ | processed (ie. the interrupt timer timed out). This causes the 6510 CPU to stop | ||
+ | executing the current program, save the registers on the stack, and begin to | ||
+ | execute the interrupt code. Some of the things which get done during an | ||
+ | interrupt include the keyboard scan, and updating TI (the software clock). When | ||
+ | the interrupt code is finished, an RTI instruction is executed, which brings | ||
+ | the interrupt' | ||
+ | and the current program in memory continues to execute once again. It will | ||
+ | continue to do so until the next interrupt occurs, about 1/60 of a second later. | ||
+ | |||
+ | The above is what happens in a normal C64 (the C128 follows the same idea, but | ||
+ | more events occur during a C128 interrupt). [Ed. Note: In addition, the C=128 | ||
+ | generates its interrupts via a screen raster instead of the CIA chip.] | ||
+ | |||
+ | However, you can change the normal course of events, and cause some code of your | ||
+ | design to be added to the normal list of events which occur every interrupt. | ||
+ | Some of the simple favourites include flashing the border 60 times per second. | ||
+ | (Note that we have not begun the topic of rasters yet; this has nothing to do | ||
+ | with rasters. That discussion begins at the next heading.) | ||
+ | |||
+ | How do you change the interrupt' | ||
+ | The C64 contains an interrupt VECTOR at locations 788/9 which is ' | ||
+ | through' | ||
+ | this vector to point to YOUR code, and make the end of your code point to the | ||
+ | normal Kernal location (where the interrupt normally would have jumped to, | ||
+ | $EA31), and you are careful not to step on anything, your code will be executed | ||
+ | 60 times per second. | ||
+ | |||
+ | An example is in order: | ||
+ | |||
+ | ; flasher | ||
+ | ; | ||
+ | ; this program causes the border to flash 60 times per second | ||
+ | ; | ||
+ | setup = * | ||
+ | |||
+ | sei ; disable interrupts | ||
+ | lda #< | ||
+ | sta 788 ; put into interrupt vector | ||
+ | lda #> | ||
+ | sta 789 | ||
+ | cli ; re-enable interrupts | ||
+ | rts ; return to caller | ||
+ | |||
+ | intcode = * | ||
+ | |||
+ | inc $d020 ; change border colour | ||
+ | jmp $ea31 ; exit back to rom | ||
+ | |||
+ | |||
+ | The above is an example of a very simple interrupt routine. If you were to | ||
+ | assemble it with an assembler, and SYS to the SETUP routine, you would see your | ||
+ | border flash 60 times per second. | ||
+ | |||
+ | You will notice the SEI and CLI machine language instructions used above. They | ||
+ | are very important. We don't want an interrupt occurring in between the STA 788 | ||
+ | and the STA 789 instructions. | ||
+ | |||
+ | Think what would happen if one did: 788 would have been modified, but 789 would | ||
+ | still be pointing to the high byte of the Kernal address. Result: the interrupt | ||
+ | would have jumped to heaven knows where. You can be virtually guaranteed that | ||
+ | it would NOT be pointing to a valid piece of interrupt code. Your machine would | ||
+ | crash. The SEI instruction turns interrupts OFF, so that there is no danger of | ||
+ | an interrupt occurring during execution of the following routine. The CLI turns | ||
+ | them back on. If you forget to turn them back on, and accidentally leave them | ||
+ | off, your keyboard will freeze when you return to basic, and your machine will | ||
+ | seem to lock up. | ||
+ | |||
+ | The above was a very simple example. There are many useful things which can also | ||
+ | be done on an interrupt. I have seen code which played music in the background | ||
+ | of a running Basic program (it played the popular .MUS files). GEOS uses | ||
+ | interrupts extensively to control the pointing of the mouse, and to trigger | ||
+ | events. Interrupts are powerful beasts, and the following concept concerning | ||
+ | raster interrupts specifically is a particularly useful animal for some people. | ||
+ | |||
+ | |||
+ | The Raster | ||
+ | ---------- | ||
+ | |||
+ | A raster is a loosely used term. It refers to an interrupt that is triggered | ||
+ | when the ray gun on the back of your monitor draws a certain line on the video | ||
+ | screen. There are many different sources which can cause an interrupt. You are | ||
+ | not limited to what the CIA chip can do. Rasters depend on interrupts | ||
+ | specifically generated by the VIDEO chip. You could make this interrupt change | ||
+ | the border colour of the screen below a certain screen line. When the screen | ||
+ | line you specified gets redrawn, the interrupt goes off. Your code then quickly | ||
+ | changes some memory locations to create a different video mode or effect. You | ||
+ | could cause the bottom half of the screen to gets it's character definitions | ||
+ | from another, different character set. Or, you could make the top 3/4 of your | ||
+ | screen exist in hi-res multi-colour graphics, and keep the bottom 1/4 of the | ||
+ | screen in text mode. | ||
+ | |||
+ | Some facts about the video screen: it gets redrawn exactly 60 times per second. | ||
+ | It contains 200 scan lines on the normal 25*40 display, numbered 50 to 250 or | ||
+ | thereabouts (note that there are more visible scan lines though: the top and | ||
+ | bottom borders, for example). The actual re-drawing of the screen is | ||
+ | synchronized to the electrical power coming into your house, 60 Hz. That's why | ||
+ | some programs behave differently when run on European machines. The power is | ||
+ | delivered at 50 Hz over there. | ||
+ | |||
+ | Why do we have to worry about a video interrupt? If the screen gets redrawn 60 | ||
+ | times per second, and regular interrupts also occur at 60 times per second, why | ||
+ | not simply put some code into the regular interrupt to do what we want with the | ||
+ | screen? Because the two types of interrupts are not in sync. Neither one of them | ||
+ | occurs EXACTLY 60 times per second, and the differences are enough to make it | ||
+ | next to impossible to get coordinated activity of any kind happening on the | ||
+ | screen. When we use the video interrupt, we KNOW we are at a certain line on the | ||
+ | screen, as being on that line is what caused the interrupt to happen in the | ||
+ | first place. | ||
+ | |||
+ | So, let's summarize. We know that regular interrupts occur 60 times per second. | ||
+ | We also know that the video screen gets re-drawn 60 times per second, and that | ||
+ | we can cause an interrupt to be generated when a certain line gets drawn on the | ||
+ | screen. One slight drawback to all of this is that BOTH types of interrupts | ||
+ | (regular and raster driven) travel through the SAME vector (ie. about 120 | ||
+ | interrupts per second, 60 of one, and 60 of the other). Your code will have to | ||
+ | check and see what the source of the interrupt was, and act accordingly. Or will | ||
+ | it? | ||
+ | |||
+ | The system needs an interrupt to occur 60 times per second to do housekeeping, | ||
+ | and uses the CIA clock to generate the interrupts. We want to interrupt every | ||
+ | time a certain scan line is reached on the monitor, which will also just happen | ||
+ | to occur at 60 times per second. We also have to make sure that they don' | ||
+ | interfere with each other. The regular interrupts should be sent to their Rom | ||
+ | destination, | ||
+ | else. | ||
+ | |||
+ | If both are occurring at 60 times per second, why not do the job of the system | ||
+ | Rom, and our video code on the SAME interrupt? We know that the CIA chip is not | ||
+ | good for this; it is out of sync with the video image. Why not turn OFF the CIA | ||
+ | interrupt, enable the raster/ | ||
+ | Then we would have an interrupt signal that occurs 60 times per second, and is | ||
+ | in perfect sync with the video image. | ||
+ | |||
+ | That's exactly what we're going to do. | ||
+ | |||
+ | Astute reads will notice a slight flaw in the above logic. For simplification | ||
+ | purposes, I didn't get into the fact that you will need TWO raster interrupts | ||
+ | PER SCREEN to accomplish anything useful. Why two? Because any change to the | ||
+ | video mode you put into effect 3/4 of the way down the screen will have to be | ||
+ | undone at the TOP of the next screen update. If you decide to make the top 3/4 | ||
+ | of the screen a hi-res image, and the bottom 1/4 text, you need one interrupt | ||
+ | 3/4 of the way down the screen to change from hi-res to text, but you need a | ||
+ | SECOND one at the top of the screen to change back to hi-res from text. | ||
+ | |||
+ | So, we will now have 120 interrupts going off every second to accomplish our | ||
+ | video desires, with 60 of them working a double shift, making sure the system | ||
+ | interrupt code gets executed also. Remember that we are working with a specific | ||
+ | example. There is no reason why you couldn' | ||
+ | video modes, and have (N+1)*60 interrupts going off per second. As long as you | ||
+ | keep your code short (so your interrupts don't take too long, and have another | ||
+ | interrupt occur before the current one is done - messy), it will work | ||
+ | beautifully. | ||
+ | |||
+ | So far, this is all talk. Let's write a few short code segments to accomplish | ||
+ | some of the feats we've just discussed. | ||
+ | |||
+ | The first we'll do is a re-hash of the one presented above. It flashes the | ||
+ | border again. It does not do any mid-screen changes of video modes or anything | ||
+ | fancy like that, so only 1 interrupt per screen is required (ie. 60 per second, | ||
+ | not 120 etc.). This program simply shows the same idea, but this time using | ||
+ | video interrupts as the source rather than the CIA. You probably won't | ||
+ | notice a difference during execution. | ||
+ | |||
+ | ---------------------------------------------------------------------------- | ||
+ | ; flasher - part II | ||
+ | ; | ||
+ | ; this program causes the border to flash 60 times per second | ||
+ | ; the source of the interrupts is the video chip | ||
+ | ; | ||
+ | setup = * | ||
+ | |||
+ | sei ; disable interrupts | ||
+ | |||
+ | lda #$7f ; turn off the cia interrupts | ||
+ | sta $dc0d | ||
+ | |||
+ | lda $d01a ; enable raster irq | ||
+ | ora #$01 | ||
+ | sta $d01a | ||
+ | |||
+ | lda $d011 ; clear high bit of raster line | ||
+ | and #$7f | ||
+ | sta $d011 | ||
+ | |||
+ | lda #100 ; line number to go off at | ||
+ | sta $d012 ; low byte of raster line | ||
+ | |||
+ | lda #> | ||
+ | sta 788 ; put into interrupt vector | ||
+ | lda #< | ||
+ | sta 789 | ||
+ | cli ; re-enable interrupts | ||
+ | rts ; return to caller | ||
+ | |||
+ | |||
+ | intcode = * | ||
+ | |||
+ | inc $d020 ; change border colour | ||
+ | |||
+ | lda $d019 ; clear source of interrupts | ||
+ | sta $d019 | ||
+ | |||
+ | lda #100 ; reset line number to go off at | ||
+ | sta $d012 | ||
+ | |||
+ | jmp $ea31 ; exit back to rom | ||
+ | -------------------------------------------------------------------------- | ||
+ | |||
+ | As you can tell, there' | ||
+ | original. Execute it, and you'll notice that it looks pretty much the same as | ||
+ | the results from the first program. But there' | ||
+ | are now being generated from the video chip, not the CIA. For this program, it | ||
+ | didn't make much difference. However, for a more complicated program, it makes | ||
+ | a world of difference. | ||
+ | |||
+ | I'd better explain some of the code used above: | ||
+ | |||
+ | lda #$7f | ||
+ | sta $dc0d | ||
+ | |||
+ | - This piece disables any interrupts caused by the CIA chip. | ||
+ | |||
+ | lda $d01a | ||
+ | ora #$01 | ||
+ | sta $d01a | ||
+ | |||
+ | - Location $d01a controls which sources may cause an interrupt | ||
+ | | ||
+ | | ||
+ | | ||
+ | bit, and this piece of code activates it. | ||
+ | |||
+ | lda $d011 | ||
+ | and #$7f | ||
+ | sta $d011 | ||
+ | |||
+ | - This code clears bit #7 of location $d011. This location is used | ||
+ | for many different things. Bit #7 represents the highest bit of | ||
+ | the raster line (see segment below for more on the raster line | ||
+ | #). More than 256 raster line numbers are possible (some are off | ||
+ | | ||
+ | This bit is the 9th bit. I set it to zero because all my code | ||
+ | | ||
+ | | ||
+ | my part, to make the code simpler. | ||
+ | |||
+ | lda #100 | ||
+ | sta $d012 | ||
+ | |||
+ | - Location $d012 is the lower 8 bits of the raster line on which | ||
+ | the interrupt is to be generated. The number 100 was another | ||
+ | | ||
+ | | ||
+ | | ||
+ | |||
+ | lda #> | ||
+ | ... | ||
+ | rts | ||
+ | |||
+ | - Re-vectors the interrupt code to the new code. | ||
+ | |||
+ | inc $d020 | ||
+ | |||
+ | - Changes the border colour. | ||
+ | |||
+ | lda $d019 | ||
+ | sta $d019 | ||
+ | |||
+ | - These lines clear the bit in the interrupt register which tells the | ||
+ | | ||
+ | |||
+ | lda #100 | ||
+ | sta $d012 | ||
+ | |||
+ | - This line resets the raster line to go off at line number 100 | ||
+ | again (same as above). It should be reset, so the next interrupt | ||
+ | will know what line to occur on. | ||
+ | |||
+ | jmp $ea31 | ||
+ | |||
+ | - Exit back to the Kernal Rom. | ||
+ | |||
+ | |||
+ | A Useful Example | ||
+ | ---------------- | ||
+ | |||
+ | The following is an example of a more sophisticated piece of raster code. It | ||
+ | makes the top half of the screen border white, and the bottom half black. | ||
+ | |||
+ | --------------------------------------------------------------------------- | ||
+ | setup = * | ||
+ | |||
+ | ; some equates | ||
+ | |||
+ | COLOUR1 = 0 | ||
+ | COLOUR2 = 1 | ||
+ | LINE1 = 20 | ||
+ | LINE2 = 150 | ||
+ | |||
+ | ; code starts | ||
+ | |||
+ | setup = * | ||
+ | |||
+ | sei ; disable interrupts | ||
+ | |||
+ | lda #$7f ; turn off the cia interrupts | ||
+ | sta $dc0d | ||
+ | |||
+ | lda $d01a ; enable raster irq | ||
+ | ora #$01 | ||
+ | sta $d01a | ||
+ | |||
+ | lda $d011 ; clear high bit of raster line | ||
+ | and #$7f | ||
+ | sta $d011 | ||
+ | |||
+ | lda # | ||
+ | sta $d012 ; low byte of raster line | ||
+ | |||
+ | lda #> | ||
+ | sta 788 ; put into interrupt vector | ||
+ | lda #< | ||
+ | sta 789 | ||
+ | |||
+ | cli ; re-enable interrupts | ||
+ | rts ; return to caller | ||
+ | |||
+ | intcode = * | ||
+ | |||
+ | lda modeflag | ||
+ | ; bottom of screen | ||
+ | beq mode1 | ||
+ | jmp mode2 | ||
+ | |||
+ | mode1 = * | ||
+ | |||
+ | lda #$01 ; invert modeflag | ||
+ | sta modeflag | ||
+ | |||
+ | lda # | ||
+ | sta $d020 | ||
+ | |||
+ | lda # | ||
+ | sta $d012 ; (which will activate MODE2) | ||
+ | |||
+ | lda $d019 | ||
+ | sta $d019 | ||
+ | |||
+ | jmp $ea31 ; MODE1 exits to Rom | ||
+ | |||
+ | mode2 = * | ||
+ | |||
+ | lda #$00 ; invert modeflag | ||
+ | sta modeflag | ||
+ | |||
+ | lda # | ||
+ | sta $d020 | ||
+ | |||
+ | lda # | ||
+ | sta $d012 ; (which will activate MODE1) | ||
+ | |||
+ | lda $d019 | ||
+ | sta $d019 | ||
+ | |||
+ | pla ; we exit interrupt entirely. | ||
+ | tay ; since happening 120 times per | ||
+ | pla ; second, only 60 need to go to | ||
+ | tax ; hardware Rom. The other 60 simply | ||
+ | pla ; end | ||
+ | rti | ||
+ | |||
+ | modeflag .byte 0 | ||
+ | |||
+ | ---------------------------------------------------------------------------- | ||
+ | |||
+ | The above code, when executed, will result in the top half of your border being | ||
+ | white, and the bottom black. You may wish to fiddle with the equates (COLOUR1, | ||
+ | COLOUR2, LINE1, and LINE2) to get different effects. | ||
+ | |||
+ | I see some confused faces concerning why the above exit the interrupts the way | ||
+ | they do. Remember, since we want a split screen effect, we have to have one | ||
+ | interrupt occur at the TOP of the screen, to turn on the WHITE effect, and one | ||
+ | midway down to turn on the BLACK effect. Two interrupts times 60 means 120 | ||
+ | interrupts will be executed per second. The Rom only needs 60 per second to | ||
+ | service the keyboard and its other stuff. So, we send 60 to the Rom (the | ||
+ | interrupts which go through MODE1) by JMPing to $EA31, and the other 60 we | ||
+ | trash. The PLA... RTI business is the proper way to bring an interrupt to an end | ||
+ | without going through the Rom. The RTI will ReTurn from Interrupt, and cause the | ||
+ | regular program to continue to execute. | ||
+ | |||
+ | That brings to an end this discussion on rasters. I hope the above examples | ||
+ | have proved to be a valuable learning tool for you. With luck, they will | ||
+ | motivate you to continue to experiment with rasters, and come up with some neat | ||
+ | effects. | ||
+ | |||
+ | If you have any questions, be sure to ask me about them. | ||
+ | |||
+ | ============================================================================== | ||
+ | </ | ||
+ | ====== BURSTING YOUR 128: THE FASTLOAD BURST COMMAND ====== | ||
+ | < | ||
+ | by Craig Bruce < | ||
+ | |||
+ | 1. INTRODUCTION | ||
+ | |||
+ | This article discusses the well-unknown Fastload command of the 1571 and 1581 | ||
+ | disk drive Burst Command Instruction Set. If you look in the back of your '71 | ||
+ | (or '81 I presume) disk drive manual, you will find that the information given | ||
+ | about the Fastload utility is not exactly abundant. | ||
+ | |||
+ | The Fastload command was intended to load program files into memory for | ||
+ | execution, but it can be used just as well for reading through sequential | ||
+ | files that would be much too large to load into a single bank of internal | ||
+ | memory. | ||
+ | |||
+ | To make use of the Fastload burst command, I implement a word counting utility | ||
+ | that will count the number of lines, words, and characters in a text file on a | ||
+ | 1571 or 1581 disk drive. | ||
+ | regular sequential file accessing through the kernel and DOS is that the | ||
+ | Fastload operates about 3.5 times faster on both drives. | ||
+ | |||
+ | 2. WORD COUNTING UTILITY | ||
+ | |||
+ | To use the word counting program, LOAD and RUN it like a regular BASIC | ||
+ | program. | ||
+ | device number 8, or put a one character prefix and a ":" | ||
+ | device. | ||
+ | of valid names: | ||
+ | |||
+ | . filename | ||
+ | . b: | ||
+ | . a: | ||
+ | |||
+ | The file must be on either a 1571 or 1581 disk drive; the program will not | ||
+ | work with non-burst devices. | ||
+ | files, since the Fastload command can be told not to worry about the file | ||
+ | type. | ||
+ | |||
+ | I use the same definition of a word as the Unix " | ||
+ | of characters delimited by whitespace, where whitespace is defined to be | ||
+ | SPACE, TAB, and NEWLINE (Carriage Return) characters. | ||
+ | I simply count the number of NEWLINEs. | ||
+ | end with a NEWLINE character, then the count will be one line short. | ||
+ | the same as the Unix wc command too. A proper text file should have its last | ||
+ | line end with a NEWLINE character. | ||
+ | |||
+ | On my JiffyDOS-ified 1571 and 1581, I am able to achieve a word counting speed | ||
+ | of 5,400 chars/sec and 6,670 chars/sec, respectively. | ||
+ | of a difference JiffyDOS makes, but I am not willing to rip out the ROMs to | ||
+ | check. | ||
+ | |||
+ | 3. BURST READ LIBRARY | ||
+ | |||
+ | This section presents the burst reading library that you can incorporate into | ||
+ | your own programs and describes how the burst commands work. The library has | ||
+ | three calls: | ||
+ | |||
+ | . burstOpen | ||
+ | . burstRead | ||
+ | . burstClose () | ||
+ | |||
+ | I define three common storage variables for using this package: " | ||
+ | " | ||
+ | data read in from the disk drive is stored before processing, and is located | ||
+ | at $0B00. | ||
+ | returned from the burst command system. | ||
+ | when the end of file has been encountered. | ||
+ | of data bytes available in " | ||
+ | value will be somewhere between 1 and 254. A full sector contains 254 bytes | ||
+ | of data and two bytes of control information. | ||
+ | |||
+ | " | ||
+ | respectively. | ||
+ | the data buffer between calls, if you wish. For reasons not completely | ||
+ | understood, interrupts must be disabled for the entire course of burst reading | ||
+ | a file. I suspect this is because the IRQ service routine reads the interrupt | ||
+ | mask register of CIA#1, thus clearing the SerialDataReady flag that the burst | ||
+ | read routine waits for. Anyway, the open routine does a SEI and the close | ||
+ | routine does a CLI, so you don't have to do this yourself. | ||
+ | |||
+ | If an error occurs during the exection of one of these routines, it will | ||
+ | return with the carry flag set and with the error code in the .A register | ||
+ | (same as the kernel (yes, I know that Commodore likes to call it the | ||
+ | " | ||
+ | code 10 means that the device is not a burst device, and error codes 16 to 31 | ||
+ | correspond to the burst controller status codes 0-15. If no error occurs, the | ||
+ | routines return with the carry flag clear, of course. | ||
+ | |||
+ | Only one file may be open at a time for Fastloading, | ||
+ | the disk drive and the entire serial bus. Even regular files cannot be | ||
+ | accessed while a fastload is in progress. | ||
+ | all file processing applications, | ||
+ | into memory (like for a text editor) and for summarization operations (like | ||
+ | word counting). | ||
+ | in context when it is called. | ||
+ | |||
+ | 3.1. BURST OPEN | ||
+ | |||
+ | The way that a burst command is given is to give a magical incantation over | ||
+ | the command channel to the disk drive. | ||
+ | serial bus calls (LISTN, SECND, CIOUT, and UNLSN) or use the OPEN and CHROUT | ||
+ | calls. | ||
+ | |||
+ | The burst command format for Fastload is given in the back of your drive | ||
+ | manual: | ||
+ | |||
+ | . BYTE \ bit: 7 | ||
+ | . -------+--------+-----+-----+-----+-----+-----+-----+-----+------- | ||
+ | . | ||
+ | . | ||
+ | . | ||
+ | . 3 - ?? | < | ||
+ | . -------+--------------------------------------------------+------- | ||
+ | |||
+ | where " | ||
+ | then only program (PRG) files can be loaded, and if it is ' | ||
+ | (SEQ) files can be loaded as well. The package automatically sets this flag | ||
+ | for you. Note that you don't have to do an Inquire Disk or Query Disk Format | ||
+ | in order to use this command like you have to do with the block reading and | ||
+ | writing commands. | ||
+ | |||
+ | If you want to try giving the incantation yourself, enter: | ||
+ | |||
+ | OPEN1, | ||
+ | |||
+ | (where " | ||
+ | 128 and your disk drive will spring to life and wait for you to read the file | ||
+ | data. You can't read the data from BASIC, so to cancel the command: | ||
+ | |||
+ | CLOSE1 | ||
+ | |||
+ | The " | ||
+ | at the start of the " | ||
+ | register, and the device number to load the file from in the .A register. | ||
+ | burst command header and the filename are sent to the disk drive as described | ||
+ | above. | ||
+ | |||
+ | The open command also reads the first sector of the file, for two reasons. | ||
+ | First, the status byte returned for the first sector has special meaning. | ||
+ | Status code $02 means "file not found" | ||
+ | kernel error code. Second, and most important, there is a bizarre feature | ||
+ | (read: " | ||
+ | block long, then the number of bytes reported for the block length is two | ||
+ | bytes too short. | ||
+ | the number of actual bytes in the sector: | ||
+ | |||
+ | . Actual | ||
+ | . ---------+---------+---------+---------+---------+---------+ | ||
+ | . Reported | 2 | 1 | 0 | | ||
+ | |||
+ | This is where I ran into problems with Zed-128; the logic of my program | ||
+ | screwed up on a zero length. | ||
+ | bug is bizarre because it only happens if the first sector is the only sector | ||
+ | in the file. The reported length for all subsequent sectors is correct. | ||
+ | also that 255 is reported for lengths of both 1 and 0. This is because there | ||
+ | is no actual zero length for Commodore files. | ||
+ | then immediately CLOSE1 you get a file with one carriage return character in | ||
+ | it. | ||
+ | |||
+ | The open routine calls the read routine to read a sector and if it was the | ||
+ | only sector of the file, the two additional bytes are burst-read and put into | ||
+ | the data buffer. | ||
+ | the correct count of 1. Also interesting in this case is that when the | ||
+ | 1571/81 reports the 255, it actually transfers 255 bytes of data, all of which | ||
+ | are bogus. | ||
+ | 1571 in this respect. | ||
+ | |||
+ | The open routine also executes a SEI for reasons discussed above. | ||
+ | information returned by the open call is the same as what is returned for the | ||
+ | " | ||
+ | |||
+ | 3.2. BURST READ | ||
+ | |||
+ | Once the Fastload command is started, the drive starts waiting to transfer the | ||
+ | data to you. The transfer occurs sector by sector, with each sector preceeded | ||
+ | by a burst status byte. The data is transferred using the shift register of | ||
+ | CIA#1 and the handshaking is done using the Slow Serial Clock line of CIA#2. | ||
+ | To receive a byte, you toggle the Slow Serial Clock line and wait for the | ||
+ | Shift Register Ready signal from CIA#1 and then read the data value from the | ||
+ | shift data register. | ||
+ | |||
+ | One of the clock registers in the CIA in the 1571/81 is used as the baud rate | ||
+ | generator for the serial line. I think that it uses a delay of 4 microseconds | ||
+ | per bit, which gives a baud rate of 250,000 (31.25K/ | ||
+ | experimentation, | ||
+ | 200,000 (25K/ | ||
+ | rate cannot actually be achieved because of electrical problems that I don't | ||
+ | understand. | ||
+ | the surface of the disk at around 30K/sec and if the serial bus were fast | ||
+ | enough, it could be transferred to the computer as it is being read. Some | ||
+ | things would be so much more convenient if whomever created the universe had | ||
+ | thought to make light go just a little bit faster. | ||
+ | |||
+ | The burst handshaking protocol slows the maximum transfer rate down to about | ||
+ | 16K/ | ||
+ | just transferring data, so the actual burst throughput is lower than that: | ||
+ | about 5.4K/sec with my JiffyDOS-ified 1571 and about 7K/sec with a 1581. Note | ||
+ | that you can probably increase your 1571's burst performance a bit by setting | ||
+ | it to use a sector interleave factor of 4, using the " | ||
+ | command. | ||
+ | |||
+ | All of the sectors before the last one will contain 254 bytes of data and the | ||
+ | last one will contain a specified number of bytes, from 1 to 254. The status | ||
+ | code returned for the last sector is value $1F. In this case, an additional | ||
+ | byte is sent before the data bytes that tells how many data bytes there will | ||
+ | be. This is the value that is bugged for one-sector files as described in the | ||
+ | last section. | ||
+ | transferred for a sector: | ||
+ | |||
+ | . REGULAR SECTOR | ||
+ | . | ||
+ | . 0 | Burst Status Byte | 0 | Burst Status = $1F | | ||
+ | . | ||
+ | . 1 | | ||
+ | . ... + 254 Data Bytes | ||
+ | . 254 | | ||
+ | . | ||
+ | . N+1 | | | ||
+ | . | ||
+ | |||
+ | If a sector returns a burst status code other than 0 (ok) or $1F (end), then | ||
+ | an error has occurred and the disk drive aborts the transfer, closes the burst | ||
+ | connection, and starts the drive light blinking. | ||
+ | |||
+ | The " | ||
+ | opened file into the " | ||
+ | " | ||
+ | returns with the carry flag set and the translated burst error code in the .A | ||
+ | register (same as burstOpen). | ||
+ | read, this routine returns with a value of $1F in the " | ||
+ | |||
+ | 3.3. BURST CLOSE | ||
+ | |||
+ | After reading the last data byte of the last sector of the file, the burst | ||
+ | connection and is closed automatically by the disk drive. | ||
+ | routine is not necessary for communication with the disk drive, but is | ||
+ | provided for completeness and to clear the interrupt disable bit (CLI) that | ||
+ | the open routine set to prevent interrupts while burst reading. | ||
+ | |||
+ | 3.4. PACKAGE USAGE | ||
+ | |||
+ | The following pseudo-code outlines how a user program is expected to use the | ||
+ | burst reading package: | ||
+ | |||
+ | . jsr put_filename_into_burstBuf | ||
+ | . ldx # | ||
+ | . lda # | ||
+ | . jsr burstOpen | ||
+ | . bcs reportError | ||
+ | . L1: jsr process_burstBuf_data | ||
+ | . lda burstStatus | ||
+ | . cmp #$1f | ||
+ | . beq L2 | ||
+ | . jsr burstRead | ||
+ | . bcc L1 | ||
+ | . jsr reportError | ||
+ | . L2: jsr burstClose | ||
+ | |||
+ | 4. IMPLEMENTATION | ||
+ | |||
+ | This section discusses the code that implements the word counting program. | ||
+ | is here in a special form; each code line is preceeded by a few special | ||
+ | characters and the line number. | ||
+ | to easily extract the assembler code from the rest of this magazine (and all | ||
+ | of my ugly comments). | ||
+ | following command line (substitute filenames as appropriate): | ||
+ | |||
+ | grep ' | ||
+ | |||
+ | |||
+ | .%001! | ||
+ | .%002! | ||
+ | .%003! | ||
+ | |||
+ | The code is written for the Buddy assembler and here are a few setup | ||
+ | directives. | ||
+ | |||
+ | .%004! | ||
+ | .%005! | ||
+ | .%006! | ||
+ | .%007! | ||
+ | .%008! | ||
+ | .%009! | ||
+ | |||
+ | This is what the "10 sys 7200" in BASIC looks like. It is here so this | ||
+ | program can be executed with BASIC RUN command. | ||
+ | |||
+ | .%010! | ||
+ | .%011! | ||
+ | .%012! | ||
+ | .%013! | ||
+ | .%014! | ||
+ | .%015! | ||
+ | .%016! | ||
+ | .%017! | ||
+ | .%018! | ||
+ | .%019! | ||
+ | .%020! | ||
+ | .%021! | ||
+ | .%022! | ||
+ | .%023! | ||
+ | .%024! | ||
+ | .%025! | ||
+ | .%026! | ||
+ | |||
+ | " | ||
+ | " | ||
+ | |||
+ | .%027! | ||
+ | .%028! | ||
+ | |||
+ | " | ||
+ | data becoming available in the shift register (" | ||
+ | the Slow serial bus clock line that is used for handshaking on the Fast bus. | ||
+ | |||
+ | .%029! | ||
+ | .%030! | ||
+ | .%031! | ||
+ | .%032! | ||
+ | .%033! | ||
+ | .%034! | ||
+ | .%035! | ||
+ | .%036! | ||
+ | .%037! | ||
+ | .%038! | ||
+ | |||
+ | This is the error code value this package returns if it detects that a device | ||
+ | is not Fast. | ||
+ | |||
+ | .%039! | ||
+ | .%040! | ||
+ | .%041! | ||
+ | .%042! | ||
+ | .%043! | ||
+ | |||
+ | Set up for a burst open: clear the Fast flag and the device not present flag. | ||
+ | |||
+ | .%044! | ||
+ | .%045! | ||
+ | .%046! | ||
+ | .%047! | ||
+ | .%048! | ||
+ | .%049! | ||
+ | .%050! | ||
+ | .%051! | ||
+ | |||
+ | Command the disk device to Listen. | ||
+ | (bit 7 of ioStatus). | ||
+ | |||
+ | .%052! | ||
+ | .%053! | ||
+ | .%054! | ||
+ | .%055! | ||
+ | .%056! | ||
+ | .%057! | ||
+ | .%058! | ||
+ | .%059! | ||
+ | .%060! | ||
+ | .%061! | ||
+ | |||
+ | Tell disk device to listen on the command channel (channel #15). | ||
+ | |||
+ | .%062! | ||
+ | .%063! | ||
+ | |||
+ | Send the " | ||
+ | |||
+ | .%064! | ||
+ | .%065! | ||
+ | .%066! | ||
+ | .%067! | ||
+ | .%068! | ||
+ | .%069! | ||
+ | .%070! | ||
+ | .%071! | ||
+ | |||
+ | Send the filename. | ||
+ | |||
+ | .%072! | ||
+ | .%073! | ||
+ | .%074! | ||
+ | .%075! | ||
+ | .%076! | ||
+ | .%077! | ||
+ | |||
+ | Finish sending the burst command and make sure the device is Fast. | ||
+ | |||
+ | .%078! | ||
+ | .%079! | ||
+ | .%080! | ||
+ | .%081! | ||
+ | .%082! | ||
+ | .%083! | ||
+ | .%084! | ||
+ | .%085! | ||
+ | |||
+ | Disable interrupts. | ||
+ | |||
+ | .%086! | ||
+ | |||
+ | Prepare to receive data and signal the disk drive to start sending (by | ||
+ | toggling the slow serial Clock line). | ||
+ | |||
+ | .%087! | ||
+ | .%088! | ||
+ | .%089! | ||
+ | .%090! | ||
+ | .%091! | ||
+ | .%092! | ||
+ | |||
+ | Read the first sector of the file. | ||
+ | |||
+ | .%093! | ||
+ | |||
+ | Check for errors. | ||
+ | kernel equivalent. | ||
+ | |||
+ | .%094! | ||
+ | .%095! | ||
+ | .%096! | ||
+ | .%097! | ||
+ | .%098! | ||
+ | .%099! | ||
+ | .%100! | ||
+ | .%101! | ||
+ | |||
+ | Check if this is a one-block file. | ||
+ | |||
+ | .%102! | ||
+ | .%103! | ||
+ | .%104! | ||
+ | .%105! | ||
+ | .%106! | ||
+ | .%107! | ||
+ | |||
+ | If so, we have to read the two bytes that the disk drive forgot to tell us | ||
+ | about. | ||
+ | the clock, and read the shift data register. | ||
+ | data register after sending the acknowledge signal to the disk drive because I | ||
+ | am running with interrupts disabled and it could not possibly send the next | ||
+ | byte before I pick up the current one. We wouldn' | ||
+ | while doing this, though. | ||
+ | |||
+ | .%108! | ||
+ | .%109! | ||
+ | .%110! | ||
+ | .%111! | ||
+ | .%112! | ||
+ | .%113! | ||
+ | .%114! | ||
+ | .%115! | ||
+ | .%116! | ||
+ | .%117! | ||
+ | .%118! | ||
+ | .%119! | ||
+ | |||
+ | Store the updated byte count and exit. | ||
+ | |||
+ | .%120! | ||
+ | .%121! | ||
+ | .%122! | ||
+ | .%123! | ||
+ | |||
+ | In the event of a burst error, re-enable the interrupts since the user might | ||
+ | not call the burstClose routine. | ||
+ | |||
+ | .%124! | ||
+ | .%125! | ||
+ | .%126! | ||
+ | .%127! | ||
+ | .%128! | ||
+ | .%129! | ||
+ | |||
+ | Read the next sector of the file. | ||
+ | |||
+ | .%130! | ||
+ | |||
+ | Wait for the status byte to arrive. | ||
+ | |||
+ | .%131! | ||
+ | .%132! | ||
+ | .%133! | ||
+ | |||
+ | Toggle clock line for acknowledge. | ||
+ | |||
+ | .%134! | ||
+ | .%135! | ||
+ | .%136! | ||
+ | |||
+ | Get status byte and check. | ||
+ | occurred. | ||
+ | |||
+ | .%137! | ||
+ | .%138! | ||
+ | .%139! | ||
+ | .%140! | ||
+ | .%141! | ||
+ | .%142! | ||
+ | .%143! | ||
+ | |||
+ | If status byte is $1F, then get the next byte, which tells how many data bytes | ||
+ | are to follow. | ||
+ | |||
+ | .%144! | ||
+ | .%145! | ||
+ | .%146! | ||
+ | .%147! | ||
+ | .%148! | ||
+ | .%149! | ||
+ | .%150! | ||
+ | .%151! | ||
+ | .%152! | ||
+ | .%153! | ||
+ | .%154! | ||
+ | .%155! | ||
+ | |||
+ | Read the data bytes and put them into the burst buffer. | ||
+ | value is computed before receiving the data for a little extra zip. I haven' | ||
+ | experimented with this, but you might be able to toggle the clock line before | ||
+ | receiving the data (however, probably not for the first byte). | ||
+ | |||
+ | .%156! | ||
+ | .%157! | ||
+ | .%158! | ||
+ | .%159! | ||
+ | .%160! | ||
+ | .%161! | ||
+ | .%162! | ||
+ | .%163! | ||
+ | .%164! | ||
+ | .%165! | ||
+ | .%166! | ||
+ | .%167! | ||
+ | .%168! | ||
+ | .%169! | ||
+ | .%170! | ||
+ | .%171! | ||
+ | |||
+ | Close the burst package: simply CLI. | ||
+ | |||
+ | .%172! | ||
+ | .%173! | ||
+ | .%174! | ||
+ | .%175! | ||
+ | .%176! | ||
+ | .%177! | ||
+ | .%178! | ||
+ | |||
+ | This is the word counting application code. | ||
+ | |||
+ | .%179! | ||
+ | .%180! | ||
+ | .%181! | ||
+ | .%182! | ||
+ | .%183! | ||
+ | |||
+ | The " | ||
+ | currently in a word or not. The Lines, Words, and Bytes are 24-bit counters. | ||
+ | |||
+ | .%184! | ||
+ | .%185! | ||
+ | .%186! | ||
+ | .%187! | ||
+ | .%188! | ||
+ | .%189! | ||
+ | |||
+ | Put the kernel ROM and I/O space into context then initialize the counting | ||
+ | variables. | ||
+ | |||
+ | .%190! | ||
+ | .%191! | ||
+ | .%192! | ||
+ | |||
+ | Follow the burst reading procedure outline. | ||
+ | |||
+ | .%193! | ||
+ | .%194! | ||
+ | .%195! | ||
+ | .%196! | ||
+ | .%197! | ||
+ | .%198! | ||
+ | .%199! | ||
+ | .%200! | ||
+ | .%201! | ||
+ | .%202! | ||
+ | .%203! | ||
+ | .%204! | ||
+ | .%205! | ||
+ | |||
+ | Report the numbers of lines, words, and characters and then exit. | ||
+ | |||
+ | .%206! | ||
+ | .%207! | ||
+ | .%208! | ||
+ | |||
+ | Initialize the variables. | ||
+ | |||
+ | .%209! | ||
+ | .%210! | ||
+ | .%211! | ||
+ | .%212! | ||
+ | .%213! | ||
+ | .%214! | ||
+ | .%215! | ||
+ | .%216! | ||
+ | .%217! | ||
+ | |||
+ | Get the device and filename from the user. Returns parameters suitable for | ||
+ | passing to burstOpen. | ||
+ | |||
+ | .%218! | ||
+ | |||
+ | Display the prompt. | ||
+ | |||
+ | .%219! | ||
+ | .%220! | ||
+ | .%221! | ||
+ | .%222! | ||
+ | .%223! | ||
+ | .%224! | ||
+ | |||
+ | Get the input line from the user. | ||
+ | |||
+ | .%225! | ||
+ | .%226! | ||
+ | .%227! | ||
+ | .%228! | ||
+ | .%229! | ||
+ | .%230! | ||
+ | .%231! | ||
+ | .%232! | ||
+ | |||
+ | Extract the device number from the start of the input line. If it is not | ||
+ | there, assume device number 8. | ||
+ | |||
+ | .%233! | ||
+ | .%234! | ||
+ | .%235! | ||
+ | .%236! | ||
+ | .%237! | ||
+ | .%238! | ||
+ | .%239! | ||
+ | .%240! | ||
+ | .%241! | ||
+ | .%242! | ||
+ | |||
+ | If a device name was present, then we have to move the rest of the filename | ||
+ | back over it now that we've extracted it. | ||
+ | |||
+ | .%243! | ||
+ | .%244! | ||
+ | .%245! | ||
+ | .%246! | ||
+ | .%247! | ||
+ | .%248! | ||
+ | .%249! | ||
+ | .%250! | ||
+ | .%251! | ||
+ | .%252! | ||
+ | .%253! | ||
+ | .%254! | ||
+ | .%255! | ||
+ | .%256! | ||
+ | .%257! | ||
+ | .%258! | ||
+ | .%259! | ||
+ | .%260! | ||
+ | |||
+ | Scan the burst buffer after reading a sector into it. | ||
+ | |||
+ | .%261! | ||
+ | .%262! | ||
+ | .%263! | ||
+ | .%264! | ||
+ | .%265! | ||
+ | .%266! | ||
+ | .%267! | ||
+ | .%268! | ||
+ | .%269! | ||
+ | .%270! | ||
+ | |||
+ | If the current character is a carriage return, then increment the line count. | ||
+ | |||
+ | .%271! | ||
+ | .%272! | ||
+ | .%273! | ||
+ | .%274! | ||
+ | .%275! | ||
+ | |||
+ | If the character is a TAB, SPACE, or a RETURN, then it is a Delimiter; | ||
+ | otherwise, it is considered a Letter. | ||
+ | |||
+ | .%276! | ||
+ | .%277! | ||
+ | .%278! | ||
+ | .%279! | ||
+ | .%280! | ||
+ | .%281! | ||
+ | .%282! | ||
+ | .%283! | ||
+ | .%284! | ||
+ | .%285! | ||
+ | |||
+ | If the character is a Letter and the previous one was a Delimiter, then | ||
+ | increment the word count. | ||
+ | |||
+ | .%286! | ||
+ | .%287! | ||
+ | .%288! | ||
+ | .%289! | ||
+ | .%290! | ||
+ | .%291! | ||
+ | .%292! | ||
+ | .%293! | ||
+ | .%294! | ||
+ | .%295! | ||
+ | .%296! | ||
+ | .%297! | ||
+ | .%298! | ||
+ | .%299! | ||
+ | .%300! | ||
+ | .%301! | ||
+ | .%302! | ||
+ | |||
+ | Add the number of bytes in the burst buffer to the total byte count for the | ||
+ | file. | ||
+ | |||
+ | .%303! | ||
+ | .%304! | ||
+ | .%305! | ||
+ | .%306! | ||
+ | .%307! | ||
+ | .%308! | ||
+ | .%309! | ||
+ | .%310! | ||
+ | .%311! | ||
+ | .%312! | ||
+ | .%313! | ||
+ | |||
+ | Report the number of lines, words, and bytes read. Uses a " | ||
+ | scheme. | ||
+ | |||
+ | .%314! | ||
+ | .%315! | ||
+ | .%316! | ||
+ | .%317! | ||
+ | .%318! | ||
+ | .%319! | ||
+ | .%320! | ||
+ | .%321! | ||
+ | .%322! | ||
+ | .%323! | ||
+ | .%324! | ||
+ | .%325! | ||
+ | .%326! | ||
+ | .%327! | ||
+ | .%328! | ||
+ | .%329! | ||
+ | .%330! | ||
+ | .%331! | ||
+ | .%332! | ||
+ | .%333! | ||
+ | .%334! | ||
+ | .%335! | ||
+ | .%336! | ||
+ | .%337! | ||
+ | .%338! | ||
+ | .%339! | ||
+ | .%340! | ||
+ | .%341! | ||
+ | .%342! | ||
+ | .%343! | ||
+ | .%344! | ||
+ | .%345! | ||
+ | .%346! | ||
+ | .%347! | ||
+ | |||
+ | Reports the error number given in the .A register. | ||
+ | returned from a burst routine. | ||
+ | |||
+ | .%348! | ||
+ | .%349! | ||
+ | .%350! | ||
+ | .%351! | ||
+ | .%352! | ||
+ | .%353! | ||
+ | .%354! | ||
+ | .%355! | ||
+ | .%356! | ||
+ | .%357! | ||
+ | .%358! | ||
+ | .%359! | ||
+ | .%360! | ||
+ | .%361! | ||
+ | .%362! | ||
+ | .%363! | ||
+ | .%364! | ||
+ | .%365! | ||
+ | .%366! | ||
+ | .%367! | ||
+ | .%368! | ||
+ | .%369! | ||
+ | |||
+ | Routine to print out the 24-bit number given in .AYX. | ||
+ | |||
+ | .%370! | ||
+ | .%371! | ||
+ | .%372! | ||
+ | .%373! | ||
+ | .%374! | ||
+ | .%375! | ||
+ | |||
+ | Initialize binary and BCD (Binary Coded Decimal) representations of number. | ||
+ | |||
+ | .%376! | ||
+ | .%377! | ||
+ | .%378! | ||
+ | .%379! | ||
+ | .%380! | ||
+ | .%381! | ||
+ | .%382! | ||
+ | .%383! | ||
+ | .%384! | ||
+ | .%385! | ||
+ | .%386! | ||
+ | .%387! | ||
+ | |||
+ | Rotate each bit out of the binary number and then multiply the BCD number by | ||
+ | two and add the bit in. Effectively, | ||
+ | binary number and into the BCD representation of the number. | ||
+ | |||
+ | .%388! | ||
+ | .%389! | ||
+ | .%390! | ||
+ | .%391! | ||
+ | .%392! | ||
+ | .%393! | ||
+ | .%394! | ||
+ | .%395! | ||
+ | .%396! | ||
+ | .%397! | ||
+ | .%398! | ||
+ | .%399! | ||
+ | .%400! | ||
+ | |||
+ | Take the BCD bytes and spit out the two digits they contain. | ||
+ | |||
+ | .%401! | ||
+ | .%402! | ||
+ | .%403! | ||
+ | .%404! | ||
+ | .%405! | ||
+ | .%406! | ||
+ | .%407! | ||
+ | .%408! | ||
+ | .%409! | ||
+ | .%410! | ||
+ | .%411! | ||
+ | .%412! | ||
+ | .%413! | ||
+ | .%414! | ||
+ | .%415! | ||
+ | .%416! | ||
+ | .%417! | ||
+ | .%418! | ||
+ | .%419! | ||
+ | |||
+ | Print out the individual digits of the number. | ||
+ | and all digits so far have been zero, then don't output anything, unless it is | ||
+ | the last digit of the number. | ||
+ | |||
+ | .%420! | ||
+ | .%421! | ||
+ | .%422! | ||
+ | .%423! | ||
+ | .%424! | ||
+ | .%425! | ||
+ | .%426! | ||
+ | .%427! | ||
+ | .%428! | ||
+ | .%429! | ||
+ | .%430! | ||
+ | |||
+ | 5. UUENCODED PROGRAM | ||
+ | |||
+ | Here is the binary executable in uuencoded form. The CRC32 of it is | ||
+ | 3676144922. | ||
+ | |||
+ | begin 640 wc | ||
+ | M`1P<' | ||
+ | M' | ||
+ | MH`" | ||
+ | M' | ||
+ | MYX3_&& | ||
+ | M\/ | ||
+ | M8%@88*D.C0# | ||
+ | M6QY@J0" | ||
+ | MLZD(X`*0' | ||
+ | M24Q%3D%-12!)3B!& | ||
+ | M.D9)3$5.04U%+" | ||
+ | M($E3($9/ | ||
+ | MT`+F!< | ||
+ | MQ1BE" | ||
+ | M' | ||
+ | M`+V\' | ||
+ | M889BH@.I`)5CRA# | ||
+ | D!!_HX`20]F!(2DI*2B`/' | ||
+ | ` | ||
+ | end | ||
+ | |||
+ | 6. REFERENCES | ||
+ | |||
+ | [1] Commodore Business Machines, _Commodore_1571_Disk_Drive_User' | ||
+ | CBM, 1985. | ||
+ | |||
+ | [2] Rainer Ellinger, _1571_Internals_, | ||
+ | |||
+ | =============================================================================== | ||
+ | </ | ||
+ | ====== Next Issue: (hopefully!) ====== | ||
+ | < | ||
+ | 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 using the KERNAL routines we've learned. | ||
+ | |||
+ | 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 char position. What then | ||
+ | is this FLI and how it is done ? | ||
+ | |||
+ | The 1351 Mouse Demystified | ||
+ | |||
+ | An indepth look at how the 1351 mouse operates and how to access it within | ||
+ | your own ML programs. | ||
+ | is also supplied. | ||
+ | |||
+ | LITTLE RED READER: MS-DOS file reader for the 128 and 1571/81 drives. | ||
+ | |||
+ | This article will present a package that reads MS-DOS files and the root | ||
+ | directory of MS-DOS disks. | ||
+ | package introduced in Hacking Issue #2 to allow large files to be read in. | ||
+ | The application-level code hasn't been finalized yet, but it will probably use | ||
+ | a menu-oriented full-screen display and will read and translate MS-DOS and | ||
+ | Commodore files. | ||
+ | ============================================================================= | ||
+ | END of Commodore Hacking Issue 3. | ||
+ | ============================================================================= | ||
+ | </ |
magazines/chacking3.txt · Last modified: 2015-04-17 04:34 by 127.0.0.1