magazines:chacking6
no way to compare when less than two revisions
Differences
This shows you the differences between two versions of the page.
— | magazines:chacking6 [2015-04-17 04:34] (current) – created - external edit 127.0.0.1 | ||
---|---|---|---|
Line 1: | Line 1: | ||
+ | < | ||
+ | ######## | ||
+ | ################## | ||
+ | ###### | ||
+ | ##### | ||
+ | ##### #### #### ## ##### #### | ||
+ | ##### ## ## #### ## ## | ||
+ | ##### | ||
+ | ##### ## ## ######## | ||
+ | ##### #### #### #### #### ##### #### | ||
+ | ##### ## | ||
+ | ###### | ||
+ | ################## | ||
+ | ######## | ||
+ | ------------------------------------------------------------------------------ | ||
+ | Editor' | ||
+ | by Craig Taylor | ||
+ | |||
+ | School, 2 jobs, a play, and other work has seriously restricted the amount | ||
+ | of time that I am able to devote to Commodore Hacking. What I am looking at | ||
+ | doing now is to stop writing articles for Commodore Hacking and hoping that | ||
+ | I'll still have enough time to write these notes, and organize and pull all | ||
+ | the other articles by other contributors together. The two articles I | ||
+ | had hoped to include in this issue : the one about multi-tasking and | ||
+ | about the 1351 have again been delayed. I've decided to go ahead and | ||
+ | release issue 6 and hopefully will have them in the next issue. | ||
+ | (Remember: You get what you pay for.. *smile*) | ||
+ | |||
+ | As always, Commodore Hacking is constantly looking for articles, notes, | ||
+ | short little programs, what-not on any side of the Commodore 64 and 128 - | ||
+ | primarily on the technical or hardware side. If you think you have something | ||
+ | in mind, or already written then feel free to drop me a line letting me | ||
+ | know. | ||
+ | |||
+ | In regards to several queries recently about reprinting individual articles | ||
+ | that have appeared in Commodore Hacking. You may reprint Commodore Hacking | ||
+ | and redistribute in whole, freely. For use of individual articles you _must_ | ||
+ | contanct the individual author as they still retain rights to it. Please see | ||
+ | the legal notice / mumbo below. | ||
+ | |||
+ | I recently recieved some mail from wolfgang@halcyon regarding a disk | ||
+ | magazine that he was in the process of starting and he has written the | ||
+ | following preview of a disk magazine that looks to be exciting: | ||
+ | |||
+ | " | ||
+ | european demo scenes, will soon be available for download at a | ||
+ | site/BBS near you! With articles on everything from coding to | ||
+ | instrument synthesis to art, _Scenery_ will be the definitive word | ||
+ | when it comes to creating a demo, or simply coding in general. | ||
+ | Articles are being written by some of the top names in the scene, | ||
+ | and promise to help everybody from the Basic Baby to the ML Mogul! | ||
+ | Set to be released mid-August, _Scenery_ will hopefully be a worthy | ||
+ | edition to the likes of Coder' | ||
+ | the magazine available on various Internet sites, so look for it. We | ||
+ | are always on the lookout for art, music, and coding talent, and if | ||
+ | you'd be interested in submitting an article for publication, | ||
+ | simply have a question or comment, please mail me at | ||
+ | ' | ||
+ | |||
+ | ================================================================================ | ||
+ | |||
+ | Please note that this issue and prior ones are available via anonymous FTP | ||
+ | from ccosun.caltech.edu under pub/ | ||
+ | which documentation can be obtained by sending mail to | ||
+ | " | ||
+ | line " | ||
+ | |||
+ | ================================================================================ | ||
+ | |||
+ | NOTICE: Permission is granted to re-distrubte this " | ||
+ | whole, freely for non-profit use. However, please contact individual | ||
+ | authors for permission to publish or re-distribute articles seperately. | ||
+ | A charge of no greather than 5 US dollars or equivlent may be charged for | ||
+ | library service / diskette costs for this " | ||
+ | |||
+ | ================================================================================ | ||
+ | </ | ||
+ | ===== In This Issue: ===== | ||
+ | < | ||
+ | |||
+ | DYCP - Horizontal Scrolling | ||
+ | |||
+ | DYCP - is a name for a horizontal scroller, where characters go smoothly | ||
+ | up and down during their voyage from right to left. One possibility is a | ||
+ | scroll with 8 characters - one character per sprite, but a real demo coder | ||
+ | won't be satisfied with that. | ||
+ | |||
+ | Opening the borders | ||
+ | |||
+ | VIC has many features and transparent borders are one of them. You can not | ||
+ | make characters appear in the border, but sprites are displayed in the | ||
+ | border too. | ||
+ | |||
+ | A Heavy Duty Power supply for the C-64 | ||
+ | |||
+ | This article describes how to build a heavier duty power supply for your | ||
+ | Commodore 64 computer and includes a full schematic in GeoPaint format. | ||
+ | |||
+ | LZW Compression | ||
+ | |||
+ | LZW is perhaps the most widely used form of data compression today. It | ||
+ | is simple to implement and achieves very decent compression at a fairly | ||
+ | quick pace. LZW is used in PKZIP (shrink), | ||
+ | and unix's compress. This article will attempt to explain how the | ||
+ | compression works with a short example and 6502 source code in Buddy | ||
+ | format. | ||
+ | |||
+ | THREE-KEY ROLLOVER for the C-128 and C-64. | ||
+ | |||
+ | This article examines how a three-key rollover mechanism works for the | ||
+ | keyboards of the C=128 and C=64 and will present Kernal-wedge | ||
+ | implementations for both machines. Webster' | ||
+ | tell you that this means that the machine will act sensibly if you are | ||
+ | holding down one key and then press another without releasing the first. | ||
+ | This will be useful to fast touch typers. | ||
+ | |||
+ | ================================================================================ | ||
+ | </ | ||
+ | ===== The Demo Corner: DYCP - Horizontal Scrolling ===== | ||
+ | < | ||
+ | by Pasi ' | ||
+ | Written: 16-May-91 Translation 02-Jun-92 | ||
+ | |||
+ | DYCP - too many sprites !? | ||
+ | -------------------------- | ||
+ | |||
+ | DYCP - Different Y Character Position - is a name for a horizontal scroller, | ||
+ | where characters go smoothly up and down during their voyage from right to | ||
+ | left. One possibility is a scroll with 8 characters - one character in each | ||
+ | sprite, but a real demo coder won't be satisfied with that. | ||
+ | |||
+ | Demo coders thought that it looks good to make the scrolling text change its | ||
+ | vertical position in the same time it proceeded from the right side of the | ||
+ | screen to the left. The only problem is that there is only eight sprites | ||
+ | and that is not even nearly enough to satisfy the requirements needed for | ||
+ | great look. So the only way is to use screen and somehow plot the text in | ||
+ | graphics, because character columns can not be scrolled individually. | ||
+ | Plotting the characters take absolutely too much time, because you have to | ||
+ | handle each byte seperately and the graphics bitmap must be cleared too. | ||
+ | |||
+ | |||
+ | _Character hack_ | ||
+ | |||
+ | The whole DYCP started using character graphics. You plot six character | ||
+ | rows where the character (screen) codes increase to the right and down. | ||
+ | This area is then used like a small bitmap screen. Each of the text chars | ||
+ | are displayed one byte at a time on each six rows high character columns. | ||
+ | This 240 character positions big piece of screen can be moved horizontally | ||
+ | using the x-scroll register (three lowest bits in $D016) and after eight | ||
+ | pixels you move the text itself, like in any scroll. The screen is of course | ||
+ | reduced to 38 columns wide to hide the jittering on the sides. | ||
+ | |||
+ | A good coder may also change the character sets during the display and | ||
+ | even double the size of the scroll, but because the raster time happens | ||
+ | to go to waste using this technique anyway, that is not very feasible. There | ||
+ | are also other difficulties in this approach, the biggest is the time needed | ||
+ | to clear the display. | ||
+ | |||
+ | |||
+ | _Save characters - and time_ | ||
+ | |||
+ | But why should we move an eight-byte-high character image in a 48-line-high | ||
+ | area, when 16 is really enough ? We can use two characters for the graphics | ||
+ | bitmap and then move this in eight pixel steps up and down. The lowest | ||
+ | three bits of the y-position then gives us the offset where the data must | ||
+ | be plotted inside this graphical region. The two character codes are usually | ||
+ | selected to be consecutive ones so that the image data has also 16 | ||
+ | consecutive bytes. [See picture 1.] | ||
+ | |||
+ | |||
+ | _Demo program might clear things up_ | ||
+ | |||
+ | The demo program is coded using the latter algorithm. The program first | ||
+ | copies the Character ROM to ram, because it is faster to use it from there. | ||
+ | You can easily change the program to use your own character set instead, | ||
+ | if you like. The sinus data for the vertical movement is created of a 1/4 | ||
+ | of a cycle by mirroring it both horizontally and vertically. | ||
+ | |||
+ | Two most time critical parts are clearing the character set and plotting the | ||
+ | new one. Neither of these may happen when VIC is drawing the area where the | ||
+ | scroll is, so there is a slight hurry. Using double buffering technique we | ||
+ | could overcome this limitation, but this is just an example program. For | ||
+ | speed there is CLC only when it is absolutely needed. | ||
+ | |||
+ | The NTSC version is a bit crippled, it only covers 32 columns and thus the | ||
+ | characters seem to appear from thin air. Anyway, the idea should become | ||
+ | clear. | ||
+ | |||
+ | |||
+ | _Want to go to the border ?_ | ||
+ | |||
+ | Some coders are always trying to get all effects ever done using the C64 go | ||
+ | to the border, and even successfully. The easiest way is to use only a region | ||
+ | of 21 pixels high - sprites - and move the text exactly like in characters. | ||
+ | In fact only the different addressing causes the differences in the code. | ||
+ | |||
+ | Eight horizontally expanded sprites will be just enough to fill the side | ||
+ | borders. You can also mix these techiques, but then you have the usual | ||
+ | " | ||
+ | solvable). Unfortunately sprite-dycp is even more slower than char-dycp. | ||
+ | |||
+ | |||
+ | _More movement vertically_ | ||
+ | |||
+ | You might think that using the sprites will restrict the sinus to only | ||
+ | 14 pixels. Not really, the only restriction is that the vertical position | ||
+ | difference between three consequent text character must be less than 14 | ||
+ | pixel lines. Each sprites' | ||
+ | characters residing in that sprite. Line offsets inside the sprites | ||
+ | are then obtained by subtracting the sprite y-coordinate from the character | ||
+ | y-coordinate. Maybe a little hard to follow, but maybe a picture will | ||
+ | clear the situation. [See picture 2.] | ||
+ | |||
+ | Scrolling horizontally is easy. You just have to move sprites like you would | ||
+ | use the character horizontal scroll register and after eight pixels you | ||
+ | reset the sprite positions and scroll the text one position in memory. | ||
+ | And of course, you fetch a new character for the scroll. When we have | ||
+ | different and changing sprite y-coordinates, | ||
+ | a great deal more difficult. However, in this case there is at least two | ||
+ | different ways to do it. | ||
+ | |||
+ | |||
+ | _Stretch the sprites_ | ||
+ | |||
+ | The easiest way is to position all of the sprites where the scroll will | ||
+ | be when it is in its highest position. Then stretch the first and last line | ||
+ | of each sprite so that the 19 sprite lines in the middle will be on the | ||
+ | desired place. Opening the borders now is trivial, because all of the sprites | ||
+ | are present on all of the scan lines and they steal a constant amount of | ||
+ | time. However, we lose two sprite lines. We might not want to use the first | ||
+ | and the last line for graphics, because they are stretched. | ||
+ | [See previous C=Hacking Issues for more information about stretching and | ||
+ | | ||
+ | |||
+ | A more difficult approach is to unroll the routine and let another routine | ||
+ | count the sprites present in each line and then change the time the routine | ||
+ | uses accordingly. In this way you save time during the display for other | ||
+ | effects, like color bars, because stretching will take at least 12 cycles | ||
+ | on each raster line. On the other hand, if the sinus is constant (user is | ||
+ | not allowed to change it), it is usually possible to embedd the count | ||
+ | routine directly to the border opening part of the routine. | ||
+ | |||
+ | |||
+ | _More sprites_ | ||
+ | |||
+ | You don't necassarily need to plot the characters in sprites to have more | ||
+ | than eight characters. Using a sprite multiplexing techiques you can double | ||
+ | or triple the number of sprites available. You can divide the scroll | ||
+ | vertically into several areas and because the y-coordinate of the scroll | ||
+ | is a sinus, there always is a fixed maximum number of sprites in each area. | ||
+ | This number is always smaller than the total number of sprites in the | ||
+ | whole scroll. I won't go into detail, but didn't want to leave this out | ||
+ | completely. [See picture 3.] | ||
+ | |||
+ | |||
+ | _Smoother and smoother_ | ||
+ | |||
+ | Why be satisfied with a scroll with only 40 different slices horizontally ? | ||
+ | It should be possible to count own coordinates for each pixel column on | ||
+ | the scroll. In fact the program won't be much different, but the routine | ||
+ | must also mask the unwanted bits and write the byte to memory with ORA+STA. | ||
+ | When you think about it more, it is obvious that this takes a generous amount | ||
+ | of time, handling every bit seperately will take much more than eight times | ||
+ | the time a simple LDA+STA takes. Some coders have avoided this by plotting | ||
+ | the same character to different character sets simultaneously and then | ||
+ | changing the charsets appropriately, | ||
+ | larger than 96x32 pixels. | ||
+ | |||
+ | -------------------------------------------------------------------------- | ||
+ | Picture 1 - Two character codes will make a graphical bitmap | ||
+ | |||
+ | Screen memory: | ||
+ | | ||
+ | |Char | ||
+ | |Code | ||
+ | |0 |2 |80 | ||
+ | | | ||
+ | | | ||
+ | |***** | ||
+ | |****** | **** | | ||
+ | |**__**_|__**___|_______|_______| | ||
+ | |** ** | ** | **** |Char | | ||
+ | |** ** | ** | ||
+ | |****** | | ||
+ | |***** | ||
+ | |Char | ||
+ | |Code | ||
+ | |1 |3 |4**** | ||
+ | |_______|_______|_______|******_| | ||
+ | |Char | ||
+ | |Code | ||
+ | |80 | ||
+ | | | ||
+ | | | ||
+ | | | ||
+ | | | ||
+ | |_______|_______|_______|_______| | ||
+ | |||
+ | Character set memory: | ||
+ | |||
+ | | ||
+ | |Char 0 |Char 1 |Char 2 |Char 3 |Char 4 |Char 5 |Char 6 |Char 7 | ... | ||
+ | |_______|_______|_______|_______|_______|_______|_______|_______|__ | ||
+ | DDDDDDDD | ||
+ | First column | ||
+ | |||
+ | -------------------------------------------------------------------------- | ||
+ | Picture 2 - DYCP with sprites | ||
+ | |||
+ | Sprite 0 | ||
+ | | ||
+ | | | ||
+ | | | ||
+ | | | ||
+ | |***** | ||
+ | |****** | ** | ||
+ | |** ** | ** | ||
+ | |** ** | ** | ||
+ | |**__**_|_______|_______| | ||
+ | |****** | | **** | | ||
+ | |***** | ||
+ | | | ||
+ | | | ||
+ | | | ||
+ | | | ||
+ | | | ||
+ | |_______|_______|_______| Sprite 1 | ||
+ | | | ||
+ | | | ||
+ | | | ||
+ | | | ||
+ | |_______|_______|_______||****** | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | |||
+ | -------------------------------------------------------------------------- | ||
+ | Picture 3 - Sprite multiplexing | ||
+ | |||
+ | | ||
+ | __|3 | that start from the top half. | ||
+ | |4 | |__ | ||
+ | | ||
+ | |5 `--' | ||
+ | | | | ||
+ | `--' | ||
+ | | | | ||
+ | __ `--' | ||
+ | |6 | | ||
+ | | ||
+ | | ||
+ | | ||
+ | -__------------------------------------`--' | ||
+ | |0 | | ||
+ | | | |6 | | ||
+ | `--' | ||
+ | `--' | ||
+ | __ | ||
+ | |1 | __ | ||
+ | | ||
+ | `-- __ | | | ||
+ | |2 | | ||
+ | | |__|4 | You usually have two sprites that | ||
+ | `--|3 | | are only ' | ||
+ | | ||
+ | | ||
+ | -------------------------------------------------------------------------- | ||
+ | |||
+ | DYCP demo program (PAL) | ||
+ | |||
+ | |||
+ | SINUS= | ||
+ | CHRSET= $3800 ; Here begins the character set memory | ||
+ | GFX= $3C00 ; Here we plot the dycp data | ||
+ | X16= $CE00 ; values multiplicated by 16 (0,16,32..) | ||
+ | D16= $CE30 ; divided by 16 (16 x 0,16 x 1 ...) | ||
+ | START= | ||
+ | COUNTER= $033D ; Scroll counter (x-scroll register) | ||
+ | POINTER= $033E ; Pointer to the text char | ||
+ | YPOS= | ||
+ | YPOSH= | ||
+ | CHAR= | ||
+ | ZP= | ||
+ | ZP2= $FD | ||
+ | AMOUNT= 38 ; Amount of chars to plot-1 | ||
+ | PADCHAR= 32 ; Code used for clearing the screen | ||
+ | |||
+ | *= $C000 | ||
+ | |||
+ | SEI ; Disable interrupts | ||
+ | LDA #$32 ; Character generator ROM to address space | ||
+ | STA $01 | ||
+ | LDX #0 | ||
+ | LOOP0 LDA $D000, | ||
+ | STA CHRSET,X | ||
+ | LDA $D100,X | ||
+ | STA CHRSET+256, | ||
+ | DEX | ||
+ | BNE LOOP0 | ||
+ | LDA #$37 ; Normal memory configuration | ||
+ | STA $01 | ||
+ | LDY #31 | ||
+ | LOOP1 LDA #66 ; Compose a full sinus from a 1/4th of a | ||
+ | CLC ; | ||
+ | ADC SIN,X | ||
+ | STA SINUS,X | ||
+ | STA SINUS+32,Y | ||
+ | LDA #64 | ||
+ | SEC | ||
+ | SBC SIN,X | ||
+ | STA SINUS+64,X | ||
+ | STA SINUS+96,Y | ||
+ | INX | ||
+ | DEY | ||
+ | BPL LOOP1 | ||
+ | LDX #$7F | ||
+ | LOOP2 LDA SINUS,X | ||
+ | LSR | ||
+ | CLC | ||
+ | ADC #32 | ||
+ | STA SINUS+128,X | ||
+ | DEX | ||
+ | BPL LOOP2 | ||
+ | |||
+ | LDX #39 | ||
+ | LOOP3 TXA | ||
+ | ASL | ||
+ | ASL | ||
+ | ASL | ||
+ | ASL | ||
+ | STA X16,X ; Multiplication table (for speed) | ||
+ | TXA | ||
+ | LSR | ||
+ | LSR | ||
+ | LSR | ||
+ | LSR | ||
+ | CLC | ||
+ | ADC #>GFX | ||
+ | STA D16,X ; Dividing table | ||
+ | LDA #0 | ||
+ | STA CHAR, | ||
+ | DEX | ||
+ | BPL LOOP3 | ||
+ | STA POINTER | ||
+ | LDX #7 | ||
+ | STX COUNTER | ||
+ | LOOP10 | ||
+ | DEX | ||
+ | BPL LOOP10 | ||
+ | |||
+ | LDA #> | ||
+ | STA ZP2+1 | ||
+ | LDA #< | ||
+ | STA $0314 | ||
+ | LDA #>IRQ | ||
+ | STA $0315 | ||
+ | LDA #$7F ; Disable timer interrupts | ||
+ | STA $DC0D | ||
+ | LDA #$81 ; Enable raster interrupts | ||
+ | STA $D01A | ||
+ | LDA #$A8 ; Raster compare to scan line $A8 | ||
+ | STA $D012 | ||
+ | LDA #$1B ; 9th bit | ||
+ | STA $D011 | ||
+ | LDA #30 | ||
+ | STA $D018 ; Use the new charset | ||
+ | CLI ; Enable interrupts and return | ||
+ | RTS | ||
+ | |||
+ | IRQ INC START ; Increase counter | ||
+ | LDY #AMOUNT | ||
+ | LDX START | ||
+ | LOOP4 LDA SINUS, | ||
+ | AND #7 ; to it fetch a y-position from the sinus table | ||
+ | STA YPOS, | ||
+ | LDA SINUS,X | ||
+ | LSR | ||
+ | LSR | ||
+ | LSR | ||
+ | STA YPOSH,Y | ||
+ | INX ; Chars are two positions apart | ||
+ | INX | ||
+ | DEY | ||
+ | BPL LOOP4 | ||
+ | |||
+ | LDA #0 | ||
+ | LDX #79 | ||
+ | LOOP11 | ||
+ | STA GFX+80,X | ||
+ | STA GFX+160,X | ||
+ | STA GFX+240,X | ||
+ | STA GFX+320,X | ||
+ | STA GFX+400,X | ||
+ | STA GFX+480,X | ||
+ | STA GFX+560,X | ||
+ | DEX | ||
+ | BPL LOOP11 | ||
+ | |||
+ | MAKE LDA COUNTER | ||
+ | STA $D016 | ||
+ | LDX #AMOUNT | ||
+ | CLC ; Clear carry | ||
+ | LOOP5 LDY YPOSH, | ||
+ | TXA | ||
+ | ADC LINESL, | ||
+ | STA ZP ; low byte | ||
+ | LDA #4 | ||
+ | ADC LINESH,Y | ||
+ | STA ZP+1 ; high byte | ||
+ | LDA # | ||
+ | LDY #0 ; 0. row | ||
+ | STA (ZP),Y | ||
+ | LDY #120 ; 3. row | ||
+ | STA (ZP),Y | ||
+ | TXA ; Then put consecuent character codes to the places | ||
+ | ASL ; | ||
+ | ORA #$80 ; Inverted chars | ||
+ | LDY #40 ; 1. row | ||
+ | STA (ZP),Y | ||
+ | ADC #1 ; Increase the character code, Carry won't be set | ||
+ | LDY #80 ; 2. row | ||
+ | STA (ZP),Y | ||
+ | |||
+ | LDA CHAR, | ||
+ | STA ZP2 ; | ||
+ | LDA X16,X ; Destination low byte | ||
+ | ADC YPOS, | ||
+ | STA ZP | ||
+ | LDA D16,X ; Destination high byte | ||
+ | STA ZP+1 | ||
+ | |||
+ | LDY #6 ; Transfer 7 bytes from source to destination | ||
+ | LDA (ZP2),Y : STA (ZP),Y | ||
+ | DEY ; This is the fastest way I could think of. | ||
+ | LDA (ZP2),Y : STA (ZP),Y | ||
+ | DEY | ||
+ | LDA (ZP2),Y : STA (ZP),Y | ||
+ | DEY | ||
+ | LDA (ZP2),Y : STA (ZP),Y | ||
+ | DEY | ||
+ | LDA (ZP2),Y : STA (ZP),Y | ||
+ | DEY | ||
+ | LDA (ZP2),Y : STA (ZP),Y | ||
+ | DEY | ||
+ | LDA (ZP2),Y : STA (ZP),Y | ||
+ | DEX | ||
+ | BPL LOOP5 ; Get next char in scroll | ||
+ | |||
+ | LDA #1 | ||
+ | STA $D019 ; Acknowledge raster interrupt | ||
+ | |||
+ | DEC COUNTER | ||
+ | BPL OUT | ||
+ | LOOP12 | ||
+ | STA CHAR, | ||
+ | INY | ||
+ | CPY #AMOUNT | ||
+ | BNE LOOP12 | ||
+ | LDA POINTER | ||
+ | AND #63 ; Text is 64 bytes long | ||
+ | TAX | ||
+ | LDA SCROLL, | ||
+ | ASL | ||
+ | ASL | ||
+ | ASL | ||
+ | STA CHAR+AMOUNT ; Save it to the right side | ||
+ | DEC START ; Compensation for the text scrolling | ||
+ | DEC START | ||
+ | INC POINTER | ||
+ | LDA #7 | ||
+ | STA COUNTER | ||
+ | |||
+ | OUT JMP $EA7E ; Return from interrupt | ||
+ | |||
+ | SIN BYT 0, | ||
+ | BYT 47, | ||
+ | ; 1/4 of the sinus | ||
+ | |||
+ | LINESL | ||
+ | BYT 8, | ||
+ | |||
+ | LINESH | ||
+ | |||
+ | SCROLL | ||
+ | SCR " | ||
+ | ; SCR will convert text to screen codes | ||
+ | |||
+ | -------------------------------------------------------------------------- | ||
+ | Basic loader for the Dycp demo 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 78A9328501A200BD00D09D0038BD00D19D0039CAD0F1A9378501A01FA942187D, | ||
+ | 101 DATA 75C19D00CF9920CFA94038FD75C19D40CF9960CFE88810E4A27FBD00CF4A1869, | ||
+ | 102 DATA 209D80CFCA10F3A2278A0A0A0A0A9D00CE8A4A4A4A4A18693C9D30CEA9009D90, | ||
+ | 103 DATA 03CA10E58D3E03A2078E3D039D0038CA10FAA93885FEA99B8D1403A9C08D1503, | ||
+ | 104 DATA A97F8D0DDCA9818D1AD0A9A88D12D0A91B8D11D0A91E8D18D05860EE3C03A026, | ||
+ | 105 DATA AE3C03BD00CF2907994003BD00CF4A4A4A996803E8E88810EAA900A24F9D003C, | ||
+ | 106 DATA 9D503C9DA03C9DF03C9D403D9D903D9DE03D9D303ECA10E5AD3D038D16D0A226, | ||
+ | 107 DATA 18BC68038A7995C185FBA90479AAC185FCA920A00091FBA07891FB8A0A0980A0, | ||
+ | 108 DATA 2891FB6901A05091FBBD900385FDBD00CE7D400385FBBD30CE85FCA006B1FD91, | ||
+ | 109 DATA FB88B1FD91FB88B1FD91FB88B1FD91FB88B1FD91FB88B1FD91FB88B1FD91FBCA, | ||
+ | 110 DATA 109FEE19D0CE3D031028B99103999003C8C026D0F5AD3E03293FAABDBFC10A0A, | ||
+ | 111 DATA 0A8DB603CE3C03CE3C03EE3E03A9078D3D034C7EEA000306090C0F1215181B1E, | ||
+ | 112 DATA 202326282A2D2F3133353638393B3C3D3E3E3F3F3F00285078A0C8F018406890, | ||
+ | 113 DATA B8E008305880A8D0F82000000000000000010101010101020202020202020314, | ||
+ | 114 DATA 08091300091300010E000518010D100C05001303120F0C0C00060F1200030F0D, | ||
+ | 115 DATA 0D0F040F1205000D0107011A090E050002190010011309000F0A010C01000000, | ||
+ | 200 DATA END,0 | ||
+ | |||
+ | -------------------------------------------------------------------------- | ||
+ | Uuencoded C64 executable of the basic loader (PAL) | ||
+ | |||
+ | begin 644 dycp.64 | ||
+ | M`0@-" | ||
+ | MLC`ZAT$D+$$ZF4$D.HM!)+(B14Y$(J> | ||
+ | M, | ||
+ | M*2D`J@@%`$-(LD-(JE$ZEU, | ||
+ | M0TM354T@15)23U(B.H``# | ||
+ | M, | ||
+ | M, | ||
+ | M, | ||
+ | M, | ||
+ | M.40S, | ||
+ | M.$4S1# | ||
+ | M, | ||
+ | M.3%" | ||
+ | M($%%, | ||
+ | M.# | ||
+ | M.41&,# | ||
+ | M-D0P03(R-BP@, | ||
+ | M.4%!0S$X-49# | ||
+ | M`'< | ||
+ | M,#, | ||
+ | M1D0Y, | ||
+ | M0C@X0C%& | ||
+ | M.$(Y.3$P, | ||
+ | M+" | ||
+ | M, | ||
+ | M, | ||
+ | M.# | ||
+ | M.$0P1C@R,# | ||
+ | M,# | ||
+ | M, | ||
+ | M# | ||
+ | L,# | ||
+ | `` | ||
+ | end | ||
+ | size 1439 | ||
+ | -------------------------------------------------------------------------- | ||
+ | Uuencoded C64 executable of the basic loader (NTSC) | ||
+ | |||
+ | begin 644 dycp-ntsc.bas | ||
+ | M`0@-" | ||
+ | MLC`ZAT$D+$$ZF4$D.HM!)+(B14Y$(J> | ||
+ | MI#, | ||
+ | M*0" | ||
+ | M2U-532!%4E)/ | ||
+ | M,# | ||
+ | M+" | ||
+ | M0T8Y.38P0T9%.# | ||
+ | M, | ||
+ | M.# | ||
+ | M, | ||
+ | M.$0Q-3`S+" | ||
+ | M, | ||
+ | ME0II`(, | ||
+ | M, | ||
+ | M1$$P, | ||
+ | M,#, | ||
+ | M03DP-#< | ||
+ | M(# | ||
+ | M13=$-# | ||
+ | M0C@X0C%& | ||
+ | M1D0Y, | ||
+ | M,#, | ||
+ | M, | ||
+ | M.3`W.$0S1# | ||
+ | M#' | ||
+ | M, | ||
+ | M-3@X, | ||
+ | M, | ||
+ | M-3$X,# | ||
+ | M, | ||
+ | M.3`P, | ||
+ | $,````#`P0 | ||
+ | `` | ||
+ | end | ||
+ | size 1444 | ||
+ | |||
+ | ================================================================================ | ||
+ | </ | ||
+ | ===== Opening the borders ===== | ||
+ | < | ||
+ | by Pasi ' | ||
+ | Written: 20-Jul-92 | ||
+ | |||
+ | All timings are in PAL, principles will apply to NTSC too. | ||
+ | Refer to VIC memory map in Hacking Issue 4. | ||
+ | |||
+ | VIC has many features and transparent borders are one of them. You can not | ||
+ | make characters appear in the border, but sprites are displayed in the | ||
+ | border too. "How to do this then?" is the big question. | ||
+ | |||
+ | The screen resolution in C64 has been and will be 320 x 200 pixels. Most | ||
+ | games need to use the whole screen to be efficient or just plain playable. | ||
+ | But there still is that useless border area, and you can put score and | ||
+ | other status information there instead of having them interfere with the | ||
+ | full-screen smooth-scrolling playing area. | ||
+ | |||
+ | |||
+ | _How to disable the vertical borders_ | ||
+ | |||
+ | When VIC (Video Interface Controller) has displayed all character rows, | ||
+ | it will start displaying the vertical border area. It will start displaying | ||
+ | the characters again in top of the screen. The row select register sets the | ||
+ | number of character lines on the screen. If we select the 24-row display | ||
+ | when VIC is drawing the last (25th) row, it does not start to draw the | ||
+ | border at all ! VIC will think that it already started to draw the border. | ||
+ | |||
+ | The 25-row display must be selected again in the top of the screen, so that | ||
+ | the border may be opened in the next frame too. The number of displayed rows | ||
+ | can be selected with the bit 3 in $d011. If the bit is set, VIC will display | ||
+ | 25 rows and 24 rows otherwise. We have to clear the bit somewhere during the | ||
+ | last row (raster lines $f2-$fa) and set it again in top of the screen or at | ||
+ | least somewhere before the last row (line $f2). This has to be done in every | ||
+ | frame (50 times per second in PAL). | ||
+ | |||
+ | |||
+ | _How to open the sideborders_ | ||
+ | |||
+ | The same trick can be applied to sideborders. When VIC is about to start | ||
+ | displaying the sideborder, just select 38-column mode and restore 40-column | ||
+ | mode so that you can do the trick again in the next scan line. If you need to | ||
+ | open the sideborders in the bottom or top border area, you have to open the | ||
+ | vertical borders also, but there shouldn' | ||
+ | |||
+ | There is two drawbacks in this. The timing must be precise, one clock cycle | ||
+ | off and the sideborder will not open (the sprites will generally take care of | ||
+ | the timing) and you have to do the opening on each and every line. With | ||
+ | top/bottom borders once in a frame was enough. | ||
+ | |||
+ | Another problem is bad-lines. There is not enough time to open the borders | ||
+ | during a bad line and still have all of the sprites enabled. One solution | ||
+ | is to open the borders only on seven lines and leave the bad lines unopened. | ||
+ | Another way is to use less than eight sprites. You can have six of them | ||
+ | on a bad line and still be able to open the sideborders (PAL). The old and | ||
+ | still good solution is to scroll the bad lines, so that VIC will not start | ||
+ | to draw the screen at all until it is allowed to do so. | ||
+ | [Read more about bad lines from previous C=Hacking Issues] | ||
+ | |||
+ | |||
+ | _Scrolling the screen_ | ||
+ | |||
+ | VIC begins to draw the screen from the first bad line. VIC will know what | ||
+ | line is a bad line by comparing its scan line counter to the vertical | ||
+ | scroll register : when they match, the next line is a bad line. If we change | ||
+ | the vertical scroll register ($d011), the first bad line will move also. | ||
+ | If we do this on every line, the line counter in VIC will never match with | ||
+ | it and the drawing never starts (until it is allowed to do so). | ||
+ | |||
+ | When we don't have to worry about bad lines, we have enough time to open the | ||
+ | borders and do some other effects too. It is not necassary to change the | ||
+ | vertical scroll on every line to get rid of the bad lines, just make sure | ||
+ | that it never matches the line counter (or actually the least significant | ||
+ | 8 bits). | ||
+ | |||
+ | You can even scroll the bad lines independently and you have FLD - Flexible | ||
+ | Line Distance. You just allow a bad line when it is time to display the next | ||
+ | character row. With this you can bounce the lines or scroll a hires picture | ||
+ | very fast down the screen. But this has not so much to do with borders, so | ||
+ | I will leave it to another article. (Just send requests and I might start | ||
+ | writing about FLD ..) | ||
+ | |||
+ | |||
+ | _Garbage appearing_ | ||
+ | |||
+ | When we open the top and bottom borders, some graphics may appear. Even | ||
+ | though VIC has already completed the graphics data fetches for the screen | ||
+ | area, it will still fetch data for every character position in top and bottom | ||
+ | borders. This will not do any harm though, because it does not generate any | ||
+ | bad lines and happens during video fetch cycles [see Missing Cycles article]. | ||
+ | VIC reads the data from the last address in the current video bank, which is | ||
+ | normally $3fff and displays this over and over again. | ||
+ | |||
+ | If we change the data in this address in the border area, the change will be | ||
+ | visible right away. And if you synchronize the routine to the beam position, | ||
+ | you can have a different value on each line. If there is nothing else to do | ||
+ | in the border, you can get seven different values on each scan line. | ||
+ | |||
+ | The bad thing about this graphics is that it is impossible to change its | ||
+ | color - it is always black. It is of course possible to use inverted graphics | ||
+ | and change the background color. And if you have different data on each line, | ||
+ | you can as easily have different color(s) on each line too. | ||
+ | |||
+ | If you don't use $3fff for any effects, it is a good idea to set it to zero, | ||
+ | but remember to check that you do not store anything important in that | ||
+ | address. In one demo I just cleared $3fff and it was right in the middle of | ||
+ | another packed demopart. It took some time to find out what was wrong with | ||
+ | the other part. | ||
+ | |||
+ | |||
+ | _Horizontal scrolling_ | ||
+ | |||
+ | This new graphics data also obeys the horizontal scroll register ($D016), so | ||
+ | you can do limited tech-tech effects in the border too. You can also use | ||
+ | sprites and open the sideborders. You can see an example of the tech-tech | ||
+ | effect in the first example program. Multicolor mode select has no effect | ||
+ | on this data. You can read more about tech-tech effects in a future article. | ||
+ | |||
+ | |||
+ | _Example routine_ | ||
+ | |||
+ | The example program will show how to open the top and bottom borders and how | ||
+ | to use the $3fff-graphics. It is fairly well commented, so just check it for | ||
+ | details. The program uses a sprite to do the synchronization [see Missing | ||
+ | Cycles article] and reads a part of the character ROM to the display data | ||
+ | buffer. To be honest, I might add that this is almost the same routine than | ||
+ | the one in the Missing Cycles article. I have included both PAL and NTSC | ||
+ | versions of the executables. | ||
+ | |||
+ | -------------------------------------------------------------------------- | ||
+ | The example program - $3fff-graphics | ||
+ | |||
+ | IMAGE0= $CE00 ; First graphics piece to show | ||
+ | IMAGE1= $CF00 ; Second piece | ||
+ | TECH= | ||
+ | RASTER= $FA ; Rasterline for the interrupt | ||
+ | DUMMY= | ||
+ | |||
+ | *= $C000 | ||
+ | SEI ; Disable interrupts | ||
+ | LDA #$7F ; Disable timer interrupts (CIA) | ||
+ | STA $DC0D | ||
+ | LDA #$01 ; Enable raster interrupts (VIC) | ||
+ | STA $D01A | ||
+ | STA $D015 ; Enable the timing sprite | ||
+ | LDA #<IRQ | ||
+ | STA $0314 ; Interrupt vector to our routine | ||
+ | LDA #>IRQ | ||
+ | STA $0315 | ||
+ | LDA # | ||
+ | STA $D012 ; | ||
+ | LDA # | ||
+ | STA $D001 | ||
+ | |||
+ | LDX #111 | ||
+ | LDY #0 | ||
+ | STY $D017 ; Disable y-expand | ||
+ | LDA #$32 | ||
+ | STA $01 ; Select Character ROM | ||
+ | LOOP0 LDA $D000,X | ||
+ | STA IMAGE0, | ||
+ | STA IMAGE0+112, | ||
+ | LDA $D800,X | ||
+ | STA IMAGE1,Y | ||
+ | STA IMAGE1+112, | ||
+ | INY ; Until we copied enough | ||
+ | DEX | ||
+ | BPL LOOP0 | ||
+ | LDA #$37 ; Char ROM out of the address space | ||
+ | STA $01 | ||
+ | |||
+ | LDY #15 | ||
+ | LOOP1 LDA XPOS, | ||
+ | STA TECH, | ||
+ | STA TECH+32, | ||
+ | LDA #24 | ||
+ | SEC | ||
+ | SBC XPOS,Y | ||
+ | STA TECH+16,Y | ||
+ | STA TECH+48,Y | ||
+ | DEY | ||
+ | BPL LOOP1 | ||
+ | LDY #64 | ||
+ | LOOP2 LDA TECH,Y | ||
+ | STA TECH+64,Y | ||
+ | STA TECH+128,Y | ||
+ | DEY | ||
+ | BPL LOOP2 | ||
+ | CLI ; Enable interrupts | ||
+ | RTS ; Return to basic (?) | ||
+ | |||
+ | |||
+ | IRQ LDA #$13 ; Open the bottom border (top border will open too) | ||
+ | STA $D011 | ||
+ | NOP | ||
+ | LDY #111 ; Reduce for NTSC ? | ||
+ | INC DUMMY ; Do the timing with a sprite | ||
+ | BIT $EA ; Wait a bit (add a NOP for NTSC) | ||
+ | |||
+ | LOOP3 LDA TECH, | ||
+ | STA $D016 | ||
+ | FIRST LDX IMAGE0, | ||
+ | SECOND | ||
+ | STA $3FFF ; Alternate the graphics | ||
+ | STX $3FFF | ||
+ | STA $3FFF | ||
+ | STX $3FFF | ||
+ | STA $3FFF | ||
+ | STX $3FFF | ||
+ | STA $3FFF | ||
+ | STX $3FFF | ||
+ | STA $3FFF | ||
+ | STX $3FFF | ||
+ | LDA #0 ; Throw away 2 cycles (add a NOP for NTSC) | ||
+ | DEY | ||
+ | BPL LOOP3 | ||
+ | |||
+ | STA $3FFF ; Clear the graphics | ||
+ | LDA #8 | ||
+ | STA $D016 ; x-scroll to normal | ||
+ | LDA #$1B | ||
+ | STA $D011 ; Normal screen (be ready to open the border again) | ||
+ | LDA #111 | ||
+ | DEC FIRST+1 | ||
+ | BPL OVER ; load instruction | ||
+ | STA FIRST+1 | ||
+ | OVER SEC | ||
+ | SBC FIRST+1 | ||
+ | STA SECOND+1 | ||
+ | LDA LOOP3+1 | ||
+ | SEC | ||
+ | SBC #2 | ||
+ | AND #31 ; Sinus cycle is 32 bytes | ||
+ | STA LOOP3+1 | ||
+ | |||
+ | LDA #1 | ||
+ | STA $D019 ; Acknowledge the raster interrupt | ||
+ | JMP $EA31 ; jump to the normal irq-handler | ||
+ | |||
+ | XPOS BYT $C, | ||
+ | BYT $C, | ||
+ | ; half of the sinus | ||
+ | |||
+ | -------------------------------------------------------------------------- | ||
+ | Basic loader for the $3fff-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 78A97F8D0DDCA9018D1AD08D15D0A9718D1403A9C08D1503A9FA8D12D0A9E68D, | ||
+ | 101 DATA 01D0A26FA0008C17D0A9328501BD00D09900CE9970CEBD00D89900CF9970CFC8, | ||
+ | 102 DATA CA10EAA9378501A00FB9DCC09900CD9920CDA91838F9DCC09910CD9930CD8810, | ||
+ | 103 DATA E8A040B900CD9940CD9980CD8810F45860A9138D11D0EAA06FEEFFCF24EAB906, | ||
+ | 104 DATA CD8D16D0BE53CEB91CCF8DFF3F8EFF3F8DFF3F8EFF3F8DFF3F8EFF3F8DFF3F8E, | ||
+ | 105 DATA FF3F8DFF3F8EFF3FA9008810D18DFF3FA9088D16D0A91B8D11D0A96FCE85C010, | ||
+ | 106 DATA 038D85C038ED85C08D88C0AD7FC018E901291F8D7FC0EE19D04C31EA0C0C0D0E, | ||
+ | 107 DATA 0E0F0F0F0F0F0F0F0E0E0D0C0C0B0A09090808080808080809090A0B00000000, | ||
+ | 200 DATA END,0 | ||
+ | |||
+ | -------------------------------------------------------------------------- | ||
+ | An uuencoded C64 executable $3fff-program (PAL) | ||
+ | |||
+ | begin 644 xFFF.64 | ||
+ | M`0@-" | ||
+ | MLC`ZAT$D+$$ZF4$D.HM!)+(B14Y$(J> | ||
+ | MI#, | ||
+ | M*0" | ||
+ | M2U-532!%4E)/ | ||
+ | M-40P03DW, | ||
+ | M`%L)90"# | ||
+ | M.3DW, | ||
+ | M03DS-S@U,# | ||
+ | M, | ||
+ | M.3@P0T0X.# | ||
+ | M+" | ||
+ | M.$1& | ||
+ | M1D8S1CA$1D8S1CA%1D8S1D$Y,# | ||
+ | M0CA$, | ||
+ | M-4, | ||
+ | M, | ||
+ | M, | ||
+ | -" | ||
+ | `` | ||
+ | end | ||
+ | size 823 | ||
+ | -------------------------------------------------------------------------- | ||
+ | An uuencoded C64 executable $3fff-program (NTSC) | ||
+ | |||
+ | begin 644 xfff-ntsc.64 | ||
+ | M`0@-" | ||
+ | MLC`ZAT$D+$$ZF4$D.HM!)+(B14Y$(J> | ||
+ | MI#, | ||
+ | M*0" | ||
+ | M2U-532!%4E)/ | ||
+ | M040P.$0Q-40P03DW, | ||
+ | M+" | ||
+ | M.3DP, | ||
+ | M0T$Q, | ||
+ | M14, | ||
+ | M.30P0T0Y.3@P0T0X.# | ||
+ | M14%%04(Y+" | ||
+ | M, | ||
+ | ME0II`(, | ||
+ | M.# | ||
+ | M1# | ||
+ | M1# | ||
+ | M, | ||
+ | 4(# | ||
+ | `` | ||
+ | end | ||
+ | size 830 | ||
+ | |||
+ | ============================================================================= | ||
+ | </ | ||
+ | ===== A Heavy-Duty Power Supply for the C-64 ===== | ||
+ | < | ||
+ | by John C. Andrews (no email address) | ||
+ | |||
+ | As a Commodore User for the last 4 plus years, I am aware of the many | ||
+ | articles and letters in the press that have bemoaned the burn-out | ||
+ | problem of the C-64 power supply. When our Club BBS added a one meg | ||
+ | drive and stayed on around the clock, the need for heavy-duty power | ||
+ | supply became very apparent.... Three power supplies went in 3 | ||
+ | successive days! | ||
+ | |||
+ | Part of the problem was my ignoring the seasons. You see during the | ||
+ | winter I had set the power supply between the window and the screen, | ||
+ | Yes, outside! With the advent of Spring... well, you get the picture. | ||
+ | |||
+ | The turn-around time forgetting a new commerical supply was not in the | ||
+ | best interest of the BBS and its members. Therefore, taking power | ||
+ | supply inhand, I proceeded to cut one open on my shop bandsaw. I do | ||
+ | not suggest that you do this. The parts are FIRMLY and COMPLETELY | ||
+ | encased in a hard plastic potting compound. The purpose of this is not | ||
+ | to make the item difficult to repair, but to make the entire unit | ||
+ | conductive to the heat generated inside. I doubt the wisedom of | ||
+ | potting the fuse as well. However, CBM was probably thinking of the | ||
+ | number of little fingers that could fit into an accessable fuse | ||
+ | holder. if you want the punch line it is: the final circuit board and | ||
+ | its componets are about the size of a box of matches. This includes | ||
+ | the built-in metal heat sink. | ||
+ | |||
+ | From these minscule innards I traced out the circuit and increased the | ||
+ | size of ALL components. | ||
+ | |||
+ | The handiest source of electronic parts is, of course, Radio Shack. | ||
+ | All but one part can be purchased there. | ||
+ | |||
+ | 212-1013 | ||
+ | 212-1022 | ||
+ | 273-1515 | ||
+ | 276-1184 | ||
+ | |||
+ | 270- 742 Fuse Block | ||
+ | 270-1275 | ||
+ | |||
+ | Note that there are only five parts. The rest are fuses, fuse blocks, | ||
+ | heat sinks, wire and misc. hardware. Note also that I have not listed | ||
+ | any plugs and cords. This because you can clip the cords off of both | ||
+ | sides of your defunct power supply. This will save you the hassle of | ||
+ | wriing the DIN power plug correctly: | ||
+ | |||
+ | DIN PIN OUT COLOR | ||
+ | pin 6 | ||
+ | pin 7 | ||
+ | pin 5 +5 Volts blue | ||
+ | pin 1,2,3 | ||
+ | |||
+ | The part that you can NOT get at Radio Shack is the power regulator. | ||
+ | This part will have to be scrounged up from some local, big | ||
+ | electronics supply house: | ||
+ | |||
+ | SK 9067 5 volt voltage regulator, 3+ amps. (I prefer the 5 amp.) | ||
+ | |||
+ | Radio Shack does carry regulators, but their capacity is no larger | ||
+ | than that with which you started. | ||
+ | |||
+ | The Heat sinks, (yes, more than one!) are the key to the success of | ||
+ | this project. The ones I used came from my Model Railroading days. | ||
+ | Sorry to say, I did just ahve them 'lying about' | ||
+ | I priced at the local electronics supply were more costly than the | ||
+ | other parts. The worst case situation is that you may need to drill | ||
+ | out a couple pieces of aluminum sheet. Try for 12 x 12, and bend them | ||
+ | into square bottomed U-shapes to save room. heat sinks should not | ||
+ | touch, or be electronically grounded to each other. You can also mount | ||
+ | them on stand-offs from your chassis for total air circulation. | ||
+ | |||
+ | The Radio Shack transformer is rated at only 2 amps. If you can not | ||
+ | find one with a higher rating elsewhere, it is possible to hook two in | ||
+ | parallel to get a 4 ampere output. This si tricky, as it can be done | ||
+ | either right or wrong! | ||
+ | |||
+ | Here is how to do it the right way: | ||
+ | Tape off one yellow secondary lead on each transformer. With tape | ||
+ | mark the four remaining secondary leads and letter them A and B on | ||
+ | one transformer, | ||
+ | leads to a plug to your 120 wall outlet: | ||
+ | |||
+ | |------------- | ||
+ | Note: *'s - indicate connections | 3 || | ||
+ | | ||
+ | | 3 || | ||
+ | | 3 || | ||
+ | | | ||
+ | | | | ||
+ | +--\ / | ||
+ | --|120|/ | ||
+ | --|Vlt| | ||
+ | | ||
+ | +--/ ---- | 3 || (Transformer) | ||
+ | |--------- | ||
+ | |||
+ | This would now be a good time to install a fuse in your 120 VAC | ||
+ | line. Now before plugging this into the wall, tie two of the | ||
+ | scondary leads (one from EACH transformer) together. | ||
+ | |||
+ | Something like this: A--Xfmr--B+C--Xfmr--D | ||
+ | |||
+ | Plug in your 120V side. Now using a VOM meter, measure the voltage | ||
+ | between A and D. | ||
+ | If the meter reads 18 volts, then: | ||
+ | 1. unplug from the 120. | ||
+ | 2. tie A and C together. tie B and D together. | ||
+ | 3. your 2 transformers will now give you 9 volts at 4 amps. | ||
+ | If the meter reads 0 volts, then: | ||
+ | 1. unplug from the 120. | ||
+ | 2. tie A and D together. Tie B and C together. | ||
+ | 3. your 2 transformers will now give you 9 volts at 4 amps. | ||
+ | |||
+ | Below is the file corresponding to the full schematic of the power supply. | ||
+ | [Ed's note: in GeoPaint format, converted by convert 2.5, then uuencoded]. As | ||
+ | you can see in the picture, I used only one transformer. Because it got hot, | ||
+ | I epoxied a small heat sink to it. While this solved the heat problem, it did | ||
+ | not increase the capacity of the total power supply. | ||
+ | |||
+ | Note that I used fuses on all lines. | ||
+ | |||
+ | ----------------------------------------------------------------------------- | ||
+ | begin 700 schematic. | ||
+ | M@PD,< | ||
+ | M14]3(& | ||
+ | MUH> | ||
+ | M34%424.@H*" | ||
+ | M+C4$6`<&# | ||
+ | M```````````````````````````````````````# | ||
+ | M55F: | ||
+ | M`/ | ||
+ | M````````````9V5O4& | ||
+ | M0"# | ||
+ | M# | ||
+ | M7_`& | ||
+ | M_P# | ||
+ | M`/ | ||
+ | M```````````````````````````````````````````````````````````` | ||
+ | M```````````````````````````````````````````````````````````` | ||
+ | M`````````````````````````````````````````````````````````/ | ||
+ | M_P# | ||
+ | M"/ | ||
+ | M_P``A1`# | ||
+ | MH`" | ||
+ | M_P# | ||
+ | M``/ | ||
+ | M@(" | ||
+ | MI*2DF`````2HJ%!0HP"& | ||
+ | M"/ | ||
+ | M(*@`B!" | ||
+ | M((L`# | ||
+ | M``# | ||
+ | M8``/## | ||
+ | M@,# | ||
+ | M#, | ||
+ | M``" | ||
+ | M, | ||
+ | M`0(*!!`0.$2" | ||
+ | M,, | ||
+ | M```````````````````````````````````````````````````````````` | ||
+ | M```````````````````````````````````````````````````````````` | ||
+ | M```````````````````````````````````````````````````````````` | ||
+ | M``````````````````````````````````"< | ||
+ | M!N# | ||
+ | MA64P# | ||
+ | M@D0X(+]`@$(``````/ | ||
+ | M< | ||
+ | M`@$!`8@`' | ||
+ | M986%966%A64P# | ||
+ | M!*BH4)```0-# | ||
+ | M_P# | ||
+ | M```````````````````````````````````````````````````````````` | ||
+ | M```````````````````````````````````````````````````````````` | ||
+ | M`````````````````````````````````````````````````````+``B"" | ||
+ | M`(5`%7]`0$1X````_P``I9P```# | ||
+ | M_`0$_P`], | ||
+ | M`0``_P``986%8&" | ||
+ | M`%````# | ||
+ | MCX@(" | ||
+ | M# | ||
+ | MJ`" | ||
+ | MAP`!" | ||
+ | M```````````````````````````````````````````````````````````` | ||
+ | M```````````````````````````````````````````````````````````` | ||
+ | M```````````````````````````````````````````````````````````` | ||
+ | M````````````L`" | ||
+ | MC```_P``D)" | ||
+ | M8.# | ||
+ | MB`C_`/ | ||
+ | M`/ | ||
+ | MX(8@BP`-`0$!`@2`@(`.$1`0$X4``UEB0X4``XQ2WH4``^" | ||
+ | M`(@0F``*B!" | ||
+ | MOP``# | ||
+ | M$`/ | ||
+ | M_P" | ||
+ | MB`@(A``$_P```(0(!/ | ||
+ | M$1`0$X4``UEB0X4``XQ2WH4``^" | ||
+ | M_P# | ||
+ | M```], | ||
+ | M```[8S,; | ||
+ | M$)@`B!" | ||
+ | M"/ | ||
+ | MA0`# | ||
+ | M`0%" | ||
+ | M``3$!`3\_P# | ||
+ | M`/ | ||
+ | M_[^AOP# | ||
+ | MAB" | ||
+ | MB!" | ||
+ | M_X4``T!`P(T`!!, | ||
+ | MJ`" | ||
+ | ML``#," | ||
+ | M" | ||
+ | M$!.%``-98D.%``., | ||
+ | M`/ | ||
+ | M(D5%A``$'" | ||
+ | M@`````$!!P$!`!\0$![!`1' | ||
+ | MF`" | ||
+ | M_P# | ||
+ | M``/ | ||
+ | M!@````^!@(4`$1!(CXB(`````Z" | ||
+ | M````BHIR````0< | ||
+ | M@4& | ||
+ | M$!`0X)``" | ||
+ | M)< | ||
+ | >" | ||
+ | ` | ||
+ | end | ||
+ | |||
+ | ============================================================================= | ||
+ | </ | ||
+ | ===== LZW Compression ===== | ||
+ | < | ||
+ | by Bill Lucier (Blucier@ersys.edmonton.ab.ca, | ||
+ | |||
+ | LZW is perhaps the most widely used form of data compression today. It | ||
+ | is simple to implement and achieves very decent compression at a fairly | ||
+ | quick pace. LZW is used in PKZIP (shrink), | ||
+ | and unix's compress. This article will attempt to explain how the | ||
+ | compression works with a short example and 6502 source code in Buddy | ||
+ | format. | ||
+ | |||
+ | Originally named lz78, it was invented by Jacob Ziv and Abraham Lempel | ||
+ | in 1978 , it was later modified by Terry Welch to its present format. | ||
+ | The patent for the LZW compression method is presently held by Unisys. | ||
+ | |||
+ | LZW compresses data by taking phrases and compressing them into codes. | ||
+ | The size of the codes could vary from 9 bits to 16 bits. Although for | ||
+ | this implementation we will be using only 12 bits. As byte are read in | ||
+ | from a file they are added to a dictionary. For a 12-bit implementation | ||
+ | a dictionary will be 4k (2^12=4096) . Each entry in the dictionary | ||
+ | requires five bytes, which will be built in the form of a tree. It is | ||
+ | not a binary tree because each node may have more than two offsprings. | ||
+ | In fact, because our dictionary can hold up to 4096 different codes it | ||
+ | is possible to have one node with 3800 children nodes, although this is | ||
+ | not likely to happen. The five bytes that make up our tree will be: | ||
+ | |||
+ | The parent code: Each node has one and only one parent node. When the parent | ||
+ | code is less then 255 it is the end of a phrase. The codes | ||
+ | 0-255 do not actually exist in the tree. The following | ||
+ | | ||
+ | |||
+ | 256 : End of Stream-This marks the end of one compressed file | ||
+ | 257 : This tells the decompressor to increase the number | ||
+ | of bits its reading by one. | ||
+ | 258 : Wipe out dictionary | ||
+ | |||
+ | The code value : This is the code that will be sent to the compressor. | ||
+ | The character | ||
+ | |||
+ | Initially we send out codes that are 9 bits long, this will cover the values | ||
+ | 0-511. Once we have reached 511, we will need to increase the number of | ||
+ | bits to write by 1. This will give room for code numbers 512-1023, or | ||
+ | (2^10)-1. At this point we must ensure that the decompressor knows how | ||
+ | bits to read in at once so a code number 257 is sent to indicate that | ||
+ | the number of bits to be read is to be bumped up by one. The size of the | ||
+ | dictionary is finite so at some point we do have to be concerned with | ||
+ | what we will do when it does fill up. We could stop compiling new | ||
+ | phrases and just compress with the ones that are already in the | ||
+ | dictionary. This is not a very good choice, files tend to change | ||
+ | frequently (eg. program files as they change from code to data) so | ||
+ | sticking with the same dictionary will actually increase the size of the | ||
+ | file or at best, give poor compression. Another choice is to wipe the | ||
+ | dictionary out and start building new codes and phrases, or wipe out | ||
+ | some of the dictionary leaving behind only the newer codes and phrases. | ||
+ | For the sake of simplicity this program will just wipe out the | ||
+ | dictionary when it becomes full. | ||
+ | |||
+ | To illustrate how LZW works a small phrase will be compressed : heher. | ||
+ | To start the first two characters would be read in. The H would be | ||
+ | treated as the parent code and E becomes the character code. By means of | ||
+ | a hashing routine (the hashing routine will be explained more fully in | ||
+ | the source code) the location where HE should be is located. Since we | ||
+ | have just begun there will be nothing there,so the phrase will be added | ||
+ | to the dictionary. The codes 0-258 are already taken so we start using | ||
+ | 259 as our first code. The binary tree would look something like this: | ||
+ | | ||
+ | node # 72 - H | ||
+ | | | ||
+ | node #3200 259 - E | ||
+ | |||
+ | The node # for E is an arbitrary one. The compressor may not choose | ||
+ | that location, 3200 is used strictly for demonstration purposes. So at | ||
+ | node #3200 the values would be: | ||
+ | |||
+ | Parent code - 72 | ||
+ | code value - 259 | ||
+ | character | ||
+ | |||
+ | The node #72 is not actually used. As soon as a value less than 255 is | ||
+ | found it is assumed to be the actual value. We can't compress this yet | ||
+ | so the value 72 is sent to the output file(remember that it is sent in 9 | ||
+ | bits). The E then becomes the parent code and a new character code ( H ) | ||
+ | is read in. After again searching the dictionary the phrase EH is not | ||
+ | found. It is added to the dictionary as code number 260. Then we send | ||
+ | the E to the disk and H becomes the new parent code and the next E | ||
+ | becomes the new character code. After searching the dictionary we find | ||
+ | that we can compress HE into the code 259,we want to compress as much as | ||
+ | possible into one code so we make 259 the parent code. There may be a | ||
+ | longer string then HE that can be compressed. The R is read in as the | ||
+ | new character code. The dictionary is searched for the a 259 followed a | ||
+ | R, since it is not found it is added to the dictioary and it looks like | ||
+ | this: | ||
+ | |||
+ | node #72 - H node #69 - E | ||
+ | | ||
+ | node #3200 259 - E node #1600 260 - H | ||
+ | | | ||
+ | node #1262 261 - R | ||
+ | |||
+ | Then the value 259 is sent to the output file (to represent the HE) and | ||
+ | since that is the EOF the R is sent as well,as well as a 256 to indicate | ||
+ | the EOF has been reached. | ||
+ | |||
+ | Decompression is extremely simple. As long as the decompressor maintains | ||
+ | the dictionary as the compressor did, there will be no problems, | ||
+ | for one problem that can be handled as an exceptional case. All of the | ||
+ | little details of increasing the number of bits to read, and when to | ||
+ | flush the dictionary are taken care of by the compressor. So if the | ||
+ | dictionary was increased to 8k, the compressor would have to be set up | ||
+ | to handle a larger dictionary, but the decompressor only does as the | ||
+ | compressed file tells it to and will work with any size dictionary. The | ||
+ | only problem would be that a larger dictionary will creep into the ram | ||
+ | under the rom or possibly even use all available memory, but assuming | ||
+ | that the ram is available the decompressor will not change. The | ||
+ | decompressor would start out reading 9 bits at a time, and starts it | ||
+ | free code at 259 as the compressor did. To use the above input from the | ||
+ | compressor as an example, the output was: | ||
+ | |||
+ | 72 - For the First H | ||
+ | 69 - For the First E | ||
+ | 259 - For the Compressed HE | ||
+ | 82 - For the R | ||
+ | 256 - Eof indicator | ||
+ | |||
+ | To begin decompressing, | ||
+ | (note they will both be 9 bits long). As they are both below 256 they | ||
+ | are at the end of the string and are sent straight to the output file. | ||
+ | The first free code is 259 so that is the value assigned to the phrase | ||
+ | HE. Note when decompressing there is no need for the hashing routine, | ||
+ | the codes are the absolute locations in the dictionary (i.e. If the | ||
+ | dictionary was considered to be an array then the entry number 259 would | ||
+ | be dictionary[259]), | ||
+ | needed. So the decompressor would have an entry that looks like this: | ||
+ | |||
+ | Node # 259 | ||
+ | Parent Code - H | ||
+ | Character | ||
+ | |||
+ | The decompressor will read in the next value (259). Because the node | ||
+ | number is at the end of the compressed string we will have to take the | ||
+ | code value and place it on a stack, and take them off in a | ||
+ | Last-in, | ||
+ | character to go on the stack (in this case the E) will be the last to | ||
+ | come off. The size of the stack is dependent on the size of the | ||
+ | dictionary, so for this implementation we need a stack that is 4k long. | ||
+ | After all the characters from the string have been placed on the stack | ||
+ | they are taken off and sent to the outputfile. | ||
+ | |||
+ | There is one small error that is possible with LZW because of the way | ||
+ | the compressor defines strings. Consider the compression dictionary that | ||
+ | has the following in it: | ||
+ | |||
+ | node # | ||
+ | Value | ||
+ | | ||
+ | 65 65 | ||
+ | | ||
+ | 1262 | ||
+ | 2104 | ||
+ | 2506 | ||
+ | |||
+ | Now if the compressor was to try to compress the string ACUTEACUTEA The | ||
+ | compressor will find a match for the first five characters ' | ||
+ | will send a 262 to the output file. Then it will add the following entry | ||
+ | to the dictionary: | ||
+ | |||
+ | 3099 | ||
+ | |||
+ | Now it will try to compress the remaining characters, and it finds that | ||
+ | it can compress the entire string with the code 263, but notice that the | ||
+ | middle A, the one that was just added onto the end of the string ' | ||
+ | was never sent to the output file. The decompressor will not have the | ||
+ | code 263 defined in it's dictionary. The last code it will have defined | ||
+ | will be 262. This problem is easily remedied though, when the | ||
+ | decompressor does not have a code defined, it takes the first letter | ||
+ | from the last phrase defined and tacks it onto the end of the last | ||
+ | phrase. IE It takes the first letter (the A) from the phrase and adds it | ||
+ | on to the end as code #263. | ||
+ | |||
+ | This particular implementation is fairly slow because it reads a byte | ||
+ | and then writes one, it could be made much faster with some buffering. | ||
+ | It is also limited to compressing and decompressing one file at a time | ||
+ | and has no error checking capabilities. It is meant strictly to teach | ||
+ | LZW compression, | ||
+ | |||
+ | And now for the code: | ||
+ | | ||
+ | SYS 4000 ; sys 999 on a 64 | ||
+ | .DVO 9 ; or whatever drive used for output | ||
+ | .ORG 2500 | ||
+ | .OBJ " | ||
+ | |||
+ | TABLESIZE =5021 | ||
+ | |||
+ | ; THE TABLESIZE IS ACTUALLY 5021, ABOUT 20% LARGER THEN 4K. THIS GIVES | ||
+ | ; THE HASHING ROUTINE SOME ROOM TO MOVE. IF THE TABLE WAS EXACTLY 4K | ||
+ | ; THERE WOULD BE FREQUENT COLLISIONS WHERE DIFFERENT COMBINATIONS OF | ||
+ | ; CHARACTERS WOULD HAVE THE SAME HASH ADDRESS. INCREASING THE TABLE SIZE | ||
+ | ; REDUCES THE NUMBER OF COLLISIONS. | ||
+ | |||
+ | EOS =256 ; eos = End of stream This marks the end of file | ||
+ | |||
+ | FIRSTCODE =259 | ||
+ | MAXCODE =4096 | ||
+ | |||
+ | BUMPCODE =257 ; Whenever a 257 is encountered by the decompressor it | ||
+ | ; increases the number of bits it reads by 1 | ||
+ | |||
+ | FLUSHCODE =258 | ||
+ | |||
+ | TABLEBASE =14336 | ||
+ | |||
+ | DECODESTACK =9300 ; The location of the 4k LIFO stack | ||
+ | |||
+ | ; ORG = DECOMPRESS FILE | ||
+ | ; ORG + 3 = COMPRESS FILE | ||
+ | |||
+ | JMP EXPANDFILE | ||
+ | |||
+ | ; | ||
+ | ; COMPRESSFILE | ||
+ | ; | ||
+ | |||
+ | COMPRESSFILE JSR INITDIC ; EMPTY THE DICTIONARY | ||
+ | LDA #128 | ||
+ | STA BITMASK | ||
+ | LDA #0 | ||
+ | STA RACK | ||
+ | JSR GETCHAR | ||
+ | STA STRINGCODE | ||
+ | LDA #0 | ||
+ | STA STRINGCODE+1 | ||
+ | NEXTCHAR JSR GETCHAR | ||
+ | STA CHARACTER | ||
+ | JSR FINDNODE | ||
+ | LDA ($FE), | ||
+ | | ||
+ | AND ($FE), | ||
+ | CMP #255 ; BE ADDED TO THE DICTIONARY. | ||
+ | BEQ ADDTODICT | ||
+ | LDA ($FE), | ||
+ | STA STRINGCODE+1; | ||
+ | DEY | ||
+ | LDA ($FE),Y | ||
+ | STA STRINGCODE | ||
+ | JMP EOF | ||
+ | | ||
+ | - LDA NEXTCODE,Y | ||
+ | STA ($FE),Y | ||
+ | INY | ||
+ | CPY #5 | ||
+ | BNE - | ||
+ | INC NEXTCODE | ||
+ | BNE + | ||
+ | INC NEXTCODE+1 | ||
+ | + JSR OUTPUT | ||
+ | LDA NEXTCODE+1 | ||
+ | CMP #> | ||
+ | BNE CHECKBUMP | ||
+ | LDA NEXTCODE | ||
+ | CMP #< | ||
+ | BNE CHECKBUMP | ||
+ | LDA #< | ||
+ | STA STRINGCODE | ||
+ | LDA #> | ||
+ | STA STRINGCODE+1 | ||
+ | JSR OUTPUT | ||
+ | JSR INITDIC | ||
+ | JMP CHECKEOF | ||
+ | | ||
+ | CMP NEXTCODE+1 | ||
+ | BNE CHECKEOF | ||
+ | LDA NEXTBUMP | ||
+ | CMP NEXTCODE | ||
+ | BNE CHECKEOF | ||
+ | LDA #> | ||
+ | STA STRINGCODE+1 | ||
+ | LDA #< | ||
+ | STA STRINGCODE | ||
+ | JSR OUTPUT | ||
+ | INC CURRENTBITS | ||
+ | ASL NEXTBUMP | ||
+ | ROL NEXTBUMP+1 | ||
+ | | ||
+ | STA STRINGCODE+1 | ||
+ | LDA CHARACTER | ||
+ | STA STRINGCODE | ||
+ | EOF LDA 144 | ||
+ | BNE DONE | ||
+ | JMP NEXTCHAR | ||
+ | DONE JSR OUTPUT | ||
+ | LDA #> | ||
+ | STA STRINGCODE+1 | ||
+ | LDA #<EOS | ||
+ | STA STRINGCODE | ||
+ | JSR OUTPUT | ||
+ | LDA BITMASK | ||
+ | BEQ + | ||
+ | JSR $FFCC | ||
+ | LDX #3 | ||
+ | JSR $FFC9 | ||
+ | LDA RACK ; SEND WHAT BITS WEREN' | ||
+ | JSR $FFD2 | ||
+ | + JSR $FFCC | ||
+ | LDA #3 | ||
+ | JSR $FFC3 | ||
+ | LDA #2 | ||
+ | JMP $FFC3 | ||
+ | |||
+ | ; | ||
+ | ; INITDIC | ||
+ | ; INITIALIZES THE DICTIONARY, SETS | ||
+ | ; THE NUMBER OF BITS TO 9 | ||
+ | ; | ||
+ | |||
+ | INITDIC LDA #9 | ||
+ | STA CURRENTBITS | ||
+ | LDA #> | ||
+ | STA NEXTCODE+1 | ||
+ | LDA #< | ||
+ | STA NEXTCODE | ||
+ | LDA #>512 | ||
+ | STA NEXTBUMP+1 | ||
+ | LDA #<512 | ||
+ | STA NEXTBUMP | ||
+ | LDA #< | ||
+ | STA $FE | ||
+ | LDA #> | ||
+ | STA $FF | ||
+ | LDA #< | ||
+ | STA $FC | ||
+ | LDA #> | ||
+ | STA $FD | ||
+ | - LDY #0 | ||
+ | LDA #255 ; ALL THE CODE VALUES ARE INIT TO 255+256*255 | ||
+ | STA ($FE), | ||
+ | INY | ||
+ | STA ($FE),Y | ||
+ | CLC | ||
+ | LDA #5 ; EACH ENTRY IN THE TABLE TAKES 5 BYTES | ||
+ | ADC $FE | ||
+ | STA $FE | ||
+ | BCC + | ||
+ | INC $FF | ||
+ | + LDA $FC | ||
+ | BNE + | ||
+ | DEC $FD | ||
+ | + DEC $FC | ||
+ | LDA $FD | ||
+ | ORA $FC | ||
+ | BNE - | ||
+ | RTS | ||
+ | |||
+ | ; | ||
+ | ; GETCHAR | ||
+ | ; | ||
+ | |||
+ | GETCHAR JSR $FFCC | ||
+ | LDX #2 | ||
+ | JSR $FFC6 | ||
+ | JMP $FFCF | ||
+ | |||
+ | ; | ||
+ | ; OUTPUT | ||
+ | ; | ||
+ | |||
+ | OUTPUT LDA #0 ; THE NUMBER OF BITS OUTPUT CAN BE OF A VARIABLE | ||
+ | STA MASK+1 | ||
+ | LDA #1 ; FULL AND THEN IT IS SENT TO THE OUTPUT FILE | ||
+ | LDX CURRENTBITS | ||
+ | DEX | ||
+ | - ASL | ||
+ | ROL MASK+1 | ||
+ | DEX | ||
+ | BNE - | ||
+ | STA MASK | ||
+ | MASKDONE LDA MASK | ||
+ | ORA MASK+1 | ||
+ | BNE + | ||
+ | RTS | ||
+ | + LDA MASK | ||
+ | AND STRINGCODE | ||
+ | STA 3 | ||
+ | LDA MASK+1 | ||
+ | AND STRINGCODE+1 | ||
+ | ORA 3 | ||
+ | BEQ NOBITON | ||
+ | LDA RACK | ||
+ | ORA BITMASK | ||
+ | STA RACK | ||
+ | NOBITON LSR BITMASK | ||
+ | LDA BITMASK | ||
+ | BNE + | ||
+ | JSR $FFCC | ||
+ | LDX #3 | ||
+ | JSR $FFC9 | ||
+ | LDA RACK | ||
+ | JSR $FFD2 | ||
+ | LDA #0 | ||
+ | STA RACK | ||
+ | LDA #128 | ||
+ | STA BITMASK | ||
+ | + LSR MASK+1 | ||
+ | ROR MASK | ||
+ | JMP MASKDONE | ||
+ | |||
+ | ; | ||
+ | ; FINDNODE | ||
+ | ; THIS SEARCHES THE DICTIONARY TILL IT FINDS A PARENT NODE THAT MATCHES | ||
+ | ; THE STRINGCODE AND A CHILD NODE THAT MATCHES THE CHARACTER OR A EMPTY | ||
+ | ; NODE. | ||
+ | ; | ||
+ | |||
+ | ; THE HASHING FUNCTION - THE HASHING FUNCTION IS NEEDED BECAUSE | ||
+ | ; THERE ARE 4096 X 4096 (16 MILLION) DIFFERENT COMBINATIONS OF | ||
+ | ; CHARACTER AND STRINGCODE. BY MULTIPLYING THE CHARACTER AND STRINGCODE | ||
+ | ; IN A FORMULA WE CAN DEVELOP OF METHOD OF FINDING THEM IN THE | ||
+ | ; DICTIONARY. IF THE STRINGCODE AND CHARACTER IN THE DICTIONARY | ||
+ | ; DON'T MATCH THE ONES WE ARE LOOKING FOR WE CALCULATE AN OFFSET | ||
+ | ; AND SEARCH THE DICTIONARY FOR THE RIGHT MATCH OR A EMPTY | ||
+ | ; SPACE IS FOUND. IF AN EMPTY SPACE IS FOUND THEN THAT CHARACTER AND | ||
+ | ; STRINGCODE COMBINATION IS NOT IN THE DICTIONARY | ||
+ | |||
+ | FINDNODE LDA #0 | ||
+ | STA INDEX+1 | ||
+ | LDA CHARACTER | ||
+ | ASL ; CHARACTER AND THE STRING CODE. FOR THOSE WHO | ||
+ | ROL INDEX+1 | ||
+ | EOR STRINGCODE | ||
+ | STA INDEX ; FIND NODE WILL LOOP TILL IT FINDS A NODE | ||
+ | LDA INDEX+1 | ||
+ | EOR STRINGCODE+1 | ||
+ | STA INDEX+1 | ||
+ | ORA INDEX | ||
+ | BNE + | ||
+ | LDX #1 | ||
+ | STX OFFSET | ||
+ | DEX | ||
+ | STX OFFSET+1 | ||
+ | JMP FOREVELOOP | ||
+ | + SEC | ||
+ | LDA #< | ||
+ | SBC INDEX | ||
+ | STA OFFSET | ||
+ | LDA #> | ||
+ | SBC INDEX+1 | ||
+ | STA OFFSET+1 | ||
+ | |||
+ | FOREVELOOP JSR CALCULATE | ||
+ | LDY #0 | ||
+ | LDA ($FE),Y | ||
+ | INY | ||
+ | AND ($FE),Y | ||
+ | CMP #255 | ||
+ | BNE + | ||
+ | LDY #0 | ||
+ | RTS | ||
+ | + INY | ||
+ | - LDA ($FE),Y | ||
+ | CMP STRINGCODE-2, | ||
+ | BNE + | ||
+ | INY | ||
+ | CPY #5 | ||
+ | BNE - | ||
+ | LDY #0 | ||
+ | RTS | ||
+ | + SEC | ||
+ | LDA INDEX | ||
+ | SBC OFFSET | ||
+ | STA INDEX | ||
+ | LDA INDEX+1 | ||
+ | SBC OFFSET+1 | ||
+ | STA INDEX+1 | ||
+ | AND #128 | ||
+ | BEQ FOREVELOOP | ||
+ | CLC | ||
+ | LDA #< | ||
+ | ADC INDEX | ||
+ | STA INDEX | ||
+ | LDA #> | ||
+ | ADC INDEX+1 | ||
+ | STA INDEX+1 | ||
+ | JMP FOREVELOOP | ||
+ | |||
+ | ; | ||
+ | ; CALCULATE | ||
+ | ; TAKES THE VALUE IN INDEX AND CALCULATES ITS LOCATION IN THE DICTIONARY | ||
+ | ; | ||
+ | |||
+ | CALCULATE LDA INDEX | ||
+ | STA $FE | ||
+ | LDA INDEX+1 | ||
+ | STA $FF | ||
+ | ASL $FE | ||
+ | ROL $FF | ||
+ | ASL $FE | ||
+ | ROL $FF | ||
+ | CLC | ||
+ | LDA INDEX | ||
+ | ADC $FE | ||
+ | STA $FE | ||
+ | LDA INDEX+1 | ||
+ | ADC $FF | ||
+ | STA $FF | ||
+ | CLC | ||
+ | LDA #< | ||
+ | ADC $FE | ||
+ | STA $FE | ||
+ | LDA #> | ||
+ | ADC $FF | ||
+ | STA $FF | ||
+ | LDY #0 | ||
+ | RTS | ||
+ | |||
+ | ; | ||
+ | ; DECODESTRING | ||
+ | ; | ||
+ | |||
+ | DECODESTRING TYA ; DECODESTRING PUTS THE STRING ON THE STACK | ||
+ | STA COUNT ; IN A LIFO FASHION. | ||
+ | LDX #> | ||
+ | CLC | ||
+ | ADC #< | ||
+ | STA $FC | ||
+ | STX $FD | ||
+ | LDA #0 | ||
+ | STA COUNT+1 | ||
+ | - LDA INDEX+1 | ||
+ | BEQ + | ||
+ | JSR CALCULATE | ||
+ | LDY #4 | ||
+ | LDA ($FE),Y | ||
+ | LDY #0 | ||
+ | STA ($FC),Y | ||
+ | LDY #2 | ||
+ | LDA ($FE),Y | ||
+ | STA INDEX | ||
+ | INY | ||
+ | LDA ($FE),Y | ||
+ | STA INDEX+1 | ||
+ | JSR INFC | ||
+ | JMP - | ||
+ | + LDY #0 | ||
+ | LDA INDEX | ||
+ | STA ($FC),Y | ||
+ | INC COUNT | ||
+ | BNE + | ||
+ | INC COUNT+1 | ||
+ | + RTS | ||
+ | |||
+ | ; | ||
+ | ; INPUT | ||
+ | ; | ||
+ | |||
+ | INPUT LDA #0 ; THE INPUT ROUTINES IS USED BY THE DECOMPRESSOR | ||
+ | STA MASK+1 | ||
+ | STA RETURN | ||
+ | STA RETURN+1 | ||
+ | LDA #1 | ||
+ | LDX CURRENTBITS | ||
+ | DEX | ||
+ | - ASL | ||
+ | ROL MASK+1 | ||
+ | DEX | ||
+ | BNE - | ||
+ | STA MASK | ||
+ | - LDA MASK | ||
+ | ORA MASK+1 | ||
+ | BEQ INPUTDONE | ||
+ | LDA BITMASK | ||
+ | BPL + | ||
+ | JSR GETCHAR | ||
+ | STA RACK | ||
+ | + LDA RACK | ||
+ | AND BITMASK | ||
+ | BEQ + | ||
+ | LDA MASK | ||
+ | ORA RETURN | ||
+ | STA RETURN | ||
+ | LDA MASK+1 | ||
+ | ORA RETURN+1 | ||
+ | STA RETURN+1 | ||
+ | + LSR MASK+1 | ||
+ | ROR MASK | ||
+ | LSR BITMASK | ||
+ | LDA BITMASK | ||
+ | BNE + | ||
+ | LDA #128 | ||
+ | STA BITMASK | ||
+ | + JMP - | ||
+ | INPUTDONE RTS | ||
+ | |||
+ | ; | ||
+ | ; EXPANDFILE | ||
+ | ; WHERE THE DECOMPRESSION IS DONE | ||
+ | ; | ||
+ | |||
+ | EXPANDFILE LDA #0 | ||
+ | STA RACK | ||
+ | LDA #128 | ||
+ | STA BITMASK | ||
+ | START JSR INITDIC | ||
+ | JSR INPUT | ||
+ | LDA RETURN+1 | ||
+ | STA OLDCODE+1 | ||
+ | LDA RETURN | ||
+ | STA CHARACTER | ||
+ | STA OLDCODE | ||
+ | CMP #<EOS | ||
+ | BNE + | ||
+ | LDA RETURN+1 | ||
+ | CMP #>EOS | ||
+ | BNE + | ||
+ | JMP CLOSE | ||
+ | + JSR $FFCC | ||
+ | LDX #3 | ||
+ | JSR $FFC9 | ||
+ | LDA OLDCODE | ||
+ | JSR $FFD2 | ||
+ | NEXT JSR INPUT | ||
+ | LDA RETURN | ||
+ | STA NEWCODE | ||
+ | LDA RETURN+1 | ||
+ | STA NEWCODE+1 | ||
+ | CMP #1 ; All of the special codes Flushcode, | ||
+ | BNE ++ ; Have 1 for a MSB. | ||
+ | LDA NEWCODE | ||
+ | CMP #< | ||
+ | BNE + | ||
+ | INC CURRENTBITS | ||
+ | JMP NEXT | ||
+ | + CMP #< | ||
+ | BEQ START | ||
+ | CMP #<EOS | ||
+ | BNE + | ||
+ | JMP CLOSE | ||
+ | + SEC ; Here we compare the newcode just read in to the | ||
+ | LDA NEWCODE | ||
+ | SBC NEXTCODE | ||
+ | STA 3 | ||
+ | LDA NEWCODE+1 | ||
+ | SBC NEXTCODE+1 | ||
+ | ORA 3 | ||
+ | BCC + | ||
+ | LDA CHARACTER | ||
+ | STA DECODESTACK | ||
+ | LDA OLDCODE | ||
+ | STA INDEX | ||
+ | LDA OLDCODE+1 | ||
+ | STA INDEX+1 | ||
+ | LDY #1 | ||
+ | BNE ++ | ||
+ | + LDA NEWCODE | ||
+ | STA INDEX ; So DECODESTRING has a place to start | ||
+ | LDA NEWCODE+1 | ||
+ | STA INDEX+1 | ||
+ | LDY #0 | ||
+ | + JSR DECODESTRING | ||
+ | LDY #0 | ||
+ | LDA ($FC),Y | ||
+ | STA CHARACTER | ||
+ | INC $FC | ||
+ | BNE + | ||
+ | INC $FD | ||
+ | + JSR $FFCC | ||
+ | LDX #3 | ||
+ | JSR $FFC9 | ||
+ | L1 LDA COUNT+1 | ||
+ | ORA COUNT | ||
+ | BEQ + | ||
+ | JSR DECFC | ||
+ | LDY #0 | ||
+ | LDA ($FC),Y | ||
+ | JSR $FFD2 | ||
+ | JMP L1 | ||
+ | + LDA NEXTCODE | ||
+ | STA INDEX ; that was just entered. | ||
+ | LDA NEXTCODE+1 | ||
+ | STA INDEX+1 | ||
+ | JSR CALCULATE | ||
+ | LDY #2 ; The last character read in is tacked onto the end | ||
+ | LDA OLDCODE | ||
+ | STA ($FE), | ||
+ | | ||
+ | LDA OLDCODE+1 | ||
+ | STA ($FE),Y | ||
+ | INY | ||
+ | LDA CHARACTER | ||
+ | STA ($FE),Y | ||
+ | INC NEXTCODE | ||
+ | BNE + | ||
+ | INC NEXTCODE+1 | ||
+ | + LDA NEWCODE | ||
+ | STA OLDCODE | ||
+ | LDA NEWCODE+1 | ||
+ | STA OLDCODE+1 | ||
+ | JMP NEXT | ||
+ | CLOSE JSR $FFCC | ||
+ | LDA #2 | ||
+ | JSR $FFC3 | ||
+ | LDA #3 | ||
+ | JMP $FFC3 | ||
+ | |||
+ | DECFC LDA $FC | ||
+ | BNE + | ||
+ | DEC $FD | ||
+ | + DEC $FC | ||
+ | LDA COUNT | ||
+ | BNE + | ||
+ | DEC COUNT+1 | ||
+ | + DEC COUNT | ||
+ | RTS | ||
+ | INFC INC $FC | ||
+ | BNE + | ||
+ | INC $FD | ||
+ | + INC COUNT | ||
+ | BNE + | ||
+ | INC COUNT+1 | ||
+ | + RTS | ||
+ | |||
+ | NEXTCODE .WOR 0 | ||
+ | STRINGCODE .WOR 0 | ||
+ | CHARACTER .BYT 0 | ||
+ | NEXTBUMP .WOR 0 | ||
+ | CURRENTBITS .BYT 0 | ||
+ | RACK .BYT 0 | ||
+ | BITMASK .BYT 0 | ||
+ | MASK .WOR 0 | ||
+ | INDEX .WOR 0 | ||
+ | OFFSET .WOR 0 | ||
+ | RETURN .WOR 0 | ||
+ | COUNT .WOR 0 | ||
+ | NEWCODE .WOR 0 | ||
+ | OLDCODE .WOR 0 | ||
+ | TEST .BYT 0 | ||
+ | |||
+ | TO DRIVE THE ML I WROTE THIS SMALL BASIC PROGRAM. NOTE THAT CHANNEL TWO IS | ||
+ | ALWAYS THE INPUT AND CHANNEL THREE IS ALWAYS THE OUTPUT. EX AND CO MAY BE | ||
+ | CHANGED TO SUIT WHATEVER LOCATIONS THE PROGRAM IS REASSEMBLED AT. | ||
+ | |||
+ | 1 IFA=.THENA=1: | ||
+ | 10 EX=2500: | ||
+ | 15 PRINT" | ||
+ | 20 GETA$: | ||
+ | 30 INPUT" | ||
+ | 40 INPUT" | ||
+ | 50 OPEN2, | ||
+ | 60 IFA$=" | ||
+ | 70 IFA$=" | ||
+ | 80 END | ||
+ | |||
+ | For those interested in learning more about data | ||
+ | compression/ | ||
+ | Book' written by Mark Nelson. I learned a great deal from reading this | ||
+ | book. It explains all of the major data compression methods. (huffman | ||
+ | coding, dictionary type compression such as LZW, arithmatic coding, | ||
+ | speech compression and lossy graphics compression) | ||
+ | |||
+ | Questions or comments are welcome, they may be directed to me at : | ||
+ | |||
+ | Internet | ||
+ | Genie : b.lucier1 | ||
+ | |||
+ | ------------------------------------------------------------------------------ | ||
+ | |||
+ | begin 644 lzw.ml | ||
+ | MQ`E, | ||
+ | M\`ZQ_HWN# | ||
+ | M& | ||
+ | MC> | ||
+ | M#: | ||
+ | M#: | ||
+ | M_ABI!67^A? | ||
+ | MKO(-R@HN]@W*T/ | ||
+ | M]`V-\PU.]`VM]`W0&"#, | ||
+ | M" | ||
+ | MEPLXJ9WM]PV-^0VI$^WX# | ||
+ | MP`70]*``8# | ||
+ | M^`U, | ||
+ | MJ3AE_X7_H`!@F(W]#: | ||
+ | ML? | ||
+ | M`: | ||
+ | M#? | ||
+ | MC? | ||
+ | M_ZT!# | ||
+ | MT`-, | ||
+ | M# | ||
+ | M\`T@R`V@`+' | ||
+ | M[PV1_N[K# | ||
+ | M_<; | ||
+ | *```````````````` | ||
+ | ` | ||
+ | end | ||
+ | |||
+ | crc32 for lzw.ml = 2460116527 | ||
+ | |||
+ | begin 644 lzw.bas | ||
+ | M`0@<" | ||
+ | M-3`S`%`(# | ||
+ | M022SL2)# | ||
+ | M1DDD.HO# | ||
+ | M3R0ZB\, | ||
+ | M1D\DJB(L4" | ||
+ | ` | ||
+ | end | ||
+ | |||
+ | crc32 for lzw.bas = 100674089 | ||
+ | |||
+ | =============================================================================== | ||
+ | </ | ||
+ | ===== THREE-KEY ROLLOVER for the C-128 and C-64. ===== | ||
+ | < | ||
+ | by Craig Bruce < | ||
+ | |||
+ | 1. INTRODUCTION | ||
+ | |||
+ | This article examines a three-key rollover mechanism for the keyboards of the | ||
+ | C-128 and C-64 and presents Kernal-wedge implementations for both machines. | ||
+ | Webster' | ||
+ | machine will act sensibly if you are holding down one key and then press | ||
+ | another without releasing the first (or even press a third key while holding | ||
+ | down two others). | ||
+ | without rollover can be quite annoying; you get a lot of missing letters. | ||
+ | |||
+ | Another annoying property of the kernel keyscanning is joystick interference. | ||
+ | If you move the joystick plugged into port #1, you will notice that some junk | ||
+ | keystrokes result. | ||
+ | checking if the joystick is pressed and ignoring the keyboard if it is. | ||
+ | |||
+ | The reason that a 3-key rollover is implemented instead of the more general | ||
+ | N-key rollover is that scanning the keyboard becomes more and more unreliable | ||
+ | as more keys are held down. Key " | ||
+ | like you are holding down a certain key when you really are not. So, by | ||
+ | limiting the number of keys scanned to 3, some of this can be avoided. | ||
+ | will get strange results if you hold down more than three keys at a time, and | ||
+ | even sometimes when holding down 3 or less. The " | ||
+ | Commodore, Control, Alternate, and CapsLock) don't count in the 3 keys of | ||
+ | rollover, but they do make the keyboard harder to read correctly. | ||
+ | Fortunately, | ||
+ | without releasing any keys. | ||
+ | |||
+ | 2. USER GUIDE | ||
+ | |||
+ | Using these utilities is really easy - you just type away like normal. | ||
+ | install the C-128 version, enter: | ||
+ | |||
+ | BOOT " | ||
+ | |||
+ | and you're in business. | ||
+ | go to work. The program loads into memory at addresses $1500-$17BA (5376-6074 | ||
+ | decimal), so you'll want to watch out for conflicts with other utilities. | ||
+ | This program also takes over the IRQ vector and the BASIC restart vector | ||
+ | ($A00). | ||
+ | program, you must reset the machine (or poke the kernel values back into the | ||
+ | vectors); it does not uninstall itself. | ||
+ | |||
+ | Loading the C-64 version is a bit trickier, so a small BASIC loader program is | ||
+ | provided. | ||
+ | " | ||
+ | and execute it (with a SYS 50432). | ||
+ | The program takes over the IRQ and NMI vectors and only gives them back to the | ||
+ | kernel upon uninstallation. | ||
+ | |||
+ | Something that you may or may not know about the C-64 is that its keys can be | ||
+ | made to repeat by poking to address 650 decimal. | ||
+ | repeating of all keys. POKE650,0 will enable only the repeating of the SPACE, | ||
+ | DELETE, and CURSOR keys. POKE650,64 will disable the repeating of all keys. | ||
+ | An unusual side effect of changing this to either full repeat or no repeat is | ||
+ | that holding down SHIFT+COMMODORE (character set shift) will repeat rapidly. | ||
+ | |||
+ | To see the rollover in action, hold down the " | ||
+ | press " | ||
+ | with the kernal. | ||
+ | notice that the " | ||
+ | feature - it avoids problems if you don't release all of the keys you are | ||
+ | holding down, at once). | ||
+ | releasing the " | ||
+ | scanner. | ||
+ | combinations that cause problems; more on this below. | ||
+ | |||
+ | Also, take a spaz on the joystick plugged into port #1 and observe that no | ||
+ | garbage gets typed in. This was an annoying problem with the kernel of both | ||
+ | the 64 and 128 and has lead many different games to picking between joystick | ||
+ | #1 and #2 as the primary controller. | ||
+ | to either Keyscan-128/ | ||
+ | |||
+ | 3. KEYBOARD SCANNING | ||
+ | |||
+ | The Kernal scans the keyboard sixty times a second to see what keys you are | ||
+ | holding down. Because of hardware peculiarities, | ||
+ | techniques that will give different results. | ||
+ | |||
+ | 3.1. SCANNING EXAMPLE | ||
+ | |||
+ | An example program is included to demonstrate different keyboard scanning | ||
+ | techniques possible. | ||
+ | |||
+ | BOOT " | ||
+ | |||
+ | On a C-64, you must: | ||
+ | |||
+ | LOAD " | ||
+ | |||
+ | and then: | ||
+ | |||
+ | SYS 4864 | ||
+ | |||
+ | The same program works on both machines. | ||
+ | will be displayed on the 40-column screen, as scanned by different techniques. | ||
+ | The leftmost one is scanned from top to bottom " | ||
+ | left scans from bottom to top " | ||
+ | keyboard sideways, and the rightmost matrix scans the keys from top to bottom | ||
+ | " | ||
+ | |||
+ | The mapping of keyscan matrix positions to keys is as follows: | ||
+ | |||
+ | ROWS: \ | ||
+ | poke \ | ||
+ | $DC00 | ||
+ | | ||
+ | 255-1 | DOWN | | ||
+ | | ||
+ | 255-2 |LEFT-SH| | ||
+ | | ||
+ | 255-4 | | ||
+ | | ||
+ | 255-8 | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | 255-128 | ||
+ | | ||
+ | |||
+ | The following table contains the additional keys which must be scanned on the | ||
+ | C128 (but which are not displayed by the example scanning program). | ||
+ | |||
+ | ROWS: \ | ||
+ | poke \ | ||
+ | $D02F | ||
+ | | ||
+ | 255-1 | | ||
+ | | ||
+ | 255-2 | | ||
+ | | ||
+ | 255-4 |NO-SCRL| RIGHT | LEFT | DOWN | | ||
+ | | ||
+ | |||
+ | These tables are presented on page 642 of the Commodore 128 Programmer' | ||
+ | Reference Guide. | ||
+ | and location 197 on the C64 are calculated based on the above tables. | ||
+ | entry in the " | ||
+ | has a scan code of 0, the " | ||
+ | entry on the second scan line in the " | ||
+ | etc., all the way down to code 63. The scan codes for the 128 go all the way | ||
+ | to 87, continuing in the second table like the first. | ||
+ | |||
+ | You will notice some strange effects of the different scanning techniques when | ||
+ | you hold down multiple keys. More on this below. | ||
+ | #1 around. | ||
+ | |||
+ | 3.2. SCANNING HARDWARE | ||
+ | |||
+ | To scan the 128 keyboard, you must poke a value into $DC00 (CIA#1 port A) and | ||
+ | $D02F (VIC chip keyboard select port) to select the row to be scanned. | ||
+ | Data Direction Register for this port will be set to all outputs by the | ||
+ | Kernal, so you don't have to worry about it. Each bit of $DC00 and the three | ||
+ | least significant bits of $D02F are used for selecting rows. A " | ||
+ | that a row IS selected, and a " | ||
+ | value to use for selecting among the various rows are given in the two tables | ||
+ | in the previous section. | ||
+ | |||
+ | Using one bit per row allows you to select multiple rows at the same time. It | ||
+ | can be useful to select all rows at one time or no rows. To read the row that | ||
+ | has been selected, simply peek at location $DC01 (CIA#1 port B). Each bit | ||
+ | will tell you whether the corresponding key is currently being held down or | ||
+ | not. Again, we have reverse logic; a " | ||
+ | down, and a " | ||
+ | corresponding to the keys are given as the column headings in the tables in | ||
+ | the previous section. | ||
+ | switch, it is recommended that you " | ||
+ | following way: | ||
+ | |||
+ | again: | ||
+ | lda $dc01 | ||
+ | cmp $dc01 | ||
+ | bne again | ||
+ | |||
+ | So, to scan the entire keyboard, you simply select each scan row in some | ||
+ | order, and read and remember the keys held down on the row. As it turns out, | ||
+ | you have to be a bit careful of exactly how you " | ||
+ | is a shortcut that you can take. In order to find out if any key is being | ||
+ | held down in one operation, all you have to do is select all rows at once and | ||
+ | see if there are any " | ||
+ | held down somewhere; if not, then there is no key being held down, so you | ||
+ | don't have to bother scanning the entire keyboard. | ||
+ | keyscanning significantly, | ||
+ | scanned every 1/60 of a second. | ||
+ | |||
+ | As mentioned above, joystick #1 will interfere with the Kernal reading the | ||
+ | keyboard. | ||
+ | port A, the same place that the keyboard read is wired in. So, whenever a | ||
+ | switch in the joystick is pushed, the corresponding bit of the keyboard scan | ||
+ | register will be forced to " | ||
+ | regardless of which scan row is selected. | ||
+ | un-select all scan rows and still notice " | ||
+ | then we would know that the joystick was being pushed and would interfere with | ||
+ | our keyboard scanning, so we could abort keyboard scanning and handle this | ||
+ | case as if no keys were being held down. | ||
+ | |||
+ | It still would be possible but unlikely that the user could push the joystick | ||
+ | in the middle of us scanning the keyboard and screw up our results, so to | ||
+ | defend against this, we check for the joystick being pushed both before and | ||
+ | after scanning the keyboard. | ||
+ | of these times, then we throw out the results and assume that no keys are held | ||
+ | down. This way, the only way that a user could screw up the scanning is if | ||
+ | he/she/it were to press a switch after we begin scanning and release it before | ||
+ | we finish scanning. | ||
+ | |||
+ | You get the same deal for keyboard scanning on the 64, except you only need to | ||
+ | use $DC00 for selecting the scan rows. Also note that you will not be able to | ||
+ | play with keyboard scanning from BASIC because of the interrupt reading of the | ||
+ | keyboard. | ||
+ | the keyboard hardware, or interrupt scanning can come along at any time and | ||
+ | change all of the register settings. | ||
+ | |||
+ | 3.3. SCANNING SOURCE CODE | ||
+ | |||
+ | The four keyboard scanning techniques of the example program are presented | ||
+ | below. | ||
+ | |||
+ | pa = $dc00 ;row select | ||
+ | pb = $dc01 ;column read | ||
+ | ddra = $dc02 ;ddr for row select | ||
+ | ddrb = $dc03 ;ddr for column read | ||
+ | scanTable .buf 8 ;storage for scan | ||
+ | mask = $03 ;work location | ||
+ | |||
+ | The code is as follows, in Buddy format. | ||
+ | stores the results in the " | ||
+ | |||
+ | ------------------+------------------+------------------+------------------+ | ||
+ | Row forward fast | Row backward fast| Column right | Row forward slow | ||
+ | ------------------+------------------+------------------+------------------+ | ||
+ | sei | ||
+ | ldx #0 | ldx #7 | lda #$00 | ldx #0 | ||
+ | lda #$fe | lda #$7f | sta ddra | lda #$fe | ||
+ | sta pa | sta pa | lda #$ff | sta mask | ||
+ | nextRow = * | ||
+ | - lda pb |- lda pb | ldy #7 | lda mask | ||
+ | cmp pb | cmp pb | lda #$7f | sta pa | ||
+ | bne - | ||
+ | eor #$ff | eor #$ff | nextCol = * | ||
+ | sta scanTable,x | sta scanTable,x | lda mask | bne - | ||
+ | sec | ||
+ | rol pa | ror pa |- lda pa | sta scanTable,x | ||
+ | inx | ||
+ | cpx #8 | bpl nextRow | ||
+ | bcc nextRow | ||
+ | cli | ||
+ | rts | ||
+ | ------------------+------------------+ | ||
+ | |- asl | ||
+ | The forward " | ||
+ | the scan row selection mask into | ||
+ | the row selection register and | ||
+ | shifts the " | ||
+ | " | ||
+ | using a "rol $dc00" instruction. | ||
+ | This would probably be the obvious | ||
+ | solution to an optimizing assembler | ||
+ | programmer. | ||
+ | reason not quite understood by this | lda #$00 | | ||
+ | author, there are " | ||
+ | problems with this approach. | ||
+ | you were to hold down the two keys | ||
+ | " | ||
+ | would notice that these two keys | ||
+ | are on the same column of two successive rows. If you hold them both down, | ||
+ | you will see the two positions become active, but so will the same column of | ||
+ | all successive rows after the " | ||
+ | not actually held down. You will get an inaccurate reading if bad keys are | ||
+ | held down simultaneously. | ||
+ | This is because although the hardware returns a " | ||
+ | converts that into a " | ||
+ | everyone will get this same result, but if your keyboard is wired the same as | ||
+ | mine, you will. | ||
+ | |||
+ | The backward " | ||
+ | scanning, except for the direction of the scan and the direction of the | ||
+ | " | ||
+ | results of the forward and backward scan together would eliminate the shadow, | ||
+ | but this will not work since any rows between the rows containing the two keys | ||
+ | held down will be incorrectly read as being active. | ||
+ | |||
+ | The columnwise right scanning is the most complicated because the rows must be | ||
+ | converted into columns, to allow the scan matrix to be interpreted as before. | ||
+ | Also, the Data Direction Registers have to be changed. | ||
+ | combinging row-wise scanning with columnwise scanning would give better | ||
+ | results, and it probably would, if it weren' | ||
+ | If you hold down two or more keys on the same scan row, say " | ||
+ | of the keys will flicker or disappear, giving an inaccurate reading. | ||
+ | |||
+ | The forward " | ||
+ | what the Kernal uses (as near as I can figure - their code is extremely | ||
+ | convoluted). | ||
+ | that the row selection mask is shifted in a working storage location and poked | ||
+ | into the CIA register, rather than being shifted in place. | ||
+ | this makes a difference, but it does. There is still a problem with this | ||
+ | technique, but this problem occurs with all techniques. | ||
+ | three keys that form three " | ||
+ | then the missing corner will be read as being held down also. For example, if | ||
+ | you hold down " | ||
+ | that you are holding down the " | ||
+ | " | ||
+ | combinations will still be interpreted correctly. | ||
+ | keys such as SHIFT or CONTROL will add one more key to the hardware scanning | ||
+ | (but will not be counted in the three-key rollover), making inaccurate results | ||
+ | more likely if you are holding down multiple other keys at the same time. | ||
+ | |||
+ | 4. THE C-128 KEYSCANNER | ||
+ | |||
+ | This section gives the source code for the C-128 implementation of the | ||
+ | three-key rollover. | ||
+ | extended to work with the extra keys of the 128. It was a bit of a pain | ||
+ | wedging into the Kernal, since there is not a convenient indirect JMP into | ||
+ | scanning the keyboard, like there are for decoding and buffering pressed keys. | ||
+ | A rather lengthy IRQ " | ||
+ | point where it JSRs to the keyscanning routine. | ||
+ | the form of a " | ||
+ | |||
+ | Before scanning the keyboard, we check to see if joystick #1 is pushed and if | ||
+ | a key is actually pressed. | ||
+ | repeat handling in the ROM. If a key is held down, we scan the keyboard and | ||
+ | then examine the result. | ||
+ | CONTROL, ALT, and CAPS LOCK), put them into location $D3 (shift flags) in bit | ||
+ | postitions 1, 2, 4, 8, and 16, respectively, | ||
+ | matrix. | ||
+ | processor I/O port. This is good, because otherwise we could not abort | ||
+ | scanning if it were the only key held down. | ||
+ | |||
+ | Then we scan the keymatrix for the first three keys that are being held down, | ||
+ | or as many as are held down if less than three. | ||
+ | these keys into a 3-element array. | ||
+ | array from the previous scan and we check for different keys being in the two | ||
+ | arrays. | ||
+ | then the use has released a key, so we set a flag to inhibit interpretation of | ||
+ | keys and pretend that no keys are held down. This is to eliminate undesirable | ||
+ | effects of having other keys held down repeat if you release the most recently | ||
+ | pushed key first. | ||
+ | new keys are discovered in the next step. | ||
+ | |||
+ | If there are keys in the new array that are not in the old, then the user has | ||
+ | just pressed a new key, so that new key goes to the head of the old array and | ||
+ | we stop comparing the arrays there. | ||
+ | array is poked into the Kernal "key held down" location for the Kernal to | ||
+ | interpret later. | ||
+ | then each of the new keys will be picked up on successive keyboard scans and | ||
+ | will be interpreted as just being pushed. | ||
+ | " | ||
+ | will appear on the screen. | ||
+ | |||
+ | When we are done interpreting the keys, we check the joystick once more and if | ||
+ | it is still inactive, we present the most recently pushed down key to the | ||
+ | Kernal and JMP into the ROM keyboard decoding routine. | ||
+ | |||
+ | Unlike in previous issues, this source code is here in literal form; just | ||
+ | extract everything between the " | ||
+ | The source is in Buddy assembler format. | ||
+ | |||
+ | -----=----- | ||
+ | ;3-Key Rollover-128 by Craig Bruce 18-Jun-93 for C= Hacking magazine | ||
+ | |||
+ | .org $1500 | ||
+ | .obj " | ||
+ | |||
+ | scanrows = 11 | ||
+ | rollover = 3 | ||
+ | |||
+ | pa = $dc00 | ||
+ | pb = $dc01 | ||
+ | pk = $d02f | ||
+ | |||
+ | jmp initialInstall | ||
+ | |||
+ | ;ugly IRQ patch code. | ||
+ | |||
+ | irq = * ;$1503 | ||
+ | .byte $d8, | ||
+ | .byte $19, | ||
+ | .byte $31, | ||
+ | .byte $a5, | ||
+ | .byte $09, | ||
+ | .byte $d0, | ||
+ | .byte $ad, | ||
+ | .byte $b0, | ||
+ | .byte $01, | ||
+ | .byte $a5, | ||
+ | .byte $aa, | ||
+ | |||
+ | ; | ||
+ | |||
+ | main = * | ||
+ | lda #0 ; | ||
+ | sta pa | ||
+ | sta pk | ||
+ | - lda pb | ||
+ | cmp pb | ||
+ | bne - | ||
+ | cmp #$ff | ||
+ | beq noKeyPressed | ||
+ | |||
+ | jsr checkJoystick | ||
+ | bcc joystickPressed | ||
+ | jsr keyscan | ||
+ | jsr checkJoystick | ||
+ | bcc joystickPressed | ||
+ | jsr shiftdecode | ||
+ | jsr keydecode | ||
+ | jsr keyorder | ||
+ | ; determine which key to present to the Kernal | ||
+ | lda $033e ;set up for and dispatch to Kernal | ||
+ | sta $cc | ||
+ | lda $033f | ||
+ | sta $cd | ||
+ | ldx #$ff | ||
+ | bit ignoreKeys | ||
+ | bmi ++ | ||
+ | lda prevKeys+0 | ||
+ | cmp #$ff | ||
+ | bne + | ||
+ | lda $d3 | ||
+ | beq ++ | ||
+ | lda #88 | ||
+ | + sta $d4 | ||
+ | tay | ||
+ | jmp ($033a) | ||
+ | |||
+ | | ||
+ | lda #$7f | ||
+ | sta pa | ||
+ | lda #$ff | ||
+ | sta pk | ||
+ | |||
+ | | ||
+ | lda #$ff ; | ||
+ | ldx #rollover-1 | ||
+ | - sta prevKeys,x | ||
+ | dex | ||
+ | bpl - | ||
+ | jsr scanCaps | ||
+ | ldx #$ff | ||
+ | lda #0 | ||
+ | sta ignoreKeys | ||
+ | |||
+ | + lda #88 ;present "no key held" to Kernal | ||
+ | sta $d4 | ||
+ | tay | ||
+ | jmp $c697 | ||
+ | |||
+ | initialInstall = * ;install wedge: set restore vector, print message | ||
+ | jsr install | ||
+ | lda #< | ||
+ | ldy #> | ||
+ | sta $0a00 | ||
+ | sty $0a01 | ||
+ | ldx #0 | ||
+ | - lda installMsg, | ||
+ | beq + | ||
+ | jsr $ffd2 | ||
+ | inx | ||
+ | bne - | ||
+ | + rts | ||
+ | |||
+ | | ||
+ | .byte 13 | ||
+ | .asc " | ||
+ | .byte 0 | ||
+ | |||
+ | reinstall = * ; | ||
+ | jsr install | ||
+ | jmp $4003 | ||
+ | |||
+ | install = * ;guts of installation: | ||
+ | | ||
+ | lda #<irq | ||
+ | ldy #>irq | ||
+ | sta $0314 | ||
+ | sty $0315 | ||
+ | cli | ||
+ | ldx #rollover-1 | ||
+ | lda #$ff | ||
+ | - sta prevKeys,x | ||
+ | dex | ||
+ | bpl - | ||
+ | lda #0 | ||
+ | sta ignoreKeys | ||
+ | rts | ||
+ | |||
+ | mask = $cc | ||
+ | |||
+ | keyscan = * ;scan the (extended) keyboard using the forward | ||
+ | ldx #$ff ; | ||
+ | ldy #$ff | ||
+ | lda #$fe | ||
+ | sta mask+0 | ||
+ | lda #$ff | ||
+ | sta mask+1 | ||
+ | jmp + | ||
+ | | ||
+ | - lda pb | ||
+ | cmp pb | ||
+ | bne - | ||
+ | sty pa | ||
+ | sty pk | ||
+ | eor #$ff | ||
+ | sta scanTable,x | ||
+ | sec | ||
+ | rol mask+0 | ||
+ | rol mask+1 | ||
+ | + lda mask+0 | ||
+ | sta pa | ||
+ | lda mask+1 | ||
+ | sta pk | ||
+ | inx | ||
+ | cpx #scanrows | ||
+ | bcc nextRow | ||
+ | rts | ||
+ | |||
+ | shiftValue = $d3 | ||
+ | |||
+ | shiftRows .byte $01, | ||
+ | shiftBits .byte $80, | ||
+ | shiftMask .byte $01, | ||
+ | |||
+ | shiftdecode = * ;see which " | ||
+ | jsr scanCaps | ||
+ | ldy #4 ; | ||
+ | - ldx shiftRows,y | ||
+ | lda scanTable,x | ||
+ | and shiftBits,y | ||
+ | beq + | ||
+ | lda shiftMask,y | ||
+ | ora shiftValue | ||
+ | sta shiftValue | ||
+ | lda shiftBits,y | ||
+ | eor #$ff | ||
+ | and scanTable,x | ||
+ | sta scanTable,x | ||
+ | + dey | ||
+ | bpl - | ||
+ | rts | ||
+ | |||
+ | scanCaps = * ;scan the CAPS LOCK key from the processor I/O port | ||
+ | - lda $1 | ||
+ | cmp $1 | ||
+ | bne - | ||
+ | eor #$ff | ||
+ | and #$40 | ||
+ | lsr | ||
+ | lsr | ||
+ | sta shiftValue | ||
+ | rts | ||
+ | |||
+ | newpos = $cc | ||
+ | keycode = $d4 | ||
+ | xsave = $cd | ||
+ | |||
+ | keydecode = * ;get the scan codes of the first three keys held down | ||
+ | ldx # | ||
+ | lda #$ff | ||
+ | - sta newKeys,x | ||
+ | dex | ||
+ | bpl - | ||
+ | ldy #0 | ||
+ | sty newpos | ||
+ | ldx #0 | ||
+ | stx keycode | ||
+ | |||
+ | | ||
+ | lda scanTable,x | ||
+ | beq decodeContinue | ||
+ | ;at this point, we know that the row has a key held | ||
+ | ldy keycode | ||
+ | - lsr | ||
+ | bcc ++ | ||
+ | | ||
+ | stx xsave ; up to 3 keys | ||
+ | ldx newpos | ||
+ | cpx #rollover | ||
+ | bcs + | ||
+ | tya | ||
+ | sta newKeys,x | ||
+ | inc newpos | ||
+ | + ldx xsave | ||
+ | pla | ||
+ | + iny | ||
+ | cmp #$00 | ||
+ | bne - | ||
+ | |||
+ | | ||
+ | clc | ||
+ | lda keycode | ||
+ | adc #8 | ||
+ | sta keycode | ||
+ | inx | ||
+ | cpx #scanrows | ||
+ | bcc decodeNextRow | ||
+ | rts | ||
+ | |||
+ | ;keyorder: determine what key to present to the Kernal as being logically the | ||
+ | ;only one pressed, based on which keys previously held have been released and | ||
+ | ;which new keys have just been pressed | ||
+ | |||
+ | keyorder = * | ||
+ | ;** remove old keys no longer held from old scan code array | ||
+ | ldy #0 | ||
+ | | ||
+ | lda prevKeys, | ||
+ | cmp #$ff | ||
+ | beq ++ | ||
+ | ldx # | ||
+ | - cmp newKeys,x | ||
+ | beq + | ||
+ | dex | ||
+ | bpl - | ||
+ | | ||
+ | tax | ||
+ | - lda prevKeys+1, | ||
+ | sta prevKeys+0, | ||
+ | inx | ||
+ | cpx #rollover-1 | ||
+ | bcc - | ||
+ | lda #$ff | ||
+ | sta prevKeys+rollover-1 | ||
+ | sta ignoreKeys | ||
+ | + iny ;check next old key | ||
+ | cpy #rollover | ||
+ | bcc nextRemove | ||
+ | |||
+ | ;** insert new keys at front of old scan code array | ||
+ | + ldy #0 | ||
+ | | ||
+ | lda newKeys, | ||
+ | cmp #$ff | ||
+ | beq ++ | ||
+ | ldx # | ||
+ | - cmp prevKeys,x | ||
+ | beq + | ||
+ | dex | ||
+ | bpl - | ||
+ | | ||
+ | ldx #rollover-2 | ||
+ | - lda prevKeys+0, | ||
+ | sta prevKeys+1, | ||
+ | dex | ||
+ | bpl - | ||
+ | lda #0 | ||
+ | sta ignoreKeys | ||
+ | pla | ||
+ | sta prevKeys+0 | ||
+ | ldy # | ||
+ | + iny | ||
+ | cpy #rollover | ||
+ | bcc nextInsert | ||
+ | + rts ;now, the head of the old scan code array contains | ||
+ | ; the scan code to present to the Kernal, and other | ||
+ | ; positions represent keys that are also held down | ||
+ | ; that have already been processed and therefore can | ||
+ | ; be ignored until they are released | ||
+ | |||
+ | checkJoystick = * ; | ||
+ | lda #$ff ; | ||
+ | sta pa ; | ||
+ | sta pk | ||
+ | - lda pb | ||
+ | cmp pb | ||
+ | bne - | ||
+ | cmp #$ff | ||
+ | lda #$7f ; | ||
+ | sta pa ; | ||
+ | lda #$ff | ||
+ | sta pk | ||
+ | rts | ||
+ | |||
+ | ;global variables | ||
+ | |||
+ | scanTable | ||
+ | newKeys | ||
+ | ignoreKeys .buf 1 ; | ||
+ | ; new key has been pressed, stop all key | ||
+ | ; repeating | ||
+ | prevKeys | ||
+ | -----=----- | ||
+ | |||
+ | And that's all there is to it. :-) | ||
+ | |||
+ | 5. THE C-64 KEYSCANNER | ||
+ | |||
+ | The boot program for the C-64 keyscanner is as follows: | ||
+ | |||
+ | 10 d=peek(186) | ||
+ | 20 if a=1 then 60 | ||
+ | 30 a=1 | ||
+ | 40 load" | ||
+ | 50 goto 10 | ||
+ | 60 sys 49152+5*256 | ||
+ | |||
+ | It is very much like boot programs for other machine language programs that | ||
+ | don't load at the start of BASIC. | ||
+ | device accessed, and activate it. | ||
+ | |||
+ | A listing of the C-64 keyscanning code is not presented here because it is so | ||
+ | similar to the C-128 listing. | ||
+ | Kernal patches and the keyboard scanning (because the three extra rows don't | ||
+ | have to be scanned). | ||
+ | again, to get at the call to the key scanning. | ||
+ | over the BASIC reset vector (since there isn't one), the NMI vector is | ||
+ | taken over to insure the survival of the key scanner after a RUN/ | ||
+ | A bit of its preamble also had to be copied out of ROM to get at the good | ||
+ | stuff. | ||
+ | |||
+ | 6. UUENCODED FILES | ||
+ | |||
+ | Here are the binary executables in uuencoded form. The CRC32s of the four | ||
+ | files are as follows: | ||
+ | |||
+ | crc32 = 3398956287 for " | ||
+ | crc32 = 2301926894 for " | ||
+ | crc32 = 1767081474 for " | ||
+ | crc32 = 1604419896 for " | ||
+ | |||
+ | begin 640 keyscan128 | ||
+ | M`!5,' | ||
+ | M4`: | ||
+ | M$M" | ||
+ | M$=" | ||
+ | MK0'< | ||
+ | M_RRT%S`QK; | ||
+ | M(-T6HO^I`(VT%ZE8A=2H3)?& | ||
+ | M# | ||
+ | MM1? | ||
+ | M.";, | ||
+ | MIA< | ||
+ | M`JG_G; | ||
+ | MR, | ||
+ | MZ.`" | ||
+ | M%\H0]ZD`C; | ||
+ | 9_XTOT& | ||
+ | ` | ||
+ | end | ||
+ | begin 640 keyscan64.boot | ||
+ | M`0@." | ||
+ | M15E30T%.-C0B+$0L, | ||
+ | )(" | ||
+ | ` | ||
+ | end | ||
+ | begin 640 keyscan64 | ||
+ | M`, | ||
+ | M\XV' | ||
+ | MZJD`C0#< | ||
+ | MZX7VHO\L>,< | ||
+ | M(+7& | ||
+ | M24Y35$%, | ||
+ | MC7C' | ||
+ | MT`-L`H`@O/ | ||
+ | MS0'< | ||
+ | M@<: | ||
+ | M=<? | ||
+ | MYABERVD(A< | ||
+ | M]: | ||
+ | MJ0" | ||
+ | *```````````````` | ||
+ | ` | ||
+ | end | ||
+ | begin 640 keyshow | ||
+ | M`!-," | ||
+ | MHAX@%Q0@3!1, | ||
+ | M!ZE_C0#< | ||
+ | MI0.-`=RM`-S-`-S0^*+_C@'< | ||
+ | M6& | ||
+ | MRA# | ||
+ | M$S@F`^C@" | ||
+ | 6!T8" | ||
+ | ` | ||
+ | end | ||
+ | ================================================================================ | ||
+ | </ | ||
+ | ===== In the Next Issue: ===== | ||
+ | < | ||
+ | |||
+ | Next Issue: | ||
+ | |||
+ | Tech-tech - more resolution to vertical shift | ||
+ | |||
+ | One time half of the demos had pictures waving horizontally on the width | ||
+ | of the whole screen. This effect is named tech-tech and it is done using | ||
+ | character graphics. How exactly and is the same possible with sprites ? | ||
+ | |||
+ | THE DESIGN OF ACE-128/64 | ||
+ | |||
+ | Design of ACE-128/64 command shell environment (and kernel replacement). | ||
+ | will cover the organization, | ||
+ | the still-under-development but possibly catching-on kernel replacement for | ||
+ | the 128 and 64. The article will also discuss future directions and designs | ||
+ | for the ACE environment. | ||
+ | other kernel replacements, | ||
+ | </ |
magazines/chacking6.txt · Last modified: 2015-04-17 04:34 by 127.0.0.1