magazines:chacking5
no way to compare when less than two revisions
Differences
This shows you the differences between two versions of the page.
— | magazines:chacking5 [2015-04-17 04:34] (current) – created - external edit 127.0.0.1 | ||
---|---|---|---|
Line 1: | Line 1: | ||
+ | < | ||
+ | | ||
+ | | ||
+ | | ||
+ | @@@@@ | ||
+ | @@@@@ @@@@ @@@@ @@ @@@@@ | ||
+ | @@@@@ @@ @@ @@@@ @@ | ||
+ | | ||
+ | @@@@@ @@ @@ @@@@@@@@ | ||
+ | @@@@@ @@@@ @@@@ @@@@ @@@@ @@@@@ | ||
+ | @@@@@ @@ | ||
+ | | ||
+ | | ||
+ | | ||
+ | ----------------------------------------------------------------------------- | ||
+ | Editor' | ||
+ | by Craig Taylor | ||
+ | |||
+ | It seems that each issue of C= Hacking has always began with a " | ||
+ | late but here it is message." | ||
+ | that - This issue was originally scheduled to be out the middle of January | ||
+ | but due to several delays in obtaining articles and my delaying trying to | ||
+ | debug the multi-tasking source code it's been held up until now. | ||
+ | | ||
+ | My apologies to the authors who have had their articles into me on time - | ||
+ | school is coming first for me and having to do a lot of coding for several | ||
+ | classes was the major contributing factor to the delays. | ||
+ | |||
+ | Now, after the apologies are out of the way - Let's take a look at what has | ||
+ | happened since last time I wrote. | ||
+ | |||
+ | - RUN magazine is no longer with us. | ||
+ | |||
+ | As one of the last hold-outs I was expecting RUN magazine to keep on printing | ||
+ | until the Commodore 64/ | ||
+ | decided it wouldn' | ||
+ | US magazine in publication for the Commodore (6502 based) computers that I am | ||
+ | aware of. Speaking of Twin Cities (not sure if he's combining the 64/128 or | ||
+ | just coming out with seperate Twin Cities magazines) does anybody know or | ||
+ | have any information on when the next issue will be out? Or has my | ||
+ | lastest issue just not been sent out? | ||
+ | |||
+ | As I was writing this I got the latest issue of Twin Cities which has | ||
+ | expanded to C=64 coverage also. The new issue looks very nice, about 53 | ||
+ | pages of so of good decent material. I'd recommend get a subscription for | ||
+ | those of you who are looking to still hear about new Commodore products. | ||
+ | |||
+ | I'd like to get people' | ||
+ | people will think will probably be the main source of information for C= | ||
+ | owners. | ||
+ | newsgroup but I'm wondering about individuals who do not have access to | ||
+ | such a newsgroup and do not have access to the internet. Let me know what | ||
+ | you think - hopefully through a friend w/ access to the internet. Sort of | ||
+ | a catch-22 I guess. | ||
+ | |||
+ | - A Mail-Server has been setup to automate sending issue requests. | ||
+ | |||
+ | The full details of how to use the Mail-Server is in a documentation file | ||
+ | contained within but this mail-server (whose source code is available for | ||
+ | anyone who wishes to see it written in VAX DCL code) also allows file | ||
+ | requests which will be uuencoded and sent to you. I am trying to have all of | ||
+ | the programs in each issue available via request as for some people it is | ||
+ | a minor pain trying to extract and compile the programs contained within. | ||
+ | |||
+ | - I saw a note recently that the speed-up board work was still being done. | ||
+ | |||
+ | Does anybody know anything further about this? I'm interested in this and | ||
+ | how it would be carried out / done but aside from an occasional post here | ||
+ | and there about it I actually hear very little. | ||
+ | |||
+ | - There is also work on an Ansi C compiler being done. | ||
+ | |||
+ | Recently a group of people (about 9 currently) are working on a C compiler | ||
+ | for the C=64 and C=128 which will eventually support the full ANSI C | ||
+ | library. A large list of extensions have been proposed and the compiler | ||
+ | will probably be released as either shareware or possibly, public domain. | ||
+ | |||
+ | Ack! - This magazine keeps growing. The last issue was approx. | ||
+ | somewhere around 3000 lines, this one is just a tad over 6000. I'm | ||
+ | sure that we're not suffering the quality just because of the | ||
+ | quantity. :-) Be sure to take a look at the previous back issues | ||
+ | available via the Mail-Server and don't be afraid to suggest comments | ||
+ | or suggestions. While usually the authors are too busy to take ideas | ||
+ | for new programs we always welcome to hear how useful you find certain | ||
+ | programs included herein etc. | ||
+ | |||
+ | Also I am looking for articles on any type of software project, hardware | ||
+ | project or general theory articles that you would like to submit. Just | ||
+ | leave me a message via email at " | ||
+ | that I've just signed up for a GENIE account and can be reached there via | ||
+ | C.TAYLOR37 once my account is approved. | ||
+ | |||
+ | ============================================================================= | ||
+ | |||
+ | Please note that this issue and prior ones are available via anonymous | ||
+ | FTP from ccosun.caltech.edu under pub/ | ||
+ | mailserver which is documented in this issue. | ||
+ | |||
+ | ============================================================================= | ||
+ | | ||
+ | NOTICE: Permission is granted to re-distribute 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 greater than 5 US. Dollars or equivlent may be charged for | ||
+ | library service / diskette costs for this " | ||
+ | |||
+ | ============================================================================= | ||
+ | </ | ||
+ | ====== In this Issue: ====== | ||
+ | < | ||
+ | Mail-Server Documentation | ||
+ | |||
+ | This articles describes how to access the mail-server for Commodore Hacking | ||
+ | and includes a list of currently available files and back-issues. | ||
+ | |||
+ | Stretching Sprites | ||
+ | |||
+ | It's possible to expand sprites to more than twice their original size, but | ||
+ | there is no need to expand all of them equally. This article examins how to | ||
+ | expand them 2,3, or more multiples of their original size. | ||
+ | |||
+ | Rob Hubbard' | ||
+ | |||
+ | This article written by Anthony McSweeney, presents the valuable source to | ||
+ | Rob Hubbard' | ||
+ | or 30 musics, including such classics as Thing on a Spring (Gremlin Graphics), | ||
+ | Commando (Elite), Thrust (Firebird), International Karate (System 3), and | ||
+ | Proteus (also known as Warhawk, by Firebird). | ||
+ | |||
+ | ZPM3 and ZCCP Enhancements for CP/M Plus from Simeon Cran | ||
+ | |||
+ | Although all the articles to date in C= Hacking have focused on 6510/ 8502 | ||
+ | programming, | ||
+ | C128 CP/M users should be aware of the benefits of a new set of enhancements | ||
+ | to the operating system that offers inreased speed and flexibility as well | ||
+ | as new features. If that isn't enough, this package will also run ZCPR 3.3 | ||
+ | utilities and applications that won't run under standard CP/M Plus. | ||
+ | |||
+ | Multi-Tasking on the C=128 - Part 1 | ||
+ | |||
+ | This article examines the rudiments of Multi-Tasking and also details the | ||
+ | system calls in the Multi-Tasking package to be released in the next issue | ||
+ | of C= Hacking. | ||
+ | |||
+ | LITTLE RED WRITER: MS-DOS file reader/ | ||
+ | |||
+ | This article is an extension on Little Red Reader which was presented in the | ||
+ | last issue and allows for reading and writing of MS-Dos diskettes from and to | ||
+ | 1571/81 drives. | ||
+ | |||
+ | ============================================================================= | ||
+ | </ | ||
+ | ====== Mail-Server Documentation ====== | ||
+ | < | ||
+ | by Craig Taylor (duck@pembvax1.pembroke.edu) | ||
+ | |||
+ | What is a mail-server? | ||
+ | |||
+ | A mailserver is an automated job that will scan my mail file for messages | ||
+ | with a subject line of " | ||
+ | | ||
+ | on me and you. Easier for me so that I don't have to deal with 50+ messages | ||
+ | each month asking for files to be sent out and also insures that your files | ||
+ | that you requested will be sent within 24 hours. In addition it allows | ||
+ | files to be more easily sent and accessed in case you are not able to | ||
+ | | ||
+ | |||
+ | If you have FTP access please see the Editor' | ||
+ | | ||
+ | using that somewhat quicker to use. | ||
+ | |||
+ | How to use the mail-server / What it is. | ||
+ | |||
+ | This mail-server is intended to help me keep track / more easily update my | ||
+ | | ||
+ | C= Hacking mailed to them. | ||
+ | |||
+ | To use it simply send a message to " | ||
+ | | ||
+ | body of the mail message: | ||
+ | |||
+ | Currently the following commands are supported: | ||
+ | |||
+ | help - sends current documentation f file list | ||
+ | send iss< | ||
+ | subscribe | ||
+ | | ||
+ | everytime the catalog changes. | ||
+ | catalog | ||
+ | psend name - send uuencoded binary. | ||
+ | |||
+ | Commands no longer supported: | ||
+ | |||
+ | status | ||
+ | (use the help file) | ||
+ | |||
+ | Please note that the mailserver is only run at 2:00 AM EST. | ||
+ | |||
+ | Catalog List - Last update February 27, 1993. | ||
+ | |||
+ | iss1. - C= Hacking, Issue #1 | ||
+ | iss2. - C= Hacking, Issue #2 | ||
+ | iss3. - C= Hacking, Issue #3 | ||
+ | iss4. - C= Hacking, Issue #4 | ||
+ | iss5. - C= Hacking, Issue #5 | ||
+ | contents.lis | ||
+ | mailserv.012493 | ||
+ | |||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | |||
+ | -- Temporary Files -> Or files that will be deleted as needed for space | ||
+ | |||
+ | | ||
+ | | ||
+ | |||
+ | NOTE: Files marked with " | ||
+ | sent to you in uuencoded form. They may _not_ be requested via SEND. | ||
+ | |||
+ | ============================================================================= | ||
+ | </ | ||
+ | ====== The Demo Corner: Stretching Sprites ====== | ||
+ | < | ||
+ | by Pasi ' | ||
+ | Written: 16-May-91 | ||
+ | |||
+ | (All timings are in PAL, principles will apply to NTSC too) | ||
+ | |||
+ | You might have heard that it is possible to expand sprites to more than | ||
+ | twice their original size. Imagine a sprite scroller with 6-times expanded | ||
+ | sprites. However, there is no need to expand all of them equally. Using | ||
+ | this technique, it is possible to make easy sinus effects and constantly | ||
+ | expanding and shrinking letters. | ||
+ | |||
+ | The VIC (video interface controller) may be fooled in many things. One of | ||
+ | them is the vertical expansion of sprites. If you clear the expand flag and | ||
+ | then set it back straight away, VIC will think it has only displayed the | ||
+ | first one of the expanded lines. If we do the trick again, VIC will continue | ||
+ | to display the same data again and again. But why does VIC behave like this ? | ||
+ | |||
+ | |||
+ | _Logic gates will tell the truth_ | ||
+ | |||
+ | It is not really a bug, but a feature. The hardware design to implement the | ||
+ | vertical enlargement was just as simple as possible. Those, who do not care | ||
+ | about hardware should skip this part... The whole y-enlargement is handled | ||
+ | with five simple logical ports. Each sprite has an associated Set-Reset | ||
+ | flip-flop to tell whether to jump to the next sprite line (add three bytes | ||
+ | to the data counter) or not. | ||
+ | |||
+ | Let's call the state of the flip-flop Q and the inputs R (reset) and S (set). | ||
+ | The function of a SR flip-flop is quite simple: if R is one, Q goes to zero, | ||
+ | if S is one, Q goes to one. Otherwise the state of the flip-flop does not | ||
+ | change. In this case the flip-flop is Set, if either the Y-enlargement bit | ||
+ | is zero or the state of the flip-flop is zero at the end of a scan line. The | ||
+ | flip-flop is reset, if both the state and the Y-enlargement are ones at the | ||
+ | end of the line. | ||
+ | |||
+ | When you clear the bit in the vertical expansion register, the flip-flop will | ||
+ | be set regardless of the electron beam position on the scan line. If you | ||
+ | set the bit again before the end of the line, the flip-flop will be cleared | ||
+ | and VIC will be displaying the same sprite line again. In other words, VIC | ||
+ | will think that it is starting to display the second line of the expanded | ||
+ | sprite row. This way any of the lines in any of the sprites may be stretched | ||
+ | as wanted. | ||
+ | |||
+ | .---- Current flipflop state (if one, enables add to sprite pointer) | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | |||
+ | So, simply, at any time, if vertical expand is zero, the add enable is set | ||
+ | to one. At the end of the line - before adding - the state is cleared if | ||
+ | vertical expand is one. | ||
+ | |||
+ | |||
+ | _Even odder ?_ | ||
+ | |||
+ | Something very weird happens when we clear the expansion bit right when VIC | ||
+ | is adding three to the sprite image counters. The values in the counters will | ||
+ | be increased only by two, and the data is then read from the wrong place. | ||
+ | |||
+ | Normally the display of a sprite ends when VIC has shown all of the 21 | ||
+ | lines of the sprite (the counter will end up to $3f). If there has been a | ||
+ | counter mixup, $3f is not reached after 21 lines and VIC will go on counting | ||
+ | and will display the sprite again, now normally. If we fool the counter only | ||
+ | once, the counter value $3f is reached when the sprite is displayed twice. | ||
+ | |||
+ | |||
+ | _Fiddling_ | ||
+ | |||
+ | I don't think the distorted counter effect can be used for anything, but | ||
+ | there is many things where the variable stretching could be used. When you | ||
+ | open the borders, you can be sure that there is a constant amount of time, | ||
+ | if you stretch the sprites to the whole lenght of the area. You may stretch | ||
+ | only the first and last lines, stretch the other lines by a constant or | ||
+ | using a table, or using a variable table or any of the combinations possible. | ||
+ | |||
+ | |||
+ | _A raster routine is a must_ | ||
+ | |||
+ | Because you have to access the VIC registers on each line during the stretch, | ||
+ | you need some kind of routine which can do other kinds of tricks besides the | ||
+ | stretch. You can open the side borders and change the background color and | ||
+ | maybe you have to shift the screen (and the bad lines with it) downwards. | ||
+ | [See previous C=Hacking Issues for talk about raster interrupts.] | ||
+ | |||
+ | Look at the demo program. In the beginning of the raster routine there is | ||
+ | first some timing, then a loop that lasts exactly 46 clock cycles. It takes | ||
+ | exactly one scan line to execute. Inside the loop we first do the necassary | ||
+ | modifications to the vertical scroll register, then we change the background | ||
+ | color and then we open the side borders. And finally we handle the stretching | ||
+ | using the stretch data, where a zero-bit means that the corresponding sprite | ||
+ | will be stretched. A one-bit means that VIC is allowed to go to the next line | ||
+ | of the sprite data. | ||
+ | |||
+ | |||
+ | _Stretching takes time_ | ||
+ | |||
+ | Besides showing the stretched sprites we need time to generate the stretching | ||
+ | data, unless of course, the stretch is constant. We have to have 20 | ||
+ | one-bits for each sprite in our table. It is not feasible to determine the | ||
+ | state of each byte in the table, instead you clear the table and plot the | ||
+ | needed bits. | ||
+ | |||
+ | The routine is quite straightforward, | ||
+ | to make it faster. First we load Y with the stretch of the first line (the | ||
+ | y-coordinate of the data). Then we use it as an index to the table and plot | ||
+ | the right bit and increase Y with the expansion value. Then we do it again | ||
+ | until we have all of the 20 bits scattered to the table. The last sprite line | ||
+ | will then stretch until we stop the stretching, because the last line is | ||
+ | not allowed to be drawn. | ||
+ | |||
+ | |||
+ | _Speed is everything_ | ||
+ | |||
+ | The calculation itself is easy, but optimizing the routine is not. If all | ||
+ | of the sprites are stretched equally (by integer amounts) and from the same | ||
+ | position, the routine is the fastest possible. | ||
+ | and smooth stretch. | ||
+ | and thus also needs more processor time. If each sprite has to be stretched | ||
+ | individually, | ||
+ | |||
+ | The fastest routine I have ever written uses some serious selfmodification | ||
+ | tricks. There are also some other tricks to speed up the stretch, but they | ||
+ | are all secret ones.. :-) Well, what the h*ck, I will include it anyway. | ||
+ | By the time you read this I have already made a faster routine.. | ||
+ | |||
+ | You can speed up that routine (by 17%) by unrolling the inner loop, but you | ||
+ | have to use a different addressing mode for ORA (zero-page). You also need | ||
+ | to place some restrictions to the tables used.. If you unroll both loops, | ||
+ | you can get ~25% faster routine than the Fore!-version. | ||
+ | |||
+ | |||
+ | _Demo program_ | ||
+ | |||
+ | I tried to collect all of the main principles of stretching and raster | ||
+ | routines to the demo program. I use the term " | ||
+ | execution is tightly synchronized to the electron beam and to the screen | ||
+ | display. The program may be unclear in places, but I wanted to keep it as | ||
+ | short as possible. The routine opens the side borders, scrolls the screen | ||
+ | vertically, changes the background color and stretches the sprites. | ||
+ | |||
+ | The stretcher routine allows different y-position and amount of expansion | ||
+ | for each sprite. This routine uses 1/8 fractions to do the counting, and so | ||
+ | it is much too slow to use in a real demo. VIC registers are initialized | ||
+ | from a table, instead of setting them separately. Interrupt position is one | ||
+ | line above the sprites. The program does not open the top or bottom borders. | ||
+ | (I usually use a NMI to open the vertical borders, so that I only need to | ||
+ | use one raster-IRQ position.) | ||
+ | |||
+ | I tried to make a NTSC version, but I couldn' | ||
+ | There are also less cycles available so you can't stretch all of the sprites | ||
+ | individually in NTSC (with this routine that is..). | ||
+ | |||
+ | -------------------------------------------------------------------------- | ||
+ | Fast-stretch from Megademo92 (part: Fore!) | ||
+ | |||
+ | SINPOS | ||
+ | SINSPEED | ||
+ | YSINPOS | ||
+ | YSINSPEED | ||
+ | MASK Bit mask for passess (usually $01, | ||
+ | |||
+ | YSINUS | ||
+ | STRETCH | ||
+ | SIZET | ||
+ | DATA Stretch data table (cleared before this routine) | ||
+ | |||
+ | [xx] marks selfmodification. For example loop counter, bit mask and | ||
+ | index to the stretch and size data tables are stored straight in the | ||
+ | code. | ||
+ | |||
+ | 0b90 lda #$06 ; Number of sprites-1 (here I used only 7 sprites) | ||
+ | 0b92 sta $0b96 | ||
+ | 0b95 ldx # | ||
+ | 0b97 clc ; Clear carry for adc | ||
+ | 0b98 lda SINPOS, | ||
+ | 0b9b sta $0bd1 ; Set low bytes of indices | ||
+ | 0b9e sta $0bb8 | ||
+ | 0ba1 adc SINSPEED, | ||
+ | 0ba4 and #$7f ; Table is 128 bytes (twice) | ||
+ | 0ba6 sta SINPOS, | ||
+ | 0ba9 lda YSINPOS, | ||
+ | 0bac adc YSINSPEED,x ; Add Y sinus speed | ||
+ | 0baf sta YSINPOS, | ||
+ | 0bb2 tay ; Position to index register | ||
+ | 0bb3 lda YSINUS, | ||
+ | 0bb6 sec ; adc either sets or clears carry, we have to set it | ||
+ | 0bb7 sbc SIZET[1e] | ||
+ | 0bba clc ; | ||
+ | 0bbb tay ; MaxSize/2 < Y-sinus < AreaHeight-MaxSize/ | ||
+ | 0bbc lda MASK, | ||
+ | 0bbf sta $0bcb ; Store mask | ||
+ | 0bc2 sta $0bdb | ||
+ | 0bc5 ldx #$13 ; 19 lines here + 1 after | ||
+ | 0bc7 lda DATA, | ||
+ | 0bca ora #[$01] | ||
+ | 0bcc sta DATA,y | ||
+ | 0bcf tya | ||
+ | 0bd0 adc STRETCH[1e], | ||
+ | 0bd3 tay | ||
+ | 0bd4 dex ; decrease counter | ||
+ | 0bd5 bne $0bc7 ; Do the 19 lines | ||
+ | 0bd7 lda DATA, | ||
+ | 0bda ora #[$01] | ||
+ | 0bdc sta DATA,y | ||
+ | 0bdf dec $0b96 ; Next sprite(s) | ||
+ | 0be2 bpl $0b95 | ||
+ | 0be4 rts | ||
+ | |||
+ | Timings: | ||
+ | ------- | ||
+ | clear 128 bytes: 514 + 12 cycles | ||
+ | 7 passes | ||
+ | |||
+ | The unrolled clear routine consists of one load (lda #$00) and 128 | ||
+ | store instructions (sta $nnnn). 12 cycles are counted for JSR/RTS. | ||
+ | |||
+ | Stretching of 8 sprites would take slightly less than 80 lines, which is one | ||
+ | fourth of the total raster time. Displaying a 128-line high stretcher takes | ||
+ | about 130 lines (counting sprite setup and synchronization), | ||
+ | of lines more. Total 212 lines leaves 100 lines (6300 cycles) free for other | ||
+ | activities in a PAL system. In a NTSC system you would have only 50 lines | ||
+ | left. | ||
+ | |||
+ | |||
+ | A simple basic routine to create the stretch data: | ||
+ | ------------------------------------------------- | ||
+ | a=0:for f=0 to 127: | ||
+ | poke Table+f+128, | ||
+ | |||
+ | This will also handle the ' | ||
+ | handle fractions in the stretcher routine. The use of a table also gives the | ||
+ | opportunity to have a separate size for each sprite line. The table does | ||
+ | not need to be a sinus, it could have triangle or any other ' | ||
+ | long as the minimum value in the table (sprite line size) is 1. | ||
+ | |||
+ | |||
+ | A basic routine to do the size/2 table: | ||
+ | -------------------------------------- | ||
+ | a=0:for f=0 to 19: | ||
+ | for f=0 to 127:poke STable+f, | ||
+ | |||
+ | -------------------------------------------------------------------------- | ||
+ | _Stretcher program_ | ||
+ | |||
+ | YSCROLL= $CF00 ; Vertical scroll table (moves bad lines) | ||
+ | STRETCH= $CF80 ; Stretch table | ||
+ | COLORS= | ||
+ | YCOORD= | ||
+ | HEIGHT= | ||
+ | YPOS= 52 ; Sprite y-coordinate | ||
+ | SPRCOL= | ||
+ | |||
+ | |||
+ | *= $C000 | ||
+ | |||
+ | SEI ; Disable interrupts | ||
+ | LDA #$7F | ||
+ | STA $DC0D ; Disable timer interrupts | ||
+ | LDA #< | ||
+ | STA $0314 | ||
+ | LDA #>IRQ | ||
+ | STA $0315 | ||
+ | LDX #$3E ; We create a sprite to cassette buffer | ||
+ | LOOP LDA SPRITE,X | ||
+ | STA $0340,X | ||
+ | DEX | ||
+ | BPL LOOP | ||
+ | LDX #7 | ||
+ | LOOP2 LDA #$D ; Set the sprite image pointers | ||
+ | STA $07F8,X | ||
+ | LDA # | ||
+ | STA $D027,X | ||
+ | DEX | ||
+ | BPL LOOP2 | ||
+ | LDX #$26 | ||
+ | LOOP3 LDA VIDEO, | ||
+ | STA $D000,X | ||
+ | DEX | ||
+ | BPL LOOP3 | ||
+ | LDX #$7F ; Create the y-scroll table | ||
+ | LOOP4 | ||
+ | AND #$07 | ||
+ | ORA #$10 ; Non-blank screen | ||
+ | STA YSCROLL,X | ||
+ | LDA #$00 | ||
+ | STA COLORS,X | ||
+ | DEX | ||
+ | BPL LOOP4 | ||
+ | STA $3FFF | ||
+ | LDX #23 ; Create a color table | ||
+ | LOOP5 LDA BACK,X | ||
+ | STA COLORS+8,X | ||
+ | STA COLORS+32,X | ||
+ | STA COLORS+56,X | ||
+ | STA COLORS+80,X | ||
+ | STA COLORS+96,X | ||
+ | DEX | ||
+ | BPL LOOP5 | ||
+ | JSR CHANGE | ||
+ | CLI ; Enable interrupts | ||
+ | RTS | ||
+ | |||
+ | IRQ LDX #$01 | ||
+ | LDY #$08 ; ' | ||
+ | NOP ; Timing | ||
+ | NOP | ||
+ | NOP | ||
+ | BIT $EA ; (Add NOP's etc. for NTSC) | ||
+ | LOOP6 LDA YSCROLL-1,X ; Move the screen (bad lines) | ||
+ | STA $D011 4 | ||
+ | LDA COLORS, | ||
+ | DEC $D016 ; Open the border | ||
+ | STA $D021 ; Set the background color 4 | ||
+ | STY $D016 ; Screen to normal | ||
+ | LDA STRETCH, | ||
+ | STA $D017 4 | ||
+ | EOR #$FF 2 | ||
+ | STA $D017 4 | ||
+ | ; (Add NOP for NTSC +2) | ||
+ | INX ; Increase counter | ||
+ | BPL LOOP6 ; Loop 127 times + 3 | ||
+ | --- | ||
+ | LDA #1 ; Ack the raster interrupt | ||
+ | STA $D019 +17(sprites) | ||
+ | --- | ||
+ | JSR DOSTRETCH | ||
+ | |||
+ | JMP $EA31 | ||
+ | |||
+ | SPRITE | ||
+ | BYT 0, | ||
+ | BYT $5D, | ||
+ | BYT $BB, | ||
+ | BYT 0, | ||
+ | BYT 2, | ||
+ | BYT $37, | ||
+ | BYT 0, | ||
+ | |||
+ | VIDEO BYT $E8, | ||
+ | BYT $E0, | ||
+ | BYT $FF, | ||
+ | ; Init values for VIC - sprites, interrupts, colors | ||
+ | |||
+ | BACK BYT 0, | ||
+ | BYT 0, | ||
+ | BYT 0, | ||
+ | |||
+ | DOSTRETCH | ||
+ | LDX #31 ; Clear the table | ||
+ | LDA #0 ; (Unrolling will help the speed, | ||
+ | LOOP7 STA STRETCH, | ||
+ | STA STRETCH+32, | ||
+ | STA STRETCH+64, | ||
+ | STA STRETCH+96, | ||
+ | DEX | ||
+ | BPL LOOP7 | ||
+ | STA REMAIND+1 | ||
+ | LDA #7 | ||
+ | STA COUNTER+1 | ||
+ | LDA #$80 | ||
+ | STA MASK+1 | ||
+ | COUNTER LDX #$00 ; The argument is the counter | ||
+ | LDY YCOORD, | ||
+ | LDA HEIGHT, | ||
+ | STA ADD+1 | ||
+ | LDX #20 ; Handle 20 lines | ||
+ | LOOP8 LDA STRETCH+2,Y | ||
+ | MASK ORA #$00 | ||
+ | STA STRETCH+2, | ||
+ | STY YADD+1 | ||
+ | REMAIND LDA #0 | ||
+ | AND #7 ; Previous remainder | ||
+ | ADD ADC #0 ; | ||
+ | STA REMAIND+1 | ||
+ | LSR | ||
+ | LSR | ||
+ | LSR | ||
+ | CLC ; Take the integer part | ||
+ | YADD ADC #0 | ||
+ | TAY ; New value to y-register | ||
+ | DEX | ||
+ | BNE LOOP8 | ||
+ | LSR MASK+1 | ||
+ | DEC COUNTER+1 | ||
+ | BPL COUNTER | ||
+ | |||
+ | CHANGE | ||
+ | ASL ; Sprite height changes with 2x speed | ||
+ | AND #$3F | ||
+ | TAY ; 64 bytes long table | ||
+ | INC CHANGE+1 | ||
+ | LDX #7 ; Do eight sprites | ||
+ | LOOP9 LDA SINUS,Y | ||
+ | LSR | ||
+ | LSR | ||
+ | CLC ; Use the same sinus as y-data | ||
+ | ADC #8 | ||
+ | STA HEIGHT, | ||
+ | TYA | ||
+ | ADC #10 ; Next sprite enlargement will be 10 entries | ||
+ | AND #$3F ; | ||
+ | TAY | ||
+ | DEX | ||
+ | BPL LOOP9 | ||
+ | LDX #7 | ||
+ | LDA CHANGE+1 | ||
+ | AND #$3F | ||
+ | TAY | ||
+ | LOOP10 | ||
+ | STA YCOORD,X | ||
+ | TYA | ||
+ | ADC #10 ; Next sprite position is 10 entries from this one | ||
+ | AND #$3F | ||
+ | TAY | ||
+ | DEX | ||
+ | BPL LOOP10 | ||
+ | RTS | ||
+ | |||
+ | SINUS BYT $20, | ||
+ | BYT $36, | ||
+ | BYT $3F, | ||
+ | BYT $36, | ||
+ | BYT $20, | ||
+ | BYT 9, | ||
+ | BYT 9, | ||
+ | |||
+ | -------------------------------------------------------------------------- | ||
+ | Stretching sprites demo program basic loader (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 78A9648D1403A9C08D1503A23EBD96C09D4003CA10F7A207A90D9DF807A9029D, | ||
+ | 101 DATA 27D0CA10F3A226BDD5C09D00D0CA10F7A27F8E0DDC8A290709109D00CFA9009D, | ||
+ | 102 DATA 80CECA10F08DFF3FA217BDFCC09D88CE9DA0CE9DB8CE9DD0CE9DE0CECA10EB20, | ||
+ | 103 DATA 67C15860A201A008EAEAEA24EABDFFCE8D11D0BD80CECE16D08D21D08C16D0BD, | ||
+ | 104 DATA 80CF8D17D049FF8D17D0E810E0EE19D02014C14C31EA00000003FB00077E0035, | ||
+ | 105 DATA DF001D7700B75D00BD837EEF01DEBB0178AE0370EB0000BA0360EE03D8FB02F6, | ||
+ | 106 DATA FE83BD9FBA0037EE003DFB00077E0003DF00000000E834203450348034B034E0, | ||
+ | 107 DATA 3410344034C118330000FF08FF150101FFFFFF00000000000000010A000B0C0F, | ||
+ | 108 DATA 010F0C0B00060E0D010D0E060009020A010A0209A21FA9009D80CF9DA0CF9DC0, | ||
+ | 109 DATA CF9DE0CFCA10F18D4DC1A9078D35C1A9808D45C1A200BC8003BD88038D51C1A2, | ||
+ | 110 DATA 14B982CF09009982CF8C5AC1A900290769008D4DC14A4A4A186900A8CAD0E24E, | ||
+ | 111 DATA 45C1CE35C110CDA9000A293FA8EE68C1A207B99EC14A4A1869089D880398690A, | ||
+ | 112 DATA 293FA8CA10ECA207AD68C1293FA8B99EC19D800398690A293FA8CA10F1602023, | ||
+ | 113 DATA 26292C2F313436383A3C3D3E3F3F3F3F3F3E3D3C3A383634312F2C292623201C, | ||
+ | 114 DATA 191613100E0B09070503020100000000000102030507090B0E101316191C0000, | ||
+ | 200 DATA END,0 | ||
+ | |||
+ | -------------------------------------------------------------------------- | ||
+ | Uuencoded C64 exutable for stretching sprites (PAL) | ||
+ | |||
+ | begin 644 stretch.64 | ||
+ | M`0@-" | ||
+ | MLC`ZAT$D+$$ZF4$D.HM!)+(B14Y$(J> | ||
+ | MI#, | ||
+ | M*0" | ||
+ | M2U-532!%4E)/ | ||
+ | M14)$.39#,# | ||
+ | M`%L)90"# | ||
+ | M, | ||
+ | M, | ||
+ | M1$4P0T5# | ||
+ | M045!, | ||
+ | M+" | ||
+ | M, | ||
+ | M1$8P,# | ||
+ | M, | ||
+ | M-T5%,# | ||
+ | M0C`S-$4P+" | ||
+ | M, | ||
+ | M=@ML`(, | ||
+ | M.4$R, | ||
+ | M1D-!, | ||
+ | M.# | ||
+ | M-4%#, | ||
+ | M(#, | ||
+ | M, | ||
+ | M.3-& | ||
+ | M, | ||
+ | M, | ||
+ | M, | ||
+ | M,# | ||
+ | , | ||
+ | `` | ||
+ | end | ||
+ | size 1362 | ||
+ | |||
+ | ============================================================================= | ||
+ | </ | ||
+ | ====== Rob Hubbard' | ||
+ | < | ||
+ | by Anthony McSweeney (u882859@postoffice.utas.edu.au) | ||
+ | |||
+ | [Ed's Note: I questioned this article concerning copyright problems and he | ||
+ | has assured me that it is legal to present it in entirity like this as it is | ||
+ | past a certain # of years. Accordingly I'm presenting it and any concerns | ||
+ | should be taken up with him and not myself.] | ||
+ | |||
+ | Introduction: | ||
+ | ************ | ||
+ | |||
+ | How do you introduce someone like Rob Hubbard?? He came, he saw and he | ||
+ | conquered the '64 world. In my estimation, this one man was resposible for | ||
+ | selling more '64 software than any other single person. Hell! I think that Rob | ||
+ | Hubbard was responsible for selling more COMMODORE 64's than any other person! | ||
+ | I certainly bought my '64 after being blown away by the Monty on the Run music | ||
+ | in December 1985. In the next few years, Rob would totally dominate the '64 | ||
+ | music scene, releasing one hit after another. I will even say that some really | ||
+ | terrible games sold well only on the strength of their brilliant Rob Hubbard | ||
+ | music (eg. KnuckleBusters and W.A.R.). | ||
+ | |||
+ | So how did Rob achieve this success? Firstly (of course) he is a superb | ||
+ | composer and musician, able to make the tunes that bring joy to our hearts | ||
+ | everytime we hear them! (also consider the amazing diversity of styles of | ||
+ | music that Rob composed). Secondly, he was able to make music which was suited | ||
+ | to the strengths and limitations of the SID chip. Just recall the soundfx used | ||
+ | at the beginning of Thrust, or in the Delta in-game music. Perhaps the biggest | ||
+ | limitation of SID must be the meagre 3 channels that can be used, but most | ||
+ | Hubbard songs appear to have four, five or even more instruments going (just | ||
+ | listen to the beginning of Phantoms of the Asteriods for example... that's | ||
+ | only one channel used!!). I could really go on for (p)ages identifying the | ||
+ | outstanding things that Rob Hubbard did, so I will finally mention that Rob's | ||
+ | coding skills and his music routines were a major factor in his success. | ||
+ | |||
+ | |||
+ | The First Rob Hubbard Routine: | ||
+ | ***************************** | ||
+ | |||
+ | Rob Hubbard created a superb music routine from the very first tune which | ||
+ | was released (Confuzion). Furthermore, | ||
+ | for a very long time, only changing it _slightly_ over time. The sourcecode | ||
+ | that I present here was used (with slight modifications) in: Confuzion, Thing | ||
+ | on a Spring, Monty on the Run, Action Biker, Crazy Comets, Commando, Hunter | ||
+ | Patrol, Chrimera, The Last V8, Battle of Britain, Human Race, Zoids, Rasputin, | ||
+ | Master of Magic, One Man & His Droid, Game Killer, Gerry the Germ, Geoff Capes | ||
+ | Strongman Challenge, Phantoms of the Asteroids, Kentilla, Thrust, | ||
+ | International Karate, Spellbound, Bump Set and Spike, Formula 1 Simulator, | ||
+ | Video Poker, Warhawk or Proteus and many, many more! All you would need to do | ||
+ | to play a different music is to change the music data at the bottom, and a few | ||
+ | lines of the code. | ||
+ | |||
+ | This particular routine has been ripped off by many famous groups and | ||
+ | people over the years, but I don't think that they were ever generous enough | ||
+ | to share it around. Can you remember The Judges and Red Software?? They made | ||
+ | the famous Red-Hubbard demo, and used it in Rhaa-Lovely and many of their | ||
+ | other productions. I'm sure that the (Atari) ST freaks reading this will love | ||
+ | Mad Max (aka Jochen Hippel), and remember the BIG demo which featured approx | ||
+ | 100 Rob Hubbard tunes converted to the ST. Although I hate to admit it, I | ||
+ | decided to start sharing around my own sourcecode after receiving the amazing | ||
+ | Protracker sourcecode (340K!) on the Amiga (thanks Lars Hamre). That made me | ||
+ | shameful to be selfish, especially after I learned alot of from it. Why don't | ||
+ | YOU share around your old sourcecodes too! | ||
+ | |||
+ | The particular routine that is included below was ripped from Monty on the | ||
+ | Run, and it appeared in memory from $8000 to about $9554. The complete | ||
+ | routine had code for soundfx in it, which I have taken out for the sake of | ||
+ | clarity. Although the routine is really tiny - a mere 900 or 1000 bytes of | ||
+ | code, there are some amazingly complex concepts in it which require alot of | ||
+ | explanation if you don't know much about computer music or SID. Fortunately | ||
+ | for you, I have put in excellent label names for you, and also alot of really | ||
+ | helpful and amazing comments. In fact, I think this sourcecode must have a | ||
+ | much better structure and comments than Rob Hubbard' | ||
+ | the best way to understand the sourcecode is to study it, and figure out what | ||
+ | is going on using the comments. | ||
+ | |||
+ | In addition to the comments in the source, there are *3* descriptions of | ||
+ | the routine in this article. The first tells you how to use the music routine | ||
+ | when it's viewed as an already assembled ' | ||
+ | overview of the music and instrument data format, and is great for getting an | ||
+ | overall feel for what the code is doing. The third description looks at the | ||
+ | various sections of the code, and how they come together. | ||
+ | |||
+ | |||
+ | How to use the sourcecode: | ||
+ | ************************* | ||
+ | |||
+ | jsr music+0 to initialize the music number in the accumulator | ||
+ | jsr music+3 to play the music | ||
+ | jsr music+6 to stop the music and quieten SID | ||
+ | |||
+ | The music is supposed to run at 50Hz, or 50 times per second. Therefore | ||
+ | PAL users can run the music routine off the IRQ like this: | ||
+ | |||
+ | lda #$00 ; init music number 0 | ||
+ | jsr music+0 | ||
+ | sei ; install the irq and a raster compare | ||
+ | lda #<irq | ||
+ | ldx #>irq | ||
+ | sta $314 | ||
+ | stx $315 | ||
+ | lda #$1b | ||
+ | sta $d011 | ||
+ | lda #$01 | ||
+ | sta $d01a | ||
+ | lda #$7f | ||
+ | sta $dc0d | ||
+ | cli | ||
+ | loop =* | ||
+ | jmp loop ; endless loop (music is now playing off interrupt :-) | ||
+ | |||
+ | irq =* | ||
+ | lda #$01 | ||
+ | sta $d019 | ||
+ | lda #$3c | ||
+ | sta $d012 | ||
+ | |||
+ | inc $d020 ; play music, and show a raster for the time it takes | ||
+ | jsr music+3 | ||
+ | dec $d020 | ||
+ | |||
+ | lda #$14 | ||
+ | sta $d018 | ||
+ | jmp $ea31 | ||
+ | |||
+ | If this method is used on NTSC machines, then the music will be running at | ||
+ | 60Hz and will sound much to fast - possibly it might sound terrible. I'm | ||
+ | afraid you'll have to put up with this unless YOU are good enough to make a | ||
+ | CIA interrupt at 50Hz. As I havn't had to worry about NTSC users before, | ||
+ | perhaps someone will send me the best way to do this... | ||
+ | |||
+ | [Ed. Note: You could also keep a counter for the IRQ and don't execute it | ||
+ | every 6 interrupt. This will make it the right speed although the best | ||
+ | solution is for modifying the CIA to 50Hz like he mentions above.] | ||
+ | |||
+ | How the music data is arranged: | ||
+ | ****************************** | ||
+ | |||
+ | 1. The music ' | ||
+ | |||
+ | Each RH music is made up of a ' | ||
+ | the ' | ||
+ | all using the same playroutine (and even the same instruments :). The source | ||
+ | that appears below only has the one song in it, and the music number is | ||
+ | automatically set to 0 as a result (line 20). The label ' | ||
+ | want to look for the pointers to the songs if you want to change this. | ||
+ | |||
+ | 2. Each song is made up of three tracks. | ||
+ | |||
+ | We all know that there are only 3 channels on the SID chip, so there are | ||
+ | also 3 tracks - one for each channel. When I said ' | ||
+ | above, I was therefore referring to ' | ||
+ | the song' | ||
+ | a high and low pointer, so there are 6 bytes needed to point to a song. | ||
+ | |||
+ | 3. Each track is made up of a list of pattern numbers | ||
+ | |||
+ | Each track consists of a list of the pattern numbers in the order in which | ||
+ | they are to be played. Here we are looking at the labels ' | ||
+ | ' | ||
+ | patterns played in this song are $11, $12 and $13 on channels 1,2 and 3 | ||
+ | respectively. The track is either ended with a $ff or $fe byte. A $ff means | ||
+ | that the song needs to be looped when the end of the track is reached (like | ||
+ | the monty main tune), while a $fe means that the song is only to be played | ||
+ | once. The current offset into the track is often called the current POSITION | ||
+ | for that track. | ||
+ | |||
+ | 4. A pattern consists of a sequence of notes. | ||
+ | |||
+ | A pattern contains the data that says when the notes should be played, how | ||
+ | long they should be played for, at what pitch, with what instrument, should | ||
+ | there be ADSR, should there be bending (portamento) of the notes etc. Each | ||
+ | pattern is ended with a $ff byte, and when this is encountered, | ||
+ | pattern in the track will be played. Each note has up to a 4 byte | ||
+ | specification. | ||
+ | |||
+ | - The first byte is always the length of the note from 0-31 or 0-$1f in hex. | ||
+ | You will notice that the top three bits are not used for the length of the | ||
+ | note, so they are used for other things. | ||
+ | - Bit#5 signals no release needed. - Bit#6 signals that this note is | ||
+ | appended to the last one (no attack/ | ||
+ | instrument or portamento is coming up. | ||
+ | |||
+ | - The second byte is an optional byte, and it holds the instument number to | ||
+ | use or the portamento value (ie a bended note). This byte will be needed | ||
+ | according to whether bit#7 of the first byte is set or not...ie if the 1st | ||
+ | byte was negative, then this byte is needed. | ||
+ | - If the second byte is positive, then this is the new instrument number. | ||
+ | - If the second byte is negative, then this is a bended note (portamento). | ||
+ | and the value is the speed of the portamento (except for bits #7 and #0) | ||
+ | Bit #0 of the portamento byte determines the direction of the bend. | ||
+ | - Bit#0 = 0 then portamento is up. | ||
+ | - Bit#0 = 1 then portamento is down. | ||
+ | |||
+ | - The third byte of the specification is the pitch of the note. A pitch of | ||
+ | 0 is the lowest C possible. A pitch of 12 or $C(hex) is the next highest | ||
+ | C above that. These pitches are denoted fairly universally as eg. ' | ||
+ | for sharps eg. ' | ||
+ | 72 ($48) which is c-6 :-) | ||
+ | |||
+ | - The fourth byte if it exists will denote the end of the pattern. | ||
+ | ie. If the next byte is a $ff, then this is the end of the pattern. | ||
+ | |||
+ | NOTE: I have labelled the various bytes with numbers for convenience. Bear | ||
+ | in mind that some of these are optional, so if the second byte is not needed, | ||
+ | then I will say that the pitch of the note coming up is the 'third byte', | ||
+ | even though it isn't really. | ||
+ | |||
+ | Okay, here are some examples: | ||
+ | |||
+ | eg. $84,$04,$24 means that the length of the note is 4 (from the lower 5 bits | ||
+ | of the first byte), that the instrument to use is instrument number 4 | ||
+ | (the second byte, as indicated by bit #7 of the first byte), and that the | ||
+ | pitch of the note is $24 or c-3. | ||
+ | eg. $D6, | ||
+ | note should be appended to the last, that the second byte is a portamento | ||
+ | (as both 1st and 2nd bytes -ve!), that the portamento is going up (as | ||
+ | bit#0 = 0) with a speed of 24 ($18), that the pitch of the note is $25 | ||
+ | or c#3, and that this is the end of the pattern. | ||
+ | |||
+ | It doesn' | ||
+ | the way that Rob Hubbard made the music!! He worked out some musical ideas | ||
+ | on his cheap (musical) keyboard, and typed the notes into his assembler in | ||
+ | hex, just like this. | ||
+ | |||
+ | |||
+ | 5. The instruments are an 8 byte data structure. | ||
+ | |||
+ | You are looking at the label ' | ||
+ | 8 bytes which come along first are instrument numnber 0, the next 8 define | ||
+ | instrument number 1, etc. Here are the meanings of the bytes, but I suggest | ||
+ | that you check out your programming manuals if you are unfamiliar with these: | ||
+ | |||
+ | - Byte 0 is the pulse width low byte, and | ||
+ | - Byte 1 is the pulse width high byte. (also see byte 7). | ||
+ | |||
+ | - Byte 2 is the control register byte. | ||
+ | This specifies which type of sound should be used; sine, sawtooth etc. | ||
+ | |||
+ | - Byte 3 is the attack and decay values, and | ||
+ | - Byte 4 is the sustain and release values. | ||
+ | The note's volume is altered according to these values. When the attack and | ||
+ | decay are over, the volume of the note is held at the sustain level. When | ||
+ | length of a note is over, a release is done through the ' | ||
+ | |||
+ | - Byte 5 is the vibrato depth for the instrument. | ||
+ | |||
+ | - Byte 6 is the pulse speed. | ||
+ | Timbre is created by changing the shape of the waveform each 50th of a | ||
+ | second, and this is the most common way of achieving it. The shape of | ||
+ | the pulse waveform changes from square to a very rectangular at a speed | ||
+ | according to this byte. | ||
+ | N.B. if you are interested in how the pulse value number works, then | ||
+ | e-mail me sometime, as I found this out (exhaustively) a few days ago! | ||
+ | |||
+ | - Byte 7 is the instrument fx byte, and is the major thing which changes | ||
+ | between different music routines. Each bit in this byte determines whether | ||
+ | this instrument will have a certain effect in it. | ||
+ | - Bit#0 signals that this is a drum. Drums are made from a noise channel | ||
+ | and also a fast frequency down, with fast decay. Bass drums use a square | ||
+ | wave, and only the first 50th of a second is a noise channel. This is | ||
+ | the tell-tale instrument that gives away a Rob Hubbard routine! Hihats | ||
+ | and other drums use noise all the time. | ||
+ | - Bit#1 signals a ' | ||
+ | sounds like somebody yelling as they fall out of a plane .. AHHHHhhhhgh.. | ||
+ | ..hence I call it a skydive!! | ||
+ | - Bit#2 signals an octave arpeggio. It's a very limited arpeggio routine in | ||
+ | this song. Listen for the arpeggio and the skydive when combined, which | ||
+ | is used alot in Hubbard songs. | ||
+ | - All the other bits have no meaning in this music, but were used alot in | ||
+ | later music for the fx. | ||
+ | |||
+ | A big reason that I presented this early routine, was because there was not | ||
+ | too much in the way of special fx to confuse you. As a result, you can | ||
+ | concentrate on the guts of the code instead of the special fx :-) | ||
+ | |||
+ | |||
+ | How the sourcecode works: | ||
+ | ************************ | ||
+ | |||
+ | The routines at the top of the sourcecode are concerned with turning the | ||
+ | music on and off, and you will see that this is done through a variable called | ||
+ | ' | ||
+ | turned off and SID is quietened, thereafter mstatus is set to $80 which means | ||
+ | that the music is still off, but SID doesn' | ||
+ | the music is initialized, | ||
+ | the further initialization stuff. If mstatus is any other value, then the | ||
+ | music is being played. For any of the initialization stuff to have any meaning | ||
+ | to you, you ofcourse have to understand the rest of the playroutine :-) | ||
+ | |||
+ | After we have got past the on/off/init stuff, we are at the label called | ||
+ | ' | ||
+ | is the start of a huge loop that is done *3* times - once for each channel. | ||
+ | The loop really *is* huge, as it ends right on the last few lines of the code | ||
+ | :-) | ||
+ | |||
+ | Now that we are talking about routines within the loop, we are talking about | ||
+ | these routines being applied to the channels independantly. There are 2 main | ||
+ | routines within the loop, one is called NoteWork, and the other is called | ||
+ | SoundWork. NoteWork checks to see whether a new note is needed on this | ||
+ | channel, and if it is, then the notedata is fetched and stuff is initialized. | ||
+ | If no note is needed, then SoundWork is called which processes the instruments | ||
+ | and does the portamento. | ||
+ | |||
+ | NoteWork first checks the speed at which the notes are fetched. If the delay | ||
+ | is still occurring, then new notes are not needed and soundwork is called. | ||
+ | N.B. that the speed for Monty on the Run is 1, which means that a note of | ||
+ | length $1f will last for 64 calls to the routine (ie just over a second). If | ||
+ | the speed of the song is reset, then NoteWork decrements the length of the | ||
+ | current note. When the length of the current note hits $ff (-1) then a new | ||
+ | note is needed, otherwise SoundWork is jumped to. | ||
+ | |||
+ | The data for a new note is collected at the label ' | ||
+ | simplest case, this involves getting the next bytes of data from the current | ||
+ | pattern on this channel, but if the end of the pattern is reached, then the | ||
+ | next pattern number is fetched by reference to the current position within | ||
+ | this channel' | ||
+ | reached, and the current position needs to be reset to 0 before the next | ||
+ | pattern number can be found. | ||
+ | |||
+ | You can see quite clearly in this part of the routine where the length of | ||
+ | the note is collected, and it is determined whether a 2nd byte is needed, | ||
+ | where the pitch is collected, and the end of the song checked. You can also | ||
+ | see where some of the data is collected from the current instrument and jammed | ||
+ | into the SID registers. | ||
+ | |||
+ | SoundWork is called if no new notes are needed, and it processes the | ||
+ | instruments and does the portamento etc. This part of the routine is neatly | ||
+ | expressed in sections which are really well commented and quite easy to | ||
+ | understand. | ||
+ | |||
+ | - The first thing that occurs in SoundWork is that the 'gate bit' of SID is | ||
+ | set when the length of the note is over - this causes a release of the note. | ||
+ | |||
+ | - The vibrato routine is quite inefficient, | ||
+ | Ofcourse vibrato is implemented by raising and lowering the pitch of the | ||
+ | note ever-so-slightly causing the note to ' | ||
+ | vibrato is determined in the current instrument. | ||
+ | |||
+ | - The pulsework routine changes the pulsewidth between square wave and very | ||
+ | rectangular wave according to the pulsespeed in the current instrument. | ||
+ | (ie. it changes the sound of the instrument and thus alters the ' | ||
+ | The routine goes backwards and forwards between the two; and switches | ||
+ | when one extremity is reached. It's interesting to note that the current | ||
+ | values of the pulse width are actually stored in the instrument :-) | ||
+ | |||
+ | - Portamento is achieved by adding/ | ||
+ | the current frequency each time this part of the routine is called. | ||
+ | |||
+ | - The instrument fx routines are also really easy to figure out, as they are | ||
+ | well commented. Both the drums and the skydive do a very fast frequency | ||
+ | down, so it is the most significant byte of the frequency which is reduced | ||
+ | .. and not 16-bit maths (math?!) The arpeggio is only an octave arpeggio, | ||
+ | so for the first 50th of a second, the current note is played, and for | ||
+ | the next 50th of a second, current note+12 is played, followed by the | ||
+ | current note again etc. | ||
+ | ( If you don't know what an arpeggio is..it' | ||
+ | ( a chord are played individually in a rapid succession. It produces a ) | ||
+ | ( ' | ||
+ | ( note is changed 50 times per second, which gives a very nice sound. If ) | ||
+ | ( you have listened to some computer music, then you will have definately ) | ||
+ | ( listened to an arpeggios all the time, even if you don't realize it! ) | ||
+ | |||
+ | |||
+ | Final Thoughts: | ||
+ | ************** | ||
+ | |||
+ | *Bounce* I'm finally near the end of this article! It has been alot of work | ||
+ | to try to explain this routine, but I'm glad that I've done it *grin* If you | ||
+ | have any questions then please feel free to e-mail me, or even e-mail Craig if | ||
+ | it's after August 1993 and I'll make sure that I leave a forwarding address | ||
+ | with him. Also, please feel free to e-mail me and tell me what you think of | ||
+ | this article. I will only be bothered writing more of the same if I know that | ||
+ | someone is finding them useful/ | ||
+ | interested in Amiga or ST music too, as I've done alot on both of those | ||
+ | machines. | ||
+ | |||
+ | I'm not sure whether Craig will be putting the actual sourcecode below this | ||
+ | text, or in some kind of Appendix. In either case, I SHALL take all legal | ||
+ | responsibilty for publishing Rob Hubbard' | ||
+ | to publish the routine in his net-mag because of copyright reasons. As a | ||
+ | post-graduate law student that will be working as a commercial lawyer | ||
+ | (attourney for Americans :) specializing in copyright/ | ||
+ | software/ | ||
+ | any practical legal consequences for me. | ||
+ | |||
+ | I would have given an arm or a leg for a commented Rob Hubbard sourcecode in | ||
+ | the past, so I hope you enjoy this valuable offering. | ||
+ | ----------------------------------------------------------------------------- | ||
+ | ;rob hubbard | ||
+ | ;monty on the run music driver | ||
+ | |||
+ | ;this player was used (with small mods) | ||
+ | ;for his first approx 30 musix | ||
+ | |||
+ | .org $8000 | ||
+ | .obj motr | ||
+ | |||
+ | jmp initmusic | ||
+ | jmp playmusic | ||
+ | jmp musicoff | ||
+ | |||
+ | |||
+ | ; | ||
+ | ;init music | ||
+ | |||
+ | initmusic =* | ||
+ | |||
+ | lda #$00 ; | ||
+ | ldy #$00 | ||
+ | asl | ||
+ | sta tempstore | ||
+ | asl | ||
+ | clc | ||
+ | adc tempstore | ||
+ | tax | ||
+ | |||
+ | - lda songs, | ||
+ | sta currtrkhi, | ||
+ | inx ;current tracks | ||
+ | iny | ||
+ | cpy #$06 | ||
+ | bne - | ||
+ | |||
+ | lda #$00 ; | ||
+ | sta $d404 | ||
+ | sta $d40b | ||
+ | sta $d412 | ||
+ | sta $d417 | ||
+ | |||
+ | lda #$0f ;full volume | ||
+ | sta $d418 | ||
+ | |||
+ | lda #$40 ;flag init music | ||
+ | sta mstatus | ||
+ | |||
+ | rts | ||
+ | |||
+ | |||
+ | ; | ||
+ | ;music off | ||
+ | |||
+ | musicoff =* | ||
+ | |||
+ | lda #$c0 ;flag music off | ||
+ | sta mstatus | ||
+ | rts | ||
+ | |||
+ | |||
+ | ; | ||
+ | ;play music | ||
+ | |||
+ | playmusic =* | ||
+ | |||
+ | inc counter | ||
+ | |||
+ | bit mstatus | ||
+ | bmi moff ;$80 and $c0 is off | ||
+ | bvc contplay | ||
+ | |||
+ | |||
+ | ;========== | ||
+ | ;init the song (mstatus $40) | ||
+ | |||
+ | lda #$00 ;init counter | ||
+ | sta counter | ||
+ | |||
+ | ldx #3-1 | ||
+ | - sta posoffset, | ||
+ | sta patoffset, | ||
+ | sta lengthleft, | ||
+ | sta notenum,x | ||
+ | dex | ||
+ | bpl - | ||
+ | |||
+ | sta mstatus | ||
+ | jmp contplay | ||
+ | |||
+ | |||
+ | ;========== | ||
+ | ;music is off (mstatus $80 or $c0) | ||
+ | |||
+ | moff =* | ||
+ | |||
+ | bvc + ;if mstatus $c0 then | ||
+ | lda #$00 | ||
+ | sta $d404 ;kill voice 1,2,3 | ||
+ | sta $d40b ;control registers | ||
+ | sta $d412 | ||
+ | |||
+ | lda #$0f ;full volume still | ||
+ | sta $d418 | ||
+ | |||
+ | lda #$80 ;flag no need to kill | ||
+ | sta mstatus | ||
+ | |||
+ | + jmp musicend | ||
+ | |||
+ | |||
+ | ;========== | ||
+ | ;music is playing (mstatus otherwise) | ||
+ | |||
+ | contplay =* | ||
+ | |||
+ | ldx #3-1 ; | ||
+ | |||
+ | dec speed ;check the speed | ||
+ | bpl mainloop | ||
+ | |||
+ | lda resetspd | ||
+ | sta speed | ||
+ | |||
+ | |||
+ | mainloop =* | ||
+ | |||
+ | lda regoffsets, | ||
+ | sta tmpregofst | ||
+ | tay | ||
+ | |||
+ | |||
+ | ;check whether a new note is needed | ||
+ | |||
+ | lda speed ;if speed not reset | ||
+ | cmp resetspd | ||
+ | beq checknewnote | ||
+ | jmp vibrato | ||
+ | |||
+ | checknewnote =* | ||
+ | |||
+ | lda currtrkhi, | ||
+ | sta $02 ;this track in $2 | ||
+ | lda currtrklo,x | ||
+ | sta $03 | ||
+ | |||
+ | dec lengthleft, | ||
+ | bmi getnewnote | ||
+ | |||
+ | jmp soundwork | ||
+ | |||
+ | |||
+ | ;========== | ||
+ | ;notework | ||
+ | ;a new note is needed. get the pattern | ||
+ | ;number/cc from this position | ||
+ | |||
+ | getnewnote =* | ||
+ | |||
+ | ldy posoffset, | ||
+ | lda ($02), | ||
+ | |||
+ | cmp #$ff ;pos $ff restarts | ||
+ | beq restart | ||
+ | |||
+ | cmp #$fe ;pos $fe stops music | ||
+ | bne getnotedata | ||
+ | jmp musicend | ||
+ | |||
+ | ;cc of $ff restarts this track from the | ||
+ | ;first position | ||
+ | |||
+ | restart =* | ||
+ | |||
+ | lda #$00 ;get note immediately | ||
+ | sta lengthleft, | ||
+ | sta posoffset,x | ||
+ | sta patoffset,x | ||
+ | jmp getnewnote | ||
+ | |||
+ | |||
+ | ;get the note data from this pattern | ||
+ | |||
+ | getnotedata =* | ||
+ | |||
+ | tay | ||
+ | lda patptl, | ||
+ | sta $04 ;the pattern in $4 | ||
+ | lda patpth,y | ||
+ | sta $05 | ||
+ | |||
+ | lda #$00 ; | ||
+ | sta portaval,x | ||
+ | |||
+ | ldy patoffset, | ||
+ | |||
+ | lda #$ff ; | ||
+ | sta appendfl | ||
+ | |||
+ | ;1st byte is the length of the note 0-31 | ||
+ | ;bit5 signals no release (see sndwork) | ||
+ | ;bit6 signals appended note | ||
+ | ;bit7 signals a new instrument | ||
+ | ; or portamento coming up | ||
+ | |||
+ | lda ($04), | ||
+ | sta savelnthcc, | ||
+ | sta templnthcc | ||
+ | and #$1f | ||
+ | sta lengthleft, | ||
+ | |||
+ | bit templnthcc | ||
+ | bvs appendnote | ||
+ | |||
+ | inc patoffset, | ||
+ | |||
+ | lda templnthcc | ||
+ | bpl getpitch | ||
+ | |||
+ | ;2nd byte needed as 1st byte negative | ||
+ | ;2nd byte is the instrument number(+ve) | ||
+ | ;or portamento speed(-ve) | ||
+ | |||
+ | iny | ||
+ | lda ($04), | ||
+ | bpl + | ||
+ | |||
+ | sta portaval, | ||
+ | jmp ++ | ||
+ | |||
+ | + sta instrnr, | ||
+ | |||
+ | + inc patoffset,x | ||
+ | |||
+ | ;3rd byte is the pitch of the note | ||
+ | ;get the 'base frequency' | ||
+ | |||
+ | getpitch =* | ||
+ | |||
+ | iny | ||
+ | lda ($04), | ||
+ | sta notenum,x | ||
+ | asl ;pitch*2 | ||
+ | tay | ||
+ | lda frequenzlo, | ||
+ | sta tempfreq | ||
+ | lda frequenzhi, | ||
+ | ldy tmpregofst | ||
+ | sta $d401,y | ||
+ | sta savefreqhi, | ||
+ | lda tempfreq | ||
+ | sta $d400,y | ||
+ | sta savefreqlo, | ||
+ | jmp + | ||
+ | |||
+ | appendnote =* | ||
+ | |||
+ | dec appendfl | ||
+ | |||
+ | |||
+ | ;fetch all the initial values from the | ||
+ | ;instrument data structure | ||
+ | |||
+ | + ldy tmpregofst | ||
+ | lda instrnr, | ||
+ | stx tempstore | ||
+ | asl ;instr num*8 | ||
+ | asl | ||
+ | asl | ||
+ | tax | ||
+ | |||
+ | lda instr+2, | ||
+ | sta tempctrl | ||
+ | lda instr+2,x | ||
+ | and appendfl | ||
+ | sta $d404,y | ||
+ | |||
+ | lda instr+0, | ||
+ | sta $d402,y | ||
+ | |||
+ | lda instr+1, | ||
+ | sta $d403,y | ||
+ | |||
+ | lda instr+3, | ||
+ | sta $d405,y | ||
+ | |||
+ | lda instr+4, | ||
+ | sta $d406,y | ||
+ | |||
+ | ldx tempstore | ||
+ | lda tempctrl | ||
+ | sta voicectrl,x | ||
+ | |||
+ | |||
+ | ;4th byte checks for the end of pattern | ||
+ | ;if eop found, inc the position and | ||
+ | ;reset patoffset for new pattern | ||
+ | |||
+ | inc patoffset, | ||
+ | ldy patoffset,x | ||
+ | lda ($04),y | ||
+ | |||
+ | cmp #$ff ; | ||
+ | bne + | ||
+ | |||
+ | lda #$00 ;end of pat reached | ||
+ | sta patoffset, | ||
+ | inc posoffset, | ||
+ | |||
+ | + jmp loopcont | ||
+ | |||
+ | |||
+ | ;========== | ||
+ | ;soundwork | ||
+ | ;the instrument and effects processing | ||
+ | ;routine when no new note was needed | ||
+ | |||
+ | soundwork =* | ||
+ | |||
+ | ;release routine | ||
+ | ;set off a release when the length of | ||
+ | ;the note is exceeded | ||
+ | ;bit4 of the 1st note-byte can specify | ||
+ | ;for no release | ||
+ | |||
+ | ldy tmpregofst | ||
+ | |||
+ | lda savelnthcc, | ||
+ | and #$20 ; | ||
+ | bne vibrato | ||
+ | |||
+ | lda lengthleft, | ||
+ | bne vibrato | ||
+ | |||
+ | lda voicectrl, | ||
+ | and #$fe ; | ||
+ | sta $d404, | ||
+ | lda #$00 | ||
+ | sta $d405,y | ||
+ | sta $d406,y | ||
+ | |||
+ | |||
+ | ;vibrato routine | ||
+ | ;(does alot of work) | ||
+ | |||
+ | vibrato =* | ||
+ | |||
+ | lda instrnr, | ||
+ | asl | ||
+ | asl | ||
+ | asl ;instr num*8 | ||
+ | tay | ||
+ | sty instnumby8 | ||
+ | |||
+ | lda instr+7, | ||
+ | sta instrfx | ||
+ | |||
+ | lda instr+6, | ||
+ | sta pulsevalue | ||
+ | |||
+ | lda instr+5, | ||
+ | sta vibrdepth | ||
+ | beq pulsework | ||
+ | |||
+ | lda counter | ||
+ | and #7 ;the counter' | ||
+ | cmp #4 ;into an oscillating | ||
+ | bcc + ;value (01233210) | ||
+ | eor #7 | ||
+ | + sta oscilatval | ||
+ | |||
+ | lda notenum, | ||
+ | asl ;note*2 | ||
+ | tay ;get diff btw note | ||
+ | sec ;and note+1 frequency | ||
+ | lda frequenzlo+2, | ||
+ | sbc frequenzlo, | ||
+ | sta tmpvdiflo | ||
+ | lda frequenzhi+2, | ||
+ | sbc frequenzhi, | ||
+ | |||
+ | - lsr ;divide difference by | ||
+ | ror tmpvdiflo | ||
+ | dec vibrdepth | ||
+ | bpl - | ||
+ | sta tmpvdifhi | ||
+ | |||
+ | lda frequenzlo, | ||
+ | sta tmpvfrqlo | ||
+ | lda frequenzhi, | ||
+ | sta tmpvfrqhi | ||
+ | |||
+ | lda savelnthcc, | ||
+ | and #$1f ; | ||
+ | cmp #8 | ||
+ | bcc + | ||
+ | |||
+ | ldy oscilatval | ||
+ | |||
+ | - dey ;depending on the osc | ||
+ | bmi + ;value, add the vibr | ||
+ | clc ;freq that many times | ||
+ | lda tmpvfrqlo | ||
+ | adc tmpvdiflo | ||
+ | sta tmpvfrqlo | ||
+ | lda tmpvfrqhi | ||
+ | adc tmpvdifhi | ||
+ | sta tmpvfrqhi | ||
+ | jmp - | ||
+ | |||
+ | + ldy tmpregofst | ||
+ | lda tmpvfrqlo | ||
+ | sta $d400,y | ||
+ | lda tmpvfrqhi | ||
+ | sta $d401,y | ||
+ | |||
+ | |||
+ | ; | ||
+ | ;depending on the control/ | ||
+ | ;the instrument datastructure, | ||
+ | ;width is of course inc/ | ||
+ | ;produce timbre | ||
+ | |||
+ | ;strangely the delay value is also the | ||
+ | ;size of the inc/ | ||
+ | |||
+ | pulsework =* | ||
+ | |||
+ | lda pulsevalue | ||
+ | beq portamento | ||
+ | |||
+ | ldy instnumby8 | ||
+ | and #$1f | ||
+ | dec pulsedelay, | ||
+ | bpl portamento | ||
+ | |||
+ | sta pulsedelay, | ||
+ | |||
+ | lda pulsevalue | ||
+ | and #$e0 ;from $00-$1f | ||
+ | sta pulsespeed | ||
+ | |||
+ | lda pulsedir, | ||
+ | bne pulsedown | ||
+ | |||
+ | lda pulsespeed | ||
+ | clc | ||
+ | adc instr+0, | ||
+ | pha ;to the pulse width | ||
+ | lda instr+1,y | ||
+ | adc #$00 | ||
+ | and #$0f | ||
+ | pha | ||
+ | cmp #$0e ;go pulsedown when | ||
+ | bne dumpulse | ||
+ | inc pulsedir, | ||
+ | jmp dumpulse | ||
+ | |||
+ | pulsedown =* | ||
+ | |||
+ | sec ;pulse width down | ||
+ | lda instr+0,y | ||
+ | sbc pulsespeed | ||
+ | pha ;from the pulse width | ||
+ | lda instr+1,y | ||
+ | sbc #$00 | ||
+ | and #$0f | ||
+ | pha | ||
+ | cmp #$08 ;go pulseup when | ||
+ | bne dumpulse | ||
+ | dec pulsedir, | ||
+ | |||
+ | dumpulse =* | ||
+ | |||
+ | stx tempstore | ||
+ | ldx tmpregofst | ||
+ | pla ;the instr data str | ||
+ | sta instr+1,y | ||
+ | sta $d403,x | ||
+ | pla | ||
+ | sta instr+0,y | ||
+ | sta $d402,x | ||
+ | ldx tempstore | ||
+ | |||
+ | |||
+ | ;portamento routine | ||
+ | ;portamento comes from the second byte | ||
+ | ;if it's a negative value | ||
+ | |||
+ | portamento =* | ||
+ | |||
+ | ldy tmpregofst | ||
+ | lda portaval, | ||
+ | beq drums ;none | ||
+ | |||
+ | and #$7e ;toad unwanted bits | ||
+ | sta tempstore | ||
+ | |||
+ | lda portaval, | ||
+ | and #$01 | ||
+ | beq portup | ||
+ | |||
+ | sec ;portamento down | ||
+ | lda savefreqlo, | ||
+ | sbc tempstore | ||
+ | sta savefreqlo, | ||
+ | sta $d400,y | ||
+ | lda savefreqhi, | ||
+ | sbc #$00 ; | ||
+ | sta savefreqhi, | ||
+ | sta $d401,y | ||
+ | jmp drums | ||
+ | |||
+ | portup =* | ||
+ | |||
+ | clc ;portamento up | ||
+ | lda savefreqlo, | ||
+ | adc tempstore | ||
+ | sta savefreqlo, | ||
+ | sta $d400,y | ||
+ | lda savefreqhi, | ||
+ | adc #$00 | ||
+ | sta savefreqhi, | ||
+ | sta $d401,y | ||
+ | |||
+ | |||
+ | ;bit0 instrfx are the drum routines | ||
+ | ;the actual drum timbre depends on the | ||
+ | ;crtl register value for the instrument: | ||
+ | ;ctrlreg 0 is always noise | ||
+ | ;ctrlreg x is noise for 1st vbl and x | ||
+ | ;from then on | ||
+ | |||
+ | ;see that the drum is made by rapid hi | ||
+ | ;to low frequency slide with fast attack | ||
+ | ;and decay | ||
+ | |||
+ | drums =* | ||
+ | |||
+ | lda instrfx | ||
+ | and #$01 ; | ||
+ | beq skydive | ||
+ | |||
+ | lda savefreqhi, | ||
+ | beq skydive | ||
+ | |||
+ | lda lengthleft, | ||
+ | beq skydive | ||
+ | |||
+ | lda savelnthcc, | ||
+ | and #$1f ; | ||
+ | sec ; | ||
+ | sbc #$01 | ||
+ | cmp lengthleft, | ||
+ | ldy tmpregofst | ||
+ | bcc firstime | ||
+ | |||
+ | lda savefreqhi, | ||
+ | dec savefreqhi, | ||
+ | sta $d401, | ||
+ | |||
+ | lda voicectrl, | ||
+ | and #$fe ; | ||
+ | bne dumpctrl | ||
+ | |||
+ | firstime =* | ||
+ | |||
+ | lda savefreqhi, | ||
+ | sta $d401, | ||
+ | lda #$80 ;(set noise) | ||
+ | |||
+ | dumpctrl =* | ||
+ | |||
+ | sta $d404,y | ||
+ | |||
+ | |||
+ | ;bit1 instrfx is the skydive | ||
+ | ;a long portamento-down from the note | ||
+ | ;to zerofreq | ||
+ | |||
+ | skydive =* | ||
+ | |||
+ | lda instrfx | ||
+ | and #$02 ; | ||
+ | beq octarp | ||
+ | |||
+ | lda counter | ||
+ | and #$01 | ||
+ | beq octarp | ||
+ | |||
+ | lda savefreqhi, | ||
+ | beq octarp | ||
+ | |||
+ | dec savefreqhi, | ||
+ | ldy tmpregofst | ||
+ | sta $d401,y | ||
+ | |||
+ | |||
+ | ;bit2 instrfx is an octave arpeggio | ||
+ | ;pretty tame huh? | ||
+ | |||
+ | octarp =* | ||
+ | |||
+ | lda instrfx | ||
+ | and #$04 | ||
+ | beq loopcont | ||
+ | |||
+ | lda counter | ||
+ | and #$01 | ||
+ | beq + | ||
+ | |||
+ | lda notenum, | ||
+ | clc | ||
+ | adc #$0c | ||
+ | jmp ++ | ||
+ | |||
+ | + lda notenum, | ||
+ | |||
+ | + asl ;dump the corresponding | ||
+ | tay ; | ||
+ | lda frequenzlo, | ||
+ | sta tempfreq | ||
+ | lda frequenzhi, | ||
+ | ldy tmpregofst | ||
+ | sta $d401,y | ||
+ | lda tempfreq | ||
+ | sta $d400,y | ||
+ | |||
+ | |||
+ | ;========== | ||
+ | ;end of dbf loop | ||
+ | |||
+ | loopcont =* | ||
+ | |||
+ | dex ;dbf mainloop | ||
+ | bmi musicend | ||
+ | jmp mainloop | ||
+ | |||
+ | musicend =* | ||
+ | |||
+ | rts | ||
+ | |||
+ | |||
+ | ; | ||
+ | ;frequenz data | ||
+ | ; | ||
+ | |||
+ | frequenzlo .byt $16 | ||
+ | frequenzhi .byt $01 | ||
+ | .byt $27, | ||
+ | .byt $5f, | ||
+ | .byt $ba, | ||
+ | .byt $2d, | ||
+ | .byt $bd, | ||
+ | .byt $74, | ||
+ | .byt $5a, | ||
+ | .byt $7b, | ||
+ | .byt $e8, | ||
+ | .byt $b4, | ||
+ | .byt $f5, | ||
+ | .byt $d0, | ||
+ | .byt $68, | ||
+ | .byt $eb, | ||
+ | .byt $a1, | ||
+ | .byt $d0, | ||
+ | .byt $d6, | ||
+ | .byt $42, | ||
+ | .byt $a0, | ||
+ | .byt $ac, | ||
+ | .byt $84, | ||
+ | .byt $40, | ||
+ | .byt $58, | ||
+ | .byt $08, | ||
+ | |||
+ | |||
+ | regoffsets .byt $00,$07,$0e | ||
+ | tmpregofst .byt $00 | ||
+ | posoffset | ||
+ | patoffset | ||
+ | lengthleft .byt $00,$00,$00 | ||
+ | savelnthcc .byt $00,$00,$00 | ||
+ | voicectrl | ||
+ | notenum | ||
+ | instrnr | ||
+ | appendfl | ||
+ | templnthcc .byt $00 | ||
+ | tempfreq | ||
+ | tempstore | ||
+ | tempctrl | ||
+ | vibrdepth | ||
+ | pulsevalue .byt $00 | ||
+ | tmpvdiflo | ||
+ | tmpvdifhi | ||
+ | tmpvfrqlo | ||
+ | tmpvfrqhi | ||
+ | oscilatval .byt $00 | ||
+ | pulsedelay .byt $00,$00,$00 | ||
+ | pulsedir | ||
+ | speed .byt $00 | ||
+ | resetspd | ||
+ | instnumby8 .byt $00 | ||
+ | mstatus | ||
+ | savefreqhi .byt $00,$00,$00 | ||
+ | savefreqlo .byt $00,$00,$00 | ||
+ | portaval | ||
+ | instrfx | ||
+ | pulsespeed .byt $00 | ||
+ | counter | ||
+ | currtrkhi | ||
+ | currtrklo | ||
+ | |||
+ | |||
+ | ; | ||
+ | ;monty on the run main theme | ||
+ | ; | ||
+ | |||
+ | songs =* | ||
+ | .byt < | ||
+ | .byt < | ||
+ | .byt < | ||
+ | .byt > | ||
+ | .byt > | ||
+ | .byt > | ||
+ | |||
+ | |||
+ | ; | ||
+ | ;pointers to the patterns | ||
+ | |||
+ | ;low pointers | ||
+ | patptl =* | ||
+ | .byt <ptn00 | ||
+ | .byt <ptn01 | ||
+ | .byt <ptn02 | ||
+ | .byt <ptn03 | ||
+ | .byt <ptn04 | ||
+ | .byt <ptn05 | ||
+ | .byt <ptn06 | ||
+ | .byt <ptn07 | ||
+ | .byt <ptn08 | ||
+ | .byt <ptn09 | ||
+ | .byt <ptn0a | ||
+ | .byt <ptn0b | ||
+ | .byt <ptn0c | ||
+ | .byt <ptn0d | ||
+ | .byt <ptn0e | ||
+ | .byt <ptn0f | ||
+ | .byt <ptn10 | ||
+ | .byt <ptn11 | ||
+ | .byt <ptn12 | ||
+ | .byt <ptn13 | ||
+ | .byt <ptn14 | ||
+ | .byt <ptn15 | ||
+ | .byt <ptn16 | ||
+ | .byt <ptn17 | ||
+ | .byt <ptn18 | ||
+ | .byt <ptn19 | ||
+ | .byt <ptn1a | ||
+ | .byt <ptn1b | ||
+ | .byt <ptn1c | ||
+ | .byt <ptn1d | ||
+ | .byt <ptn1e | ||
+ | .byt <ptn1f | ||
+ | .byt <ptn20 | ||
+ | .byt <ptn21 | ||
+ | .byt <ptn22 | ||
+ | .byt <ptn23 | ||
+ | .byt <ptn24 | ||
+ | .byt <ptn25 | ||
+ | .byt <ptn26 | ||
+ | .byt <ptn27 | ||
+ | .byt <ptn28 | ||
+ | .byt <ptn29 | ||
+ | .byt <ptn2a | ||
+ | .byt <ptn2b | ||
+ | .byt <ptn2c | ||
+ | .byt <ptn2d | ||
+ | .byt 0 | ||
+ | .byt <ptn2f | ||
+ | .byt <ptn30 | ||
+ | .byt <ptn31 | ||
+ | .byt <ptn32 | ||
+ | .byt <ptn33 | ||
+ | .byt <ptn34 | ||
+ | .byt <ptn35 | ||
+ | .byt <ptn36 | ||
+ | .byt <ptn37 | ||
+ | .byt <ptn38 | ||
+ | .byt <ptn39 | ||
+ | .byt <ptn3a | ||
+ | .byt <ptn3b | ||
+ | |||
+ | ;high pointers | ||
+ | patpth =* | ||
+ | .byt >ptn00 | ||
+ | .byt >ptn01 | ||
+ | .byt >ptn02 | ||
+ | .byt >ptn03 | ||
+ | .byt >ptn04 | ||
+ | .byt >ptn05 | ||
+ | .byt >ptn06 | ||
+ | .byt >ptn07 | ||
+ | .byt >ptn08 | ||
+ | .byt >ptn09 | ||
+ | .byt >ptn0a | ||
+ | .byt >ptn0b | ||
+ | .byt >ptn0c | ||
+ | .byt >ptn0d | ||
+ | .byt >ptn0e | ||
+ | .byt >ptn0f | ||
+ | .byt >ptn10 | ||
+ | .byt >ptn11 | ||
+ | .byt >ptn12 | ||
+ | .byt >ptn13 | ||
+ | .byt >ptn14 | ||
+ | .byt >ptn15 | ||
+ | .byt >ptn16 | ||
+ | .byt >ptn17 | ||
+ | .byt >ptn18 | ||
+ | .byt >ptn19 | ||
+ | .byt >ptn1a | ||
+ | .byt >ptn1b | ||
+ | .byt >ptn1c | ||
+ | .byt >ptn1d | ||
+ | .byt >ptn1e | ||
+ | .byt >ptn1f | ||
+ | .byt >ptn20 | ||
+ | .byt >ptn21 | ||
+ | .byt >ptn22 | ||
+ | .byt >ptn23 | ||
+ | .byt >ptn24 | ||
+ | .byt >ptn25 | ||
+ | .byt >ptn26 | ||
+ | .byt >ptn27 | ||
+ | .byt >ptn28 | ||
+ | .byt >ptn29 | ||
+ | .byt >ptn2a | ||
+ | .byt >ptn2b | ||
+ | .byt >ptn2c | ||
+ | .byt >ptn2d | ||
+ | .byt 0 | ||
+ | .byt >ptn2f | ||
+ | .byt >ptn30 | ||
+ | .byt >ptn31 | ||
+ | .byt >ptn32 | ||
+ | .byt >ptn33 | ||
+ | .byt >ptn34 | ||
+ | .byt >ptn35 | ||
+ | .byt >ptn36 | ||
+ | .byt >ptn37 | ||
+ | .byt >ptn38 | ||
+ | .byt >ptn39 | ||
+ | .byt >ptn3a | ||
+ | .byt >ptn3b | ||
+ | |||
+ | |||
+ | ; | ||
+ | ;tracks | ||
+ | ; | ||
+ | |||
+ | ;track1 | ||
+ | montymaintr1 =* | ||
+ | .byt $11, | ||
+ | .byt $03, | ||
+ | .byt $07, | ||
+ | .byt $2f, | ||
+ | .byt $34, | ||
+ | .byt $35, | ||
+ | .byt $38, | ||
+ | .byt $2a, | ||
+ | .byt $ff | ||
+ | |||
+ | ;track2 | ||
+ | montymaintr2 =* | ||
+ | .byt $12, | ||
+ | .byt $39, | ||
+ | .byt $39, | ||
+ | .byt $01, | ||
+ | .byt $39, | ||
+ | .byt $39, | ||
+ | .byt $39, | ||
+ | .byt $39, | ||
+ | .byt $29, | ||
+ | .byt $39, | ||
+ | |||
+ | ;track3 | ||
+ | montymaintr3 =* | ||
+ | .byt $13,$16,$19 | ||
+ | .byt $1c, | ||
+ | .byt $1f, | ||
+ | .byt $1d, | ||
+ | .byt $20, | ||
+ | .byt $21, | ||
+ | .byt $24, | ||
+ | .byt $26, | ||
+ | .byt $26, | ||
+ | .byt $02, | ||
+ | .byt $2f, | ||
+ | .byt $0b, | ||
+ | .byt $0b, | ||
+ | .byt $0b, | ||
+ | .byt $0b, | ||
+ | .byt $0b, | ||
+ | .byt $0b, | ||
+ | .byt $ff | ||
+ | |||
+ | |||
+ | ; | ||
+ | ;patterns | ||
+ | ; | ||
+ | |||
+ | ptn00 =* | ||
+ | .byt $83, | ||
+ | .byt $3d, | ||
+ | .byt $3d, | ||
+ | .byt $3e, | ||
+ | .byt $42, | ||
+ | .byt $48, | ||
+ | .byt $4b, | ||
+ | .byt $48,$ff | ||
+ | |||
+ | ptn27 =* | ||
+ | .byt $1f,$4a,$ff | ||
+ | |||
+ | ptn28 =* | ||
+ | .byt $03, | ||
+ | .byt $03, | ||
+ | |||
+ | ptn03 =* | ||
+ | .byt $bf,$06 | ||
+ | .byt $48, | ||
+ | .byt $4b, | ||
+ | .byt $4b, | ||
+ | .byt $4b, | ||
+ | .byt $4b, | ||
+ | .byt $4c, | ||
+ | .byt $4f, | ||
+ | .byt $4f, | ||
+ | .byt $4f, | ||
+ | .byt $4f, | ||
+ | |||
+ | ptn05 =* | ||
+ | .byt $83, | ||
+ | .byt $29, | ||
+ | .byt $32, | ||
+ | .byt $30, | ||
+ | .byt $39, | ||
+ | .byt $33, | ||
+ | .byt $3c, | ||
+ | .byt $3a, | ||
+ | .byt $2d, | ||
+ | .byt $29, | ||
+ | .byt $32, | ||
+ | .byt $30, | ||
+ | .byt $39, | ||
+ | .byt $33, | ||
+ | .byt $3c, | ||
+ | .byt $37, | ||
+ | .byt $3d | ||
+ | |||
+ | ptn3a =* | ||
+ | .byt $03, | ||
+ | .byt $3e, | ||
+ | |||
+ | ptn07 =* | ||
+ | .byt $8b | ||
+ | .byt $00, | ||
+ | .byt $03, | ||
+ | .byt $03, | ||
+ | .byt $4d, | ||
+ | .byt $4d, | ||
+ | .byt $01, | ||
+ | .byt $03, | ||
+ | .byt $01, | ||
+ | .byt $03, | ||
+ | .byt $07, | ||
+ | .byt $4f, | ||
+ | .byt $4c, | ||
+ | .byt $03, | ||
+ | .byt $03, | ||
+ | .byt $33, | ||
+ | .byt $30, | ||
+ | .byt $33, | ||
+ | .byt $30, | ||
+ | .byt $03, | ||
+ | .byt $0b, | ||
+ | .byt $03, | ||
+ | .byt $01, | ||
+ | .byt $03, | ||
+ | .byt $03, | ||
+ | .byt $03, | ||
+ | .byt $03,$3c,$ff | ||
+ | |||
+ | ptn09 =* | ||
+ | .byt $83, | ||
+ | .byt $32, | ||
+ | .byt $35, | ||
+ | .byt $3a, | ||
+ | .byt $3a, | ||
+ | |||
+ | ptn2a =* | ||
+ | .byt $03, | ||
+ | .byt $03, | ||
+ | .byt $01, | ||
+ | .byt $03, | ||
+ | .byt $03, | ||
+ | |||
+ | ptn2b =* | ||
+ | .byt $03, | ||
+ | .byt $03, | ||
+ | .byt $03, | ||
+ | .byt $03, | ||
+ | .byt $ff | ||
+ | |||
+ | ptn0a =* | ||
+ | .byt $03 | ||
+ | .byt $37, | ||
+ | .byt $3a, | ||
+ | .byt $39, | ||
+ | .byt $3d, | ||
+ | .byt $3d, | ||
+ | .byt $3e, | ||
+ | .byt $3d, | ||
+ | .byt $40, | ||
+ | .byt $43, | ||
+ | .byt $41, | ||
+ | .byt $45, | ||
+ | .byt $43, | ||
+ | .byt $48, | ||
+ | .byt $45, | ||
+ | .byt $41, | ||
+ | .byt $3d, | ||
+ | |||
+ | ptn0d =* | ||
+ | .byt $01, | ||
+ | .byt $01, | ||
+ | .byt $03, | ||
+ | .byt $01, | ||
+ | .byt $01, | ||
+ | .byt $01, | ||
+ | .byt $01, | ||
+ | .byt $01, | ||
+ | .byt $01, | ||
+ | .byt $03, | ||
+ | .byt $01, | ||
+ | .byt $01, | ||
+ | .byt $01, | ||
+ | .byt $01, | ||
+ | .byt $01, | ||
+ | |||
+ | ptn0f =* | ||
+ | .byt $01,$3e,$01 | ||
+ | .byt $39, | ||
+ | .byt $39, | ||
+ | .byt $39, | ||
+ | .byt $39, | ||
+ | .byt $3a, | ||
+ | .byt $3a, | ||
+ | .byt $3a, | ||
+ | .byt $3a, | ||
+ | .byt $3d, | ||
+ | .byt $3d, | ||
+ | .byt $3d, | ||
+ | .byt $3d, | ||
+ | .byt $3e, | ||
+ | .byt $3e, | ||
+ | .byt $3e, | ||
+ | .byt $3e, | ||
+ | .byt $3e, | ||
+ | .byt $3e, | ||
+ | .byt $3e, | ||
+ | .byt $3e, | ||
+ | .byt $3f, | ||
+ | .byt $3f, | ||
+ | .byt $3f, | ||
+ | .byt $3f, | ||
+ | .byt $42, | ||
+ | .byt $42, | ||
+ | .byt $45, | ||
+ | .byt $48, | ||
+ | .byt $4a, | ||
+ | .byt $4a, | ||
+ | .byt $4a, | ||
+ | .byt $4e, | ||
+ | |||
+ | ptn11 =* | ||
+ | .byt $bf, | ||
+ | .byt $5b, | ||
+ | .byt $4f,$ff | ||
+ | |||
+ | ptn12 =* | ||
+ | .byt $bf, | ||
+ | .byt $7f,$7f,$ff | ||
+ | |||
+ | ptn13 =* | ||
+ | .byt $bf, | ||
+ | .byt $13, | ||
+ | .byt $13,$ff | ||
+ | |||
+ | ptn14 =* | ||
+ | .byt $97, | ||
+ | .byt $32, | ||
+ | .byt $17, | ||
+ | .byt $1b, | ||
+ | .byt $37,$43,$ff | ||
+ | |||
+ | ptn15 =* | ||
+ | .byt $97, | ||
+ | .byt $2e, | ||
+ | .byt $17, | ||
+ | .byt $1b, | ||
+ | .byt $34,$43,$ff | ||
+ | |||
+ | ptn16 =* | ||
+ | .byt $0f, | ||
+ | .byt $0f, | ||
+ | .byt $0f, | ||
+ | .byt $0f, | ||
+ | .byt $ff | ||
+ | |||
+ | ptn17 =* | ||
+ | .byt $97, | ||
+ | .byt $37, | ||
+ | .byt $17, | ||
+ | .byt $1b, | ||
+ | .byt $21, | ||
+ | .byt $21, | ||
+ | .byt $01,$45,$ff | ||
+ | |||
+ | ptn18 =* | ||
+ | .byt $97, | ||
+ | .byt $33, | ||
+ | .byt $17, | ||
+ | .byt $1b, | ||
+ | .byt $21, | ||
+ | .byt $21, | ||
+ | .byt $01,$42,$ff | ||
+ | |||
+ | ptn19 =* | ||
+ | .byt $0f, | ||
+ | .byt $0f, | ||
+ | .byt $0f, | ||
+ | .byt $0f, | ||
+ | .byt $ff | ||
+ | |||
+ | ptn1a =* | ||
+ | .byt $1f, | ||
+ | |||
+ | ptn1b =* | ||
+ | .byt $1f, | ||
+ | |||
+ | ptn1c =* | ||
+ | .byt $83, | ||
+ | .byt $1f, | ||
+ | .byt $1f, | ||
+ | .byt $1f, | ||
+ | .byt $1f, | ||
+ | .byt $1f, | ||
+ | .byt $1f, | ||
+ | .byt $1f, | ||
+ | .byt $1f,$ff | ||
+ | |||
+ | ptn29 =* | ||
+ | .byt $8f, | ||
+ | |||
+ | ptn2c =* | ||
+ | .byt $83, | ||
+ | .byt $2f, | ||
+ | .byt $2c, | ||
+ | |||
+ | ptn2d =* | ||
+ | .byt $43, | ||
+ | .byt $03, | ||
+ | |||
+ | ptn39 =* | ||
+ | .byt $83,$01 | ||
+ | .byt $43, | ||
+ | .byt $83, | ||
+ | .byt $03, | ||
+ | .byt $5b, | ||
+ | .byt $4f, | ||
+ | .byt $43, | ||
+ | .byt $83, | ||
+ | .byt $03,$2f | ||
+ | |||
+ | ptn01 =* | ||
+ | .byt $83, | ||
+ | .byt $03, | ||
+ | .byt $5b, | ||
+ | |||
+ | ptn02 =* | ||
+ | .byt $83, | ||
+ | .byt $1f, | ||
+ | .byt $1f,$ff | ||
+ | |||
+ | ptn1d =* | ||
+ | .byt $03, | ||
+ | .byt $03, | ||
+ | .byt $ff | ||
+ | |||
+ | ptn1e =* | ||
+ | .byt $03, | ||
+ | .byt $03, | ||
+ | .byt $ff | ||
+ | |||
+ | ptn1f =* | ||
+ | .byt $03, | ||
+ | .byt $03, | ||
+ | |||
+ | ptn04 =* | ||
+ | .byt $03, | ||
+ | .byt $03, | ||
+ | .byt $03, | ||
+ | .byt $03, | ||
+ | .byt $ff | ||
+ | |||
+ | ptn20 =* | ||
+ | .byt $03, | ||
+ | .byt $25, | ||
+ | .byt $25, | ||
+ | .byt $2d, | ||
+ | .byt $2d, | ||
+ | |||
+ | ptn06 =* | ||
+ | .byt $03, | ||
+ | .byt $03, | ||
+ | .byt $03, | ||
+ | .byt $03, | ||
+ | .byt $03, | ||
+ | .byt $03, | ||
+ | .byt $03, | ||
+ | .byt $03, | ||
+ | .byt $03, | ||
+ | .byt $03, | ||
+ | .byt $03, | ||
+ | .byt $03, | ||
+ | .byt $03, | ||
+ | .byt $03, | ||
+ | .byt $03, | ||
+ | .byt $03, | ||
+ | .byt $03, | ||
+ | |||
+ | ptn3b =* | ||
+ | .byt $83, | ||
+ | .byt $36, | ||
+ | .byt $32,$57,$ff | ||
+ | |||
+ | ptn08 =* | ||
+ | .byt $83, | ||
+ | .byt $27, | ||
+ | .byt $27,$ff | ||
+ | |||
+ | ptn21 =* | ||
+ | .byt $03, | ||
+ | .byt $03, | ||
+ | .byt $ff | ||
+ | |||
+ | ptn22 =* | ||
+ | .byt $03, | ||
+ | .byt $03, | ||
+ | .byt $ff | ||
+ | |||
+ | ptn23 =* | ||
+ | .byt $03, | ||
+ | .byt $03, | ||
+ | .byt $ff | ||
+ | |||
+ | ptn24 =* | ||
+ | .byt $03, | ||
+ | .byt $03, | ||
+ | .byt $ff | ||
+ | |||
+ | ptn25 =* | ||
+ | .byt $83, | ||
+ | .byt $29, | ||
+ | .byt $4a, | ||
+ | .byt $30, | ||
+ | .byt $2d, | ||
+ | |||
+ | ptn0b =* | ||
+ | .byt $83, | ||
+ | .byt $26, | ||
+ | .byt $26,$ff | ||
+ | |||
+ | ptn0c =* | ||
+ | .byt $03, | ||
+ | .byt $03, | ||
+ | .byt $ff | ||
+ | |||
+ | ptn26 =* | ||
+ | .byt $87, | ||
+ | .byt $26, | ||
+ | |||
+ | ptn10 =* | ||
+ | .byt $07, | ||
+ | |||
+ | ptn0e =* | ||
+ | .byt $03, | ||
+ | .byt $07, | ||
+ | |||
+ | ptn30 =* | ||
+ | .byt $bf, | ||
+ | .byt $3f, | ||
+ | .byt $03, | ||
+ | .byt $30, | ||
+ | .byt $30, | ||
+ | |||
+ | ptn31 =* | ||
+ | .byt $0f,$32 | ||
+ | .byt $af, | ||
+ | .byt $07, | ||
+ | .byt $a3, | ||
+ | .byt $90, | ||
+ | .byt $35, | ||
+ | .byt $e8, | ||
+ | |||
+ | ptn32 =* | ||
+ | .byt $07,$32,$03 | ||
+ | .byt $39, | ||
+ | .byt $38, | ||
+ | .byt $39, | ||
+ | .byt $38, | ||
+ | .byt $39, | ||
+ | .byt $3e, | ||
+ | .byt $a7, | ||
+ | .byt $32, | ||
+ | .byt $3e, | ||
+ | |||
+ | ptn33 =* | ||
+ | .byt $03,$3e | ||
+ | .byt $03, | ||
+ | .byt $3e, | ||
+ | .byt $03, | ||
+ | .byt $3e, | ||
+ | .byt $03, | ||
+ | .byt $03, | ||
+ | .byt $03, | ||
+ | .byt $43, | ||
+ | .byt $03, | ||
+ | .byt $44, | ||
+ | .byt $3c, | ||
+ | .byt $43, | ||
+ | |||
+ | ptn34 =* | ||
+ | .byt $03, | ||
+ | .byt $4a, | ||
+ | .byt $03,$4a,$ff | ||
+ | |||
+ | ptn35 =* | ||
+ | .byt $01, | ||
+ | .byt $51, | ||
+ | .byt $51, | ||
+ | .byt $51, | ||
+ | .byt $51, | ||
+ | |||
+ | ptn36 =* | ||
+ | .byt $01, | ||
+ | .byt $01, | ||
+ | .byt $01, | ||
+ | .byt $01, | ||
+ | .byt $01, | ||
+ | .byt $01, | ||
+ | .byt $01, | ||
+ | .byt $01, | ||
+ | .byt $01, | ||
+ | |||
+ | ptn37 =* | ||
+ | .byt $5f,$5f,$5f | ||
+ | .byt $47, | ||
+ | .byt $03, | ||
+ | .byt $5f, | ||
+ | .byt $2f, | ||
+ | .byt $5f, | ||
+ | .byt $2f, | ||
+ | .byt $5f, | ||
+ | .byt $0b, | ||
+ | |||
+ | ptn38 =* | ||
+ | .byt $87 | ||
+ | .byt $0b, | ||
+ | .byt $32, | ||
+ | .byt $2f, | ||
+ | .byt $5f, | ||
+ | .byt $04, | ||
+ | |||
+ | ptn2f =* | ||
+ | .byt $03, | ||
+ | .byt $24, | ||
+ | .byt $18, | ||
+ | .byt $24, | ||
+ | .byt $18, | ||
+ | .byt $22, | ||
+ | .byt $16, | ||
+ | .byt $22, | ||
+ | .byt $16, | ||
+ | .byt $1d, | ||
+ | .byt $1d, | ||
+ | .byt $1d, | ||
+ | .byt $1d, | ||
+ | .byt $24, | ||
+ | .byt $18, | ||
+ | .byt $24, | ||
+ | .byt $18, | ||
+ | |||
+ | |||
+ | ; | ||
+ | ; | ||
+ | ; | ||
+ | |||
+ | instr =* | ||
+ | .byt $80, | ||
+ | .byt $00, | ||
+ | .byt $a0, | ||
+ | .byt $00, | ||
+ | .byt $00, | ||
+ | .byt $00, | ||
+ | .byt $00, | ||
+ | .byt $00, | ||
+ | .byt $00, | ||
+ | .byt $00, | ||
+ | .byt $00, | ||
+ | .byt $80, | ||
+ | .byt $00, | ||
+ | .byt $00, | ||
+ | .byt $00, | ||
+ | .byt $00, | ||
+ | .byt $00, | ||
+ | .byt $00, | ||
+ | .byt $00, | ||
+ | .byt $00, | ||
+ | |||
+ | .end | ||
+ | |||
+ | ============================================================================= | ||
+ | </ | ||
+ | |||
+ | ====== ZPM3 and ZCCP Enhancements for CP/M Plus from Simeon Cran ====== | ||
+ | < | ||
+ | by Randy Winchester (randy@mit.edu) | ||
+ | |||
+ | Operating System Components | ||
+ | |||
+ | The CP/M Plus operating system consists of three modules. | ||
+ | Command Processor), is the part of CP/M that you see when you first boot the | ||
+ | system. | ||
+ | commands from disk. | ||
+ | |||
+ | The BDOS (Basic Disk Operating System) handles the CP/M functions of disk, | ||
+ | console, and printer input/ | ||
+ | |||
+ | The BIOS (Basic Input Output System) does the real input/ | ||
+ | BDOS. The BIOS contains the code customized for the CP/M hardware that you're | ||
+ | using. On the C128, the BIOS contains the routines for driving the 40 and 80 | ||
+ | column screens, using the REU as a RAM drive, and reading/ | ||
+ | different disk formats on 1571 and 1581 drives. | ||
+ | a collection of device drivers that are specific to your computer. | ||
+ | |||
+ | |||
+ | What's New - BIOS-R6 | ||
+ | |||
+ | BIOS-R6 (C128 BIOS modified by Randy Winchester and others) is the latest of | ||
+ | the modified versions of the C128 CP/M BIOS. Most of the changes to the BIOS | ||
+ | result in faster processing speed. For example, all the code for driving a 40 | ||
+ | column screen has been removed. | ||
+ | using it in 80 columns anyway. | ||
+ | system and increases overall speed by about 15%. Similarly, the interrupt | ||
+ | driven RS232 has been set from 300 to 75 baud. The higher the baud rate, the | ||
+ | more processor time is required to service RS232. | ||
+ | always running, decreasing the baud rate frees up cycles that the processor | ||
+ | needs to service RS232. | ||
+ | which explicitly set the baud rate when they start up. | ||
+ | |||
+ | Other features of BIOS-R6 include a screen dump function, commented source to | ||
+ | assist the programmer in producing customized systems, and support for | ||
+ | additional disk formats. | ||
+ | standard 1581 CP/M format, MAXI 71 (398K on 5.25" disks), and GP 1581 (796K on | ||
+ | 3.5" disks). | ||
+ | |||
+ | C128 CP/M programmers who want to add or change operating system features | ||
+ | should try to make changes to the BIOS. For one thing, BIOS source code is | ||
+ | available, but not available for the BDOS or CCP. (Source code is not | ||
+ | available for the BDOS and CCP replacements mentioned in this article either). | ||
+ | Another reason is that the BDOS and CCP are intended to be " | ||
+ | operating system components - that is, they are identical for different | ||
+ | computers that run CP/M Plus. A study of the BIOS source code will reveal | ||
+ | segments of code that can be removed if they aren't needed, and will provide | ||
+ | hints as to new features that can be added. | ||
+ | |||
+ | The distribution package, BIOS-R6.LBR includes documentation, | ||
+ | utilities, and support files. | ||
+ | ZPM3. [Ed. Note: The files mentioned in this article can be found via | ||
+ | anonymous FTP or via the mailserver through the " | ||
+ | |||
+ | |||
+ | ZPM3 Features | ||
+ | |||
+ | ZPM3 is a replacement BDOS by Simeon Cran. Since the BDOS is supposed to be | ||
+ | " | ||
+ | pretty typical - bug fixes, speed enhancements, | ||
+ | interacts with the BIOS and CCP in most of the same ways as the standard | ||
+ | Digital Research BDOS, and for the most part appears to be a clone of the | ||
+ | standard BDOS. The standard BDOS was coded in 8080 assembly to make it | ||
+ | compatible with machines that use the older slower 8080 processor. | ||
+ | (if any) CP/M Plus machines used the 8080. ZPM3 is coded in faster, compact Z80 | ||
+ | assembly language, for the Z80 processor that is at the heart of most CP/M Plus | ||
+ | computers (including the C128). | ||
+ | |||
+ | The ZPM3 documentation details fixes to several bugs that have plagued CP/M | ||
+ | Plus since day one. Although the bugs sound somewhat obscure, there' | ||
+ | telling when one might cause problems. | ||
+ | |||
+ | ZPM3 is much faster than standard CP/M Plus. The increased speed should be | ||
+ | obvious after using it for a short time. | ||
+ | |||
+ | The new features offered by ZPM3 are remarkable. | ||
+ | features are enhanced command line editing, a history buffer that stores and | ||
+ | recalls multiple commands, and Automatic Command Prompting. | ||
+ | work in concert to provide a flexible and convenient command line interface. | ||
+ | Command line editing now has 20 control key functions for moving or deleting by | ||
+ | characters or whole words. | ||
+ | characters) are stored in the history buffer, and can be recalled and reused, | ||
+ | or reedited if necessary. | ||
+ | seen in action. | ||
+ | it's automatic, with matching responses coming directly from the history | ||
+ | buffer. | ||
+ | and need to reuse it (or edit it slightly first), typing the first few unique | ||
+ | characters will bring back the entire command from the history buffer if it's | ||
+ | still intact. | ||
+ | some getting used to. If you don't think you can get used to it, it can be shut | ||
+ | off. | ||
+ | |||
+ | The latest version of ZPM3, ZPM3N08.ARK, | ||
+ | can also be found as a separate file. | ||
+ | |||
+ | |||
+ | |||
+ | ZCCP Documentation, | ||
+ | |||
+ | The remainder of this article will describe ZCCP and how to configure a system | ||
+ | disk to get a fully functional ZPM3/ZCCP system up and running. | ||
+ | ZPM3 both come with enough documentation to keep you busy for hours, but ZCCP | ||
+ | has never been distributed by itself, because up until this article, there has | ||
+ | not been any documentation for it. Most of the documentation that follows was | ||
+ | figured out through experimentation and later verified by Simeon Cran. | ||
+ | |||
+ | ZCCP Features | ||
+ | |||
+ | This documentation is provided to assist the user in getting a ZCCP system up | ||
+ | and running. | ||
+ | following list details which ZCPR features are provided with ZCCP, and which | ||
+ | ones aren' | ||
+ | |||
+ | * ZCPR 3.3 compatibility. | ||
+ | applications created for ZCPR 3.3 and ZCPR 3.4. | ||
+ | |||
+ | * TCAP. A Z3T termcap file describing terminal characteristics can be | ||
+ | loaded into the system. | ||
+ | to the screen - a big improvement over the old method of patching | ||
+ | individual programs with terminal control codes. | ||
+ | the ZCCP LOADSEG command. | ||
+ | |||
+ | * Named directories. | ||
+ | areas can be assigned names. | ||
+ | loaded by the ZCCP LOADSEG command. | ||
+ | |||
+ | * Command Search Path. ZCCP will search for commands along a user defined | ||
+ | search path. Up to six path elements (directories) can be defined. | ||
+ | |||
+ | * Environment block. | ||
+ | Also includes a map of active disk drives and other system information. | ||
+ | The environment block can be viewed with the Z-System SHOW utility. | ||
+ | |||
+ | * Flow control. | ||
+ | Z-System IF.COM for setting the flow state. | ||
+ | (FI, ELSE, XIF, OR, AND) are resident. | ||
+ | |||
+ | * Multiple commands can be entered on the command line. The command line | ||
+ | buffer will hold up to 225 characters. | ||
+ | semicolons. | ||
+ | |||
+ | * Extended Command Processor. | ||
+ | resident command, or located on disk along the search path, the command | ||
+ | line is passed to an extended command processor. | ||
+ | command processor is ARUNZ, a sophisticated batch file executor with alias | ||
+ | features. | ||
+ | CMDRUN.COM and place it in the ROOT directory of your boot disk. | ||
+ | |||
+ | * Error handler. | ||
+ | handle a command, control is passed to an error handler. | ||
+ | give information about the error (instead of the useless CP/M "?" | ||
+ | and allow the command line to be edited and reused. | ||
+ | |||
+ | * Resident commands. | ||
+ | CLS - clears the screen | ||
+ | NOTE - text following the NOTE command is treated as a comment. | ||
+ | FI - Flow control: | ||
+ | IF level ELSE - Flow control: | ||
+ | XIF - Flow control: | ||
+ | OR - Flow control: | ||
+ | AND - Flow control: | ||
+ | |||
+ | * Shell stack. | ||
+ | a choice of several different shells. | ||
+ | programs and word processors can also be assigned shell status. | ||
+ | |||
+ | * ZCCP uses the LOADSEG command for direct loading of RSX files that have | ||
+ | not been GENCOMed. | ||
+ | |||
+ | There are some things that Z3Plus will do that ZCCP won't do. | ||
+ | |||
+ | - ZCCP does not support a Flow Command Package (FCP). | ||
+ | transient IF command. | ||
+ | resident in ZCCP. | ||
+ | |||
+ | - A Resident Command Package (RCP) is not implemented. | ||
+ | resident in ZCCP. All other commands must be loaded from disk. This isn't | ||
+ | as much of a handicap as it might sound if you have a fast RAM drive, such | ||
+ | as a CBM 17xx REU, Quick Brown Box, or RAMLink. | ||
+ | |||
+ | - ZCCP can not load type 4 programs (used with ZCPR 3.4). It loads | ||
+ | standard COM files at 100H, and type 3 programs that load higher in memory. | ||
+ | Most type 4 programs have type 3 or COM equivalents. | ||
+ | |||
+ | - ZCCP can not reexecute loaded programs. | ||
+ | on Z-Systems with a GO command that jumps to 100H. Since ZCCP also loads at | ||
+ | 100H, a GO command would only restart ZCCP. | ||
+ | |||
+ | |||
+ | The Files | ||
+ | |||
+ | Three files are included in ZCCP.ARK: | ||
+ | |||
+ | File name Size Description | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | |||
+ | |||
+ | Getting Started - Preparing a Boot Disk | ||
+ | |||
+ | Format a Commodore CP/M format 5.25 or 3.5 inch disk. ZCCP must be booted from | ||
+ | device 8 (CP/M drive A). | ||
+ | |||
+ | Copy the files from ZCCP.ARK to user area 0 of the newly formatted disk. | ||
+ | |||
+ | Copy CPM+.SYS to user 0 of the boot disk. The CPM+.SYS must have been | ||
+ | generated using the BDOS segments from ZPM3. | ||
+ | |||
+ | Locate a copy of a Z-System alias utility. | ||
+ | others should work also. Copy it to user 0 of the boot disk. | ||
+ | |||
+ | At this point, hit the reset switch and boot the system with the new disk. | ||
+ | After the system boots, you won't be able to do much with it. The only | ||
+ | resident commands are CLS and NOTE, and ZCCP can only locate commands if they | ||
+ | are prefixed with the drive and user number. | ||
+ | |||
+ | The next step is to create a startup alias. | ||
+ | file named STARTZPM.COM and executes commands from it. STARTZPM.COM is created | ||
+ | with a ZCPR alias utility. | ||
+ | SALIAS: | ||
+ | |||
+ | | ||
+ | |||
+ | | ||
+ | |||
+ | | ||
+ | ; current drive. | ||
+ | |||
+ | QD F/F ; Installs Quick Brown Box ramdisk driver. | ||
+ | |||
+ | | ||
+ | ; LOADSEG loads the Named Directory Register | ||
+ | ; and TCAP. | ||
+ | ; Directories can now be referred to by | ||
+ | ; name, as in the next command: | ||
+ | |||
+ | | ||
+ | ; SETPTH sets the command search path. | ||
+ | ; The /c option first clears any existing path. | ||
+ | ; Directories are then listed in the | ||
+ | ; order searched. | ||
+ | ; is a 64K QBB ramdisk (drive/user F0) where | ||
+ | ; frequently used commands are stored. | ||
+ | ; a 1750 REU (drive/user M0). 1581 is a 1581 | ||
+ | ; drive, (drive/user C15) where some 700K | ||
+ | ; of utilities and applications are | ||
+ | ; located. | ||
+ | ; logged drive and user area. $$0 refers | ||
+ | ; to user area 0 of the current drive. | ||
+ | ; The ROOT directory is on drive A, user | ||
+ | ; 15, where startup utilities and system | ||
+ | ; files can be found. | ||
+ | |||
+ | 1571 [AB ; This speeds up 1571 disk drives A and B | ||
+ | ; by shutting off the redundant write verify. | ||
+ | |||
+ | | ||
+ | ; Command Prompting. | ||
+ | ; Prompting is toggled by entering CTRL-Q. | ||
+ | |||
+ | | ||
+ | |||
+ | IF ~EXIST CP.* ; Test to see if commands are loaded. | ||
+ | ; This line reads: | ||
+ | ; does not exist . . ." and sets the flow | ||
+ | ; state to true if the file doesn' | ||
+ | QD I/F ; ". . . then initialize the QBB . . ." | ||
+ | C1:CP C1:*.* F0: | ||
+ | ; ". . . copy all of the commands in | ||
+ | ; drive/user C1 to the commands (F0) | ||
+ | ; directory . . ." | ||
+ | | ||
+ | |||
+ | | ||
+ | |||
+ | CP C:ZF*.* M0: ; Copy ZFILER.COM and ZFILER.CMD to the | ||
+ | ; REU directory (M0). | ||
+ | |||
+ | | ||
+ | |||
+ | DATE S ; Set the system time and date. | ||
+ | |||
+ | | ||
+ | |||
+ | | ||
+ | |||
+ | Of course, your STARTZPM alias will vary depending on the hardware you need to | ||
+ | support, your software preferences, | ||
+ | to the upward size limit that ZCCP can handle based on the capacity of the | ||
+ | multiple command buffer. | ||
+ | up a search path and load a TCAP. | ||
+ | |||
+ | Actually, I put the cart before the horse in this example. | ||
+ | reboot your system with the LOADSEG command as listed, you'll notice that you | ||
+ | don't have a NAMES.NDR file. There isn't one distributed with ZCCP either. | ||
+ | Z-System utilities won't let you edit the NDR either, since the buffer for it | ||
+ | hasn't been created yet. This turned out to be a nasty chicken/egg situation, | ||
+ | hopefully solved by the inclusion of a sample NAMES.NDR file containing simply | ||
+ | A0:SYSTEM and A15:ROOT. | ||
+ | |||
+ | At this point, you should have a mostly functioning ZCCP system disk. Press | ||
+ | reset and boot it up. You might want to correct any problems with it or tweak | ||
+ | it to perfection before moving on. | ||
+ | |||
+ | |||
+ | List of Z-System Utilities for ZCCP | ||
+ | |||
+ | Some of the following utilities are essential, others are nice to have. The | ||
+ | version numbers listed are the latest known versions at the time that this | ||
+ | documentation was written. | ||
+ | them are available on Simtel20 or its mirror sites. | ||
+ | utilities will be uploaded to cco.caltech.edu. | ||
+ | |||
+ | | ||
+ | the other ZCPR alias utilities) are essential. | ||
+ | |||
+ | | ||
+ | ever. | ||
+ | |||
+ | | ||
+ | types of sorts, list formats, etc., displays date stamps, and supports | ||
+ | | ||
+ | |||
+ | | ||
+ | | ||
+ | |||
+ | | ||
+ | |||
+ | | ||
+ | is programmable in that it can execute user defined macros from a | ||
+ | | ||
+ | | ||
+ | | ||
+ | |||
+ | | ||
+ | file is essential if you want to use any ZCPR programs that need a | ||
+ | | ||
+ | | ||
+ | and underline modes. | ||
+ | |||
+ | | ||
+ | |||
+ | | ||
+ | | ||
+ | with ZCCP. | ||
+ | |||
+ | | ||
+ | | ||
+ | |||
+ | | ||
+ | be crunched (*.HZP), and/or loaded from a HELP.LBR library. | ||
+ | |||
+ | | ||
+ | | ||
+ | any other executable utility) as an extended command processor, rename | ||
+ | it to CMDRUN.COM. | ||
+ | |||
+ | | ||
+ | | ||
+ | | ||
+ | |||
+ | | ||
+ | |||
+ | | ||
+ | | ||
+ | |||
+ | | ||
+ | | ||
+ | | ||
+ | |||
+ | | ||
+ | | ||
+ | | ||
+ | |||
+ | | ||
+ | It is based on earlier versions of Z-System, and is a little dated, | ||
+ | but otherwise contains information that you won't find anywhere else. | ||
+ | Not everything in the manual applies to operation of ZPM3/ZCCP, but | ||
+ | with the documentation presented here, you should be able to get a | ||
+ | good idea of what works and what doesn' | ||
+ | |||
+ | |||
+ | ZCCP Technical Notes | ||
+ | |||
+ | ZCCP is a replacement CCP that implements ZCPR 3.3. It loads at 100H and is | ||
+ | stored in the bank 0 CCP buffer for fast reloading as does the standard CCP. | ||
+ | By contrast, Z3Plus loads into high memory and can be overwritten by transient | ||
+ | commands, requiring reloading Z3Plus from disk. Because ZCCP replaces the CCP, | ||
+ | a ZCCP system has more TPA (transient program area) than a Z3Plus system. | ||
+ | ZCCP system on the C128 has more than 57K of TPA, almost the same amount as a | ||
+ | standard C128 CP/M system. | ||
+ | |||
+ | This should be enough information to get started with ZPM3/ZCCP. Set up a boot | ||
+ | disk, experiment with some Z-System utilities, read ZMAN-NEW, and get some | ||
+ | applications running. | ||
+ | |||
+ | ============================================================================= | ||
+ | </ | ||
+ | ====== Multi-Tasking on the C=128 - Part 1 ====== | ||
+ | < | ||
+ | by Craig Taylor (duck@pembvax1.pembroke.edu) | ||
+ | |||
+ | I. Introduction / Package Over-view.. | ||
+ | |||
+ | This article will detail the multi-tasking kernal which I have written butt | ||
+ | is still in the debugging stage . The documentation is being released now as | ||
+ | C= Hacking has been delayed for a month while this article and a few others | ||
+ | were in the process of being shaped. The source code listings, binaries, and | ||
+ | a few sample programs will be in the next issue of C= Hacking as well as | ||
+ | | ||
+ | | ||
+ | |||
+ | The Commodore 128 does not support TRUE multi-tasking in that the processor | ||
+ | | ||
+ | | ||
+ | The Commodore 128 greatly simplifies things as in addation to the interrupts | ||
+ | it also has the provision to relocate zero page and the stack page. So the | ||
+ | | ||
+ | | ||
+ | the stack page and registers and resuming the normal IRQ as if nothing had | ||
+ | ever happened. | ||
+ | |||
+ | | ||
+ | | ||
+ | with memory contention, and with an over-all slowdown in speed. The package | ||
+ | will detail how to handle device contentions, | ||
+ | | ||
+ | | ||
+ | | ||
+ | using it. However, note that if you have multiple programs doing this then | ||
+ | you may have problems with one grabbing a logical file number after the | ||
+ | other process has checked for it. Multi-tasking is fun ' | ||
+ | this will be examined when we get into semaphores later in this article.. | ||
+ | |||
+ | Craig Bruce' | ||
+ | | ||
+ | With minor modifications (basically just changing the initial allocations so | ||
+ | that the package is not killed) it should be able to work. Also it will need | ||
+ | | ||
+ | So a memory manager is not too much of a problem. Details of what changes | ||
+ | will be necessary shall be in the next issue. | ||
+ | |||
+ | What is a process? What is a program? I've been using the terms almost | ||
+ | | ||
+ | them the same. A process, or program is defined as a program with it's own | ||
+ | | ||
+ | | ||
+ | the zero page although this is likely to change). | ||
+ | | ||
+ | | ||
+ | | ||
+ | to execute something, and serve as signals betweenn them. | ||
+ | |||
+ | | ||
+ | side of my house yet), will support pipes and a more strongly typed kernal | ||
+ | so that processes may be prioritized. | ||
+ | |||
+ | II. A Look At Multi-Tasking | ||
+ | |||
+ | The introduction introduced some basic elements of multi-tasking but I'll | ||
+ | | ||
+ | the concepts can get a bit confusing. | ||
+ | |||
+ | Background - A process is said to be in the " | ||
+ | the foreground task and may or may not have input devi ces associated | ||
+ | with it. | ||
+ | |||
+ | Foreground - A process is said to be " | ||
+ | active process and is holding the keyboard and screen display captive | ||
+ | (ie: the user is actually working within it). | ||
+ | | ||
+ | Kernal | ||
+ | needed by any programs in memory.. | ||
+ | |||
+ | Multi-Tasking - Execution of more than one process at any given | ||
+ | time. | ||
+ | |||
+ | Priority - A value associated with each process that determines how | ||
+ | often, and possibly when a process is executed. | ||
+ | |||
+ | Process - The space in memory taken up by executable program code, any | ||
+ | associated data, the stack and the registers associated and currently in | ||
+ | use by it, including the current PC (program counter).. | ||
+ | |||
+ | Semaphores - Values that are globally accessed by processes to share and | ||
+ | communicate information between each other and the kernal. | ||
+ | |||
+ | Some CPU's have available a multi- tasking mode (the 386 and 486 are the | ||
+ | most famaliar ones that come to mind), y et the 8502 chip contained inside | ||
+ | the Commodore 128 was first designed before 1985 and lacks multi-tasking. It | ||
+ | would be nice if such a multi-tasking CPU in the 6502 family did exist but | ||
+ | it would also create problems with the 6502 style architecture and wouldd | ||
+ | | ||
+ | |||
+ | So how is the C=128 supposed to do multi-tasking? | ||
+ | it.. | ||
+ | |||
+ | | ||
+ | |||
+ | Program 1: Program 2: | ||
+ | - lda #65 ; the " | ||
+ | jsr $ffd2 ; print it jsr $ffd2 ; print it | ||
+ | jmp - jmp -- | ||
+ | |||
+ | And we wanted them to multi-task we'd expect something like the following: | ||
+ | |||
+ | @A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@AA | ||
+ | |||
+ | | ||
+ | even non-simulated ones. Since we're only going to be switching tasks every | ||
+ | 1/60 of a second then we're more likely to see an output similair to this: | ||
+ | |||
+ | @@@@@@AAAAAAA@@@@@@@AAAAAAA@@@@@@@@AAAAAAA@@@@@@@AAAAAAAA@@@@@@@@AAAAAA@@@@@@@ | ||
+ | |||
+ | So that it seems a process will run for about 1/60 of a second beforee | ||
+ | | ||
+ | | ||
+ | We run into problems however. The KERNAL in the C128 that contains most | ||
+ | off the file handling, screen manipulations, | ||
+ | was never designed with the idea of multi-tasking in mind. So we're gonna | ||
+ | have code running in the KERNAL in two spots for the two differant processes | ||
+ | and it's more than likely we'll end up with something like: | ||
+ | |||
+ | @@@@@@@@< | ||
+ | |||
+ | There' | ||
+ | |||
+ | A semaphore is a value that is checked before access is granted to another | ||
+ | group of memory locations. The semaphore is basically requested via the | ||
+ | | ||
+ | |||
+ | request_semaphore | ||
+ | ldx semaphore | ||
+ | dex | ||
+ | beq + | ||
+ | cli | ||
+ | - ldy #$ff | ||
+ | dey | ||
+ | bne - | ||
+ | + inc semaphore | ||
+ | cli | ||
+ | |||
+ | Now the request_semaphore has to disable interrupts to prevent another task | ||
+ | from changing the semaphore value before this routine has had a chance. The | ||
+ | | ||
+ | |||
+ | Using a similair routine that performs the opposite - setting the semaphore | ||
+ | to a zero value when finished we can dictate what program has control over | ||
+ | what device or what memory areas. | ||
+ | |||
+ | The semaphores will be used to govern access to the KERNAL routines which | ||
+ | | ||
+ | | ||
+ | the same block of memory to two or more processes. | ||
+ | |||
+ | III. Multi-Tasking Function Calls (Package Calls) | ||
+ | |||
+ | OffSet | Name | Notes | ||
+ | -------+---------------+-------------------------------------------------- | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | -------+---------------+-------------------------------------------------- | ||
+ | |||
+ | IV. | ||
+ | |||
+ | The package should be available at the time of the next issue. A further | ||
+ | examination of how the routines work shall be examined along with the source | ||
+ | code. | ||
+ | |||
+ | Errors popped up in developing it and rather than delay C= Hacking any | ||
+ | further I decided to go ahead and release the above information so that | ||
+ | individuals can start developing appropriate routines. In addition, | ||
+ | please note that PIPEs _may_ or may not be supported in the next issue. | ||
+ | I have not fully made up my mind yet on them. | ||
+ | |||
+ | V. Referencess | ||
+ | |||
+ | Born to Code in C, Herbert Schildt, Osborne-McGraw Hill, p.203-252. | ||
+ | |||
+ | Notes from Operating Systems Course, Pembroke State Univ, Fall '92. | ||
+ | |||
+ | ============================================================================= | ||
+ | </ | ||
+ | ====== LITTLE RED READER: MS-DOS file reader/ | ||
+ | < | ||
+ | by Craig Bruce (csbruce@neumann.uwaterloo.ca) | ||
+ | |||
+ | 1. INTRODUCTION | ||
+ | |||
+ | This article is a continuation of the Little Red Reader article from last | ||
+ | issue. | ||
+ | reading them. The program still works drive-to-drive so you'll still need two | ||
+ | disk drives (either physical or logical) to use it. The program has also been | ||
+ | extended to allow MS-DOS files to be deleted and to allow the copying of | ||
+ | Commodore-DOS files between CBM-DOS disks (this makes it more convenient to | ||
+ | use the program with a temporary logical drive like RAMDOS). | ||
+ | have recently acquired a CMD FD-4000 floppy disk drive, I know that this | ||
+ | program works with MS-DOS disks with this drive (but only for the 720K | ||
+ | format). | ||
+ | |||
+ | The program still has the same organization as last time: a menu-oriented | ||
+ | user-interface program written in BASIC that makes use of a package of MS-DOS | ||
+ | disk accessing routines written in machine language. | ||
+ | Public Domain Software, so feel free to distribute and/or mangle it as you | ||
+ | wish. Just note any manglings on the " | ||
+ | blame me. | ||
+ | |||
+ | The program runs on either the 40 or 80-column screens, but you will get | ||
+ | much better performance from the BASIC portion of the program by being | ||
+ | in 80-column mode and FAST mode. A modification that someone might want | ||
+ | to make would be to spread-out the display for the 80-column screen and add | ||
+ | color to the rather bland display. | ||
+ | |||
+ | 2. USER GUIDE | ||
+ | |||
+ | LOAD and RUN the " | ||
+ | it will display an " | ||
+ | language package from the " | ||
+ | obtained from PEEK(186) - the last device accessed). | ||
+ | loaded only on the first run and is not reloaded on subsequent runs if the | ||
+ | package ID field is in place. | ||
+ | |||
+ | The system is designed to have two file selection menus: one for the MS-DOS | ||
+ | disk drive, and one for the Commodore-DOS disk drive (which may be a logical | ||
+ | disk drive). | ||
+ | these menus, and then program knows to copy them to the disk for the other | ||
+ | menu. This idea of having two selection menus is also very consistent with | ||
+ | the original program. | ||
+ | |||
+ | 2.1. MS-DOS MENU | ||
+ | |||
+ | When the program starts, the MS-DOS menu of the program is displayed. | ||
+ | looks like: | ||
+ | |||
+ | | ||
+ | |||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | |||
+ | D=DIR M=MSDEV F=CBMDEV C=COPY Q=QUIT | ||
+ | | ||
+ | |||
+ | except that immediately after starting up, "< | ||
+ | displayed rather than filenames. | ||
+ | it did in the last issue of C= Hacking. | ||
+ | number of bytes free on the drive are displayed (which is useful to know when | ||
+ | writing files) and there are some more commands. | ||
+ | |||
+ | The directory (" | ||
+ | (" | ||
+ | quit (" | ||
+ | cursor movement commands all work the same as before. | ||
+ | use to flog the beast into submission. | ||
+ | delete), "/" | ||
+ | |||
+ | The remove command is used to delete selected files from the MS-DOS disk. | ||
+ | After selecting this option, you will get an annoying "are you sure" question | ||
+ | and the the selected files will quickly disappear and the changes will finally | ||
+ | be written to disk. Deleting a batch of MS-DOS files is much quicker than | ||
+ | deleting Commodore-DOS files since MS-DOS disks use a File Allocation Table | ||
+ | rather than the linked list of blocks organization that CBM uses. In order to | ||
+ | make the BASIC program execute quicker, after deleting, the original order of | ||
+ | the filenames in the directory listing will be changed. | ||
+ | the delete operation is non-recoverable. | ||
+ | |||
+ | The change menu command is used to move back and forth between the Commodore- | ||
+ | DOS and MS-DOS menus. | ||
+ | |||
+ | 2.2. COMMODORE-DOS MENU | ||
+ | |||
+ | The Commodore-DOS menu, which displays the names of the Commodore files | ||
+ | selected for various operations, looks and works pretty much the same as | ||
+ | the MS-DOS menu: | ||
+ | |||
+ | | ||
+ | |||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | |||
+ | D=DIR M=MSDEV F=CBMDEV C=COPY Q=QUIT | ||
+ | | ||
+ | |||
+ | You'll notice, however, that the filetype field (" | ||
+ | unchangable. | ||
+ | block count of the file multiplied by 254. This menu is not maintained for | ||
+ | files being copied to the CBM-DOS disk from an MS-DOS disk. You'll | ||
+ | have to re-execute the Directory instruction to get an updated listing. | ||
+ | |||
+ | The " | ||
+ | Commodore-DOS directory will be loaded from the current CBM device number. | ||
+ | Note that in order for this to work, the CBM device must be number eight | ||
+ | or greater (a disk drive). | ||
+ | written using only GET#'s from the disk and was very slow. It was modified, | ||
+ | however, to call a machine language subroutine to read the information for | ||
+ | a directory entry from the directory listing, and hence the subroutine now | ||
+ | operates at a tolerable speed. | ||
+ | |||
+ | The " | ||
+ | means to copy the selected CBM files to the MS-DOS disk. See details below. | ||
+ | |||
+ | The copy CBM files (" | ||
+ | to another CBM-DOS disk unit. Select the files you want to copy and then | ||
+ | press X. You will then be asked what device number you want to copy the files | ||
+ | to. The device can be another disk drive or any other device (except the | ||
+ | keyboard). | ||
+ | with copying MS-DOS to CBM. If you are copying to a disk device and the file | ||
+ | already exists, then you will be asked if you wish to overwrite the file. You | ||
+ | cannot copy to the same disk unit. Also, all files are copied in binary mode | ||
+ | (regardless of what translation you have selected for a file). | ||
+ | |||
+ | The copy CBM files command was included since all of the low-level gear | ||
+ | needed to implement it (specifically " | ||
+ | also required by other functions. | ||
+ | working with RAMDOS. | ||
+ | have a RAM expander and have installed RAMDOS as device 9, then you would | ||
+ | copy MS-DOS files to RAMDOS using the MS-DOS menu, and then you would go to | ||
+ | the Commodore-DOS menu ("/" | ||
+ | Commodore-DOS diskette into your 1571, and then use " | ||
+ | RAMDOS device to the 1571. | ||
+ | |||
+ | The remove command (" | ||
+ | your CBM-DOS files your damn self. | ||
+ | |||
+ | 2.3. COPY CBM-DOS TO MS-DOS | ||
+ | |||
+ | Before you can copy selected CBM-DOS files to an MS-DOS disk, the MS-DOS disk | ||
+ | directory must be already loaded (from the MS-DOS menu). | ||
+ | since the directory and FAT information are kept in memory at all times during | ||
+ | the execution of this program. | ||
+ | |||
+ | When you enter copy mode, the screen will clear and the name of each selected | ||
+ | file is displayed as it is being copied. | ||
+ | the MS-DOS or CBM-DOS drive during copying, an error message will be displayed | ||
+ | and copying will continue (after you press a key for MS-DOS errors). | ||
+ | note that not a whole lot of effort was put into error recovery. | ||
+ | |||
+ | To generate an MS-DOS filename from an CBM-DOS filename, the following | ||
+ | algorithm is used. The filename is searched from right to left for the last | ||
+ | " | ||
+ | 11 characters, is used as the MS-DOS filename. | ||
+ | used as the extension. | ||
+ | it, up to eight, will be used as the MS-DOS filename and all characters after | ||
+ | the final " | ||
+ | |||
+ | Then, the newly generated MS-DOS filename is scanned for any extra " | ||
+ | characters or embedded spaces. | ||
+ | underscore character (" | ||
+ | display). | ||
+ | the filename and extension portions of the MS-DOS filename. | ||
+ | characters are converted to lowercase PETSCII (which is uppercase ASCII) when | ||
+ | they are copied into the MS-DOS filename. | ||
+ | is not in the 8/3 format of MS-DOS, then something in the name may be lost. | ||
+ | Some examples of filename conversion follow: | ||
+ | |||
+ | CBM-DOS FILENAME | ||
+ | ---------------- | ||
+ | " | ||
+ | " | ||
+ | "hello there.text" | ||
+ | " | ||
+ | "file 1..3.s__5" | ||
+ | |||
+ | It would have been time-consuming to have the program scan the MS-DOS | ||
+ | directory for a filename already existing on the disk, so LRR will put | ||
+ | multiple files on a disk with the same filename without complaining. | ||
+ | also gets rid of the problem of asking you if you want to overwrite the old | ||
+ | file or generate a new name. However, in order to retrieve the file from | ||
+ | disk on an MS-DOS machine, you will probably have to use the RENAME command to | ||
+ | rename the first versions of the file on the disk to something else so MS-DOS | ||
+ | will scan further in the directory for the last version of the file with the | ||
+ | same filename. | ||
+ | it in time. It would have been fairly easy to put in. | ||
+ | |||
+ | The date generated for a new MS-DOS file will be all zeros. | ||
+ | interpret this as 12:00 am, 01-Jan-80 and others don't display a date at all | ||
+ | for this value. | ||
+ | |||
+ | The physical copying of the file is done completely in machine language and | ||
+ | nothing is displayed on the screen while this is happening, but you can follow | ||
+ | things by looking at the blinking lights and listening for clicks and grinds. | ||
+ | |||
+ | Since the FAT and directory are maintained in RAM during the entire copying | ||
+ | process and are only flushed to disk after the entire batch of files are | ||
+ | copied, copying is made more efficient, since there will be no costly seek | ||
+ | back to track 0 after writing each file (like MS-DOS does). | ||
+ | number of small files to copy, then they will be knocked off in quick | ||
+ | succession, faster than many MS-DOS machines will copy them. | ||
+ | |||
+ | To simplify the implementation, | ||
+ | is not maintained like it is for reading. | ||
+ | is used for a 1571, which is not optimal. | ||
+ | slow operation anyway, and since the 1571 is particularly bad by insisting on | ||
+ | verifying blocks, not much more overhead is introduced than is already | ||
+ | present. | ||
+ | |||
+ | An interesting note about writing MS-DOS disks is that you can terminate LRR | ||
+ | in the middle of a copy (with STOP+RESTORE) or in the middle of copying a | ||
+ | batch of files, and the MS-DOS disk will remain in a perfectly consistent | ||
+ | state afterwards. | ||
+ | reason is that the control information (the FAT and directory) is maintained | ||
+ | internally and is flushed only after copying is all completed. | ||
+ | terminate LRR while it is flushing the control information. | ||
+ | |||
+ | Here is a table of copying speeds for copying to 1571, 1581, and CMD FD-4000 | ||
+ | disk units with ASC and BIN translation modes. | ||
+ | second, which includes both reading the byte from a C= disk and writing it to | ||
+ | the MS-DOS disk. The average speed for either the read or write operation | ||
+ | individually will be twice the speed given below. | ||
+ | from copying a 156,273 byte text file (the text of C= Hacking Issue #4). | ||
+ | |||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | |||
+ | These figures are for transfer speed only, not counting the couple of seconds | ||
+ | of opening files and flushing the directory. | ||
+ | are JiffyDOS-ified, | ||
+ | explain why an FD-4000 is so much slower than a 1581 for copying from a | ||
+ | RAMDOS file, but the same speed or better for copying from anything else. | ||
+ | |||
+ | Since I don't have access to an actual MS-DOS machine, I have not tested the | ||
+ | files written onto an MS-DOS disk by LRR, except by reading them back with LRR | ||
+ | and BBR. I do know, however, that earlier encarnations of this program did | ||
+ | work fine with MS-DOS machines. | ||
+ | |||
+ | 3. MS-DOS ROOT DIRECTORY | ||
+ | |||
+ | It was brought to my attention that I made a mistake in the pervious article. | ||
+ | I was wrong about the offset of the attributes field in a directory entry. | ||
+ | The layout should have been as follows: | ||
+ | |||
+ | | ||
+ | | ||
+ | | ||
+ | 8..10 | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | |||
+ | 4. FILE COPYING PACKAGE | ||
+ | |||
+ | As was mentioned above, Little Red Reader is split into two pieces: a BASIC | ||
+ | front-end user interface program and a package of machine language subroutines | ||
+ | for disk accessing. | ||
+ | most of the MS-DOS directory searching/ | ||
+ | package handles the hardware input/ | ||
+ | structure manipulations. | ||
+ | |||
+ | The file copying package is written in assembly language and is loaded into | ||
+ | memory at address $8000 on bank 0 and requires about 13K of memory. | ||
+ | package is loaded at this high address to be out of the way of the main BASIC | ||
+ | program, even if RAMDOS is installed. | ||
+ | |||
+ | This section of the article is presented in its entirety, including all of the | ||
+ | information given last time. | ||
+ | |||
+ | 4.1. INTERFACE | ||
+ | |||
+ | The subroutine call interface to the file copying package is summarized as | ||
+ | follows: | ||
+ | |||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | |||
+ | where " | ||
+ | |||
+ | The parameter passing interface is summarized as follows: | ||
+ | |||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | |||
+ | Where " | ||
+ | the processor registers. | ||
+ | |||
+ | The MS-DOS device number and device type interface variables allow you to set | ||
+ | the MS-DOS drive and the package identification number allows the application | ||
+ | program to check if the package is already loaded into memory so that it only | ||
+ | has to load the package the first time the application is run and not on | ||
+ | re-runs. | ||
+ | 132. | ||
+ | |||
+ | 4.1.1. INIT_PACKAGE SUBROUTINE | ||
+ | |||
+ | The " | ||
+ | installed, whenever the MS-DOS device number is changed, and whenever a new | ||
+ | disk is mounted to invalidate the internal track cache. | ||
+ | parameters. | ||
+ | |||
+ | 4.1.2. MS_DIR SUBROUTINE | ||
+ | |||
+ | The " | ||
+ | parameters into the internal memory of the package from the current MS-DOS | ||
+ | device number. | ||
+ | returns a pointer to the directory space in the .AY registers and the number | ||
+ | of directory entries in the .X register. | ||
+ | subroutine returns with the Carry flag set and the error code is available in | ||
+ | the " | ||
+ | space as it was read in raw from the directory sectors on the MS-DOS disk. | ||
+ | |||
+ | 4.1.3. MS_READ SUBROUTINE | ||
+ | |||
+ | The " | ||
+ | specified CBM-Kernal logical file number (the CBM file must already be | ||
+ | opened). | ||
+ | discarded after it is read from the MS-DOS file. The starting cluster number | ||
+ | of the file to copy and the low and mid bytes of the file length are passed in | ||
+ | the PV+5 and PV+7 interface words. | ||
+ | the .A register ($00=binary, | ||
+ | output to is passed in the .X register. | ||
+ | returns with the Carry flag set and the error code in the " | ||
+ | variable. | ||
+ | |||
+ | Note that since the starting cluster number and low-file length of the file to | ||
+ | be copied are required rather than the filename, it is the responsibility of | ||
+ | the front-end application program to dig through the raw directory sector data | ||
+ | to get this information. | ||
+ | file of whatever filetype on whatever device is required; the package does not | ||
+ | need to know the Commodore-DOS device number. | ||
+ | |||
+ | 4.1.4. MS_WRITE SUBROUTINE | ||
+ | |||
+ | The " | ||
+ | logical file number to a MS-DOS file. The MS-DOS device number and type are | ||
+ | set above. | ||
+ | the " | ||
+ | and CBM lfn are passed in the .A and .X registers as in the " | ||
+ | An error return is given in the usual way (.CS, errno). | ||
+ | no return values. | ||
+ | |||
+ | It is the responsibility of the calling program to initialize the MS-DOS | ||
+ | directory entry to all zeros and then set the filename and set the starting | ||
+ | cluster pointer to $0FFF. | ||
+ | file length fields of the directory entry when it finishes. | ||
+ | "dirty flags" are modified so that the directory and FAT will be flushed on | ||
+ | the next call to " | ||
+ | |||
+ | 4.1.5. MS_FLUSH SUBROUTINE | ||
+ | |||
+ | The " | ||
+ | msDevice and msType. | ||
+ | MS-DOS disk, to both physical replicas of the disk FAT. Then, each directory | ||
+ | sector that is dirty will be written to disk. After flushing, the internal | ||
+ | dirty flags will be cleared. | ||
+ | There are no other output parameters. | ||
+ | no dirty flags set, then it will return immediately, | ||
+ | disk. | ||
+ | |||
+ | 4.1.6. MS_DELETE SUBROUTINE | ||
+ | |||
+ | The " | ||
+ | (and hence, data clusters) allocated to a file and mark the directory entry as | ||
+ | being deleted (by putting an $E5 into the first character of the filename). | ||
+ | The file is specified by giving the pointer to the directory entry in | ||
+ | interface word at PV+9. After deallocating the file data, the internal | ||
+ | " | ||
+ | is on, but nothing will be written to disk. There is no error return from | ||
+ | this routine. | ||
+ | call the " | ||
+ | |||
+ | 4.1.7. MS_FORMAT SUBROUTINE | ||
+ | |||
+ | The " | ||
+ | format the MS-DOS disk and generate and write the boot sector, initial FAT, | ||
+ | and initial directory entry data. | ||
+ | |||
+ | 4.1.8. MS_BYTES_FREE SUBROUTINE | ||
+ | |||
+ | The " | ||
+ | Allocation Table, count the number of clusters free, and return the number of | ||
+ | bytes free for file storage on the disk. There are no input parameters and | ||
+ | the bytes free are returned in the .AYX registers (.A=low, .Y=mid, .X=high | ||
+ | byte). | ||
+ | directory is actually loaded. | ||
+ | |||
+ | 4.1.9. CBM_COPY SUBROUTINE | ||
+ | |||
+ | The " | ||
+ | number given in the .A register to an output CBM-Kernal lfn given in the .X | ||
+ | register, in up to 1024 byte chunks. | ||
+ | translation). | ||
+ | or not, but the input device must be a disk unit (either logical or physical). | ||
+ | An error return is given in the usual way. | ||
+ | |||
+ | 4.1.10. CBM_DIRENT SUBROUTINE | ||
+ | |||
+ | The " | ||
+ | lfn given in .A and puts the data into interface variables. | ||
+ | lfn is assumed to be open for reading a directory (" | ||
+ | returned in the word at PV+11, the first character of the filetype is returned | ||
+ | at PV+13, the number of characters in the filename is returned in PV+14, and | ||
+ | the filename characters are returned in bytes PV+15 to PV+30. | ||
+ | is given in the usual way. | ||
+ | |||
+ | This routine assumes that the first two bytes of the directory file have | ||
+ | already been read. The first call to this routine will return the name of the | ||
+ | disk. The end of a directory is signalled by a filename length of zero. In | ||
+ | this case, the block count returned will be the number of blocks free on the | ||
+ | disk. | ||
+ | |||
+ | 4.2. IMPLEMENTATION | ||
+ | |||
+ | This section presents the code that implements the MS-DOS file reading and | ||
+ | writing package. | ||
+ | the % symbol. | ||
+ | assembler code from the rest of this magazine (and all of my ugly comments). | ||
+ | On a Unix system, all you have to do is execute the following command line | ||
+ | (substitute filenames as appropriate): | ||
+ | |||
+ | grep ' | ||
+ | |||
+ | % ; Little Red Reader/ | ||
+ | % ; Written for C= Hacking Net-Magazine; | ||
+ | % | ||
+ | |||
+ | The code is written for the Buddy assembler and here are a couple setup | ||
+ | directives. | ||
+ | |||
+ | % .org $8000 | ||
+ | % .obj " | ||
+ | % | ||
+ | % ;====jump table and parameters interface ==== | ||
+ | % | ||
+ | % jmp initPackage ;() | ||
+ | % jmp msDir ;( msDevice, msType ) : .AY=dirAddr, | ||
+ | % jmp msRead | ||
+ | % jmp msWrite | ||
+ | % jmp msFlush | ||
+ | % jmp msDelete | ||
+ | % jmp msFormat | ||
+ | % jmp msBytesFree ;( ) : .AYX=bytesFree | ||
+ | % jmp cbmCopy | ||
+ | % jmp cbmDirent | ||
+ | % | ||
+ | % .byte $cb, | ||
+ | |||
+ | These interface variables are included in the package program space to | ||
+ | minimize unwanted interaction with other programs loaded at the same time, | ||
+ | such as the RAMDOS device driver. | ||
+ | |||
+ | % errno .buf 1 | ||
+ | % msDevice | ||
+ | % msType | ||
+ | % startCluster | ||
+ | % lenML .buf 2 ;length medium and low bytes | ||
+ | % writeDirent | ||
+ | % cdirBlocks | ||
+ | % cdirType | ||
+ | % cdirFlen | ||
+ | % cdirName | ||
+ | % | ||
+ | |||
+ | This command is not currently implemented. | ||
+ | |||
+ | % msFormat = * | ||
+ | % brk | ||
+ | % | ||
+ | % ;====global declaraions==== | ||
+ | % | ||
+ | % kernelListen = $ffb1 | ||
+ | % kernelSecond = $ff93 | ||
+ | % kernelUnlsn | ||
+ | % kernelAcptr | ||
+ | % kernelCiout | ||
+ | % kernelSpinp | ||
+ | % kernelChkin | ||
+ | % kernelChkout = $ffc9 | ||
+ | % kernelClrchn = $ffcc | ||
+ | % kernelChrin | ||
+ | % kernelChrout = $ffd2 | ||
+ | % | ||
+ | % st = $90 | ||
+ | % ciaClock = $dd00 | ||
+ | % ciaFlags = $dc0d | ||
+ | % ciaData | ||
+ | % | ||
+ | |||
+ | These are the parameters and derived parameters from the boot sector. | ||
+ | are kept in the program space to avoid interactions. | ||
+ | |||
+ | % clusterBlockCount .buf 1 ;1 or 2 | ||
+ | % fatBlocks | ||
+ | % rootDirBlocks | ||
+ | % rootDirEntries | ||
+ | % totalSectors | ||
+ | % firstFileBlock | ||
+ | % firstRootDirBlock .buf 1 | ||
+ | % fileClusterCount | ||
+ | % lastFatEntry | ||
+ | % | ||
+ | |||
+ | The cylinder (track) and side that is currently stored in the track cache | ||
+ | for reading. | ||
+ | |||
+ | % bufCylinder | ||
+ | % bufSide | ||
+ | |||
+ | These " | ||
+ | |||
+ | % fatDirty | ||
+ | % dirDirty | ||
+ | % formatParms | ||
+ | % | ||
+ | |||
+ | This package is split into a number of levels. | ||
+ | Kernal serial bus routines and the burst command protocol of the disk drives. | ||
+ | |||
+ | % ; | ||
+ | % | ||
+ | |||
+ | Connect to the MS-DOS device and send the " | ||
+ | burst command byte. | ||
+ | |||
+ | % sendU0 = * ;( .A=burstCommandCode ) : .CS=err | ||
+ | % pha | ||
+ | % lda #0 | ||
+ | % sta st | ||
+ | % lda msDevice | ||
+ | % jsr kernelListen | ||
+ | % lda #$6f | ||
+ | % jsr kernelSecond | ||
+ | % lda #" | ||
+ | % jsr kernelCiout | ||
+ | % bit st | ||
+ | % bmi sendU0Error | ||
+ | % lda #" | ||
+ | % jsr kernelCiout | ||
+ | % pla | ||
+ | % jsr kernelCiout | ||
+ | % bit st | ||
+ | % bmi sendU0Error | ||
+ | % clc | ||
+ | % rts | ||
+ | % | ||
+ | % sendU0Error = * | ||
+ | % lda #5 | ||
+ | % sta errno | ||
+ | % sec | ||
+ | % rts | ||
+ | % | ||
+ | |||
+ | Toggle the "Data Accepted / Ready For More" clock signal for the burst | ||
+ | transfer protocol. | ||
+ | |||
+ | % toggleClock = * | ||
+ | % lda ciaClock | ||
+ | % eor #$10 | ||
+ | % sta ciaClock | ||
+ | % rts | ||
+ | % | ||
+ | |||
+ | Wait for a burst byte to arrive in the serial data register of CIA#1 from the | ||
+ | fast serial bus. | ||
+ | |||
+ | % serialWait = * | ||
+ | % lda #$08 | ||
+ | % - bit ciaFlags | ||
+ | % beq - | ||
+ | % rts | ||
+ | % | ||
+ | |||
+ | Wait for and get a burst byte from the fast serial bus, and send the "Data | ||
+ | Accepted" | ||
+ | |||
+ | % getBurstByte = * | ||
+ | % jsr serialWait | ||
+ | % ldx ciaData | ||
+ | % jsr toggleClock | ||
+ | % txa | ||
+ | % rts | ||
+ | % | ||
+ | |||
+ | Send the burst commands to "log in" the MS-DOS disk and set the Read sector | ||
+ | interleave factor. | ||
+ | |||
+ | % mountDisk = * ;() : .CS=err | ||
+ | % lda #%00011010 | ||
+ | % jsr sendU0 | ||
+ | % bcc + | ||
+ | % rts | ||
+ | % + jsr kernelUnlsn | ||
+ | % bit st | ||
+ | % bmi sendU0Error | ||
+ | % clc | ||
+ | % jsr kernelSpinp | ||
+ | % bit ciaFlags | ||
+ | % jsr toggleClock | ||
+ | % jsr getBurstByte | ||
+ | % sta errno | ||
+ | % and #$0f | ||
+ | % cmp #2 | ||
+ | % bcs mountExit | ||
+ | |||
+ | Grab the throw-away parameters from the mount operation. | ||
+ | |||
+ | % ldy #0 | ||
+ | % - jsr getBurstByte | ||
+ | % sta formatParms, | ||
+ | % iny | ||
+ | % cpy #6 | ||
+ | % bcc - | ||
+ | % clc | ||
+ | |||
+ | Set the Read sector interleave to 1 for a 1581 or 4 for a 1571. | ||
+ | |||
+ | % ;** set interleave | ||
+ | % lda #%00001000 | ||
+ | % jsr sendU0 | ||
+ | % bcc + | ||
+ | % rts | ||
+ | % + lda #1 ;interleave of 1 for 1581 | ||
+ | % bit msType | ||
+ | % bmi + | ||
+ | % lda #4 ;interleave of 4 for 1571 | ||
+ | % + jsr kernelCiout | ||
+ | % jsr kernelUnlsn | ||
+ | % mountExit = * | ||
+ | % rts | ||
+ | % | ||
+ | |||
+ | Read all of the sectors of a given track into the track cache. | ||
+ | |||
+ | % bufptr = 2 | ||
+ | % secnum = 4 | ||
+ | % | ||
+ | % readTrack = * ;( .A=cylinder, | ||
+ | % pha | ||
+ | % txa | ||
+ | |||
+ | Get the side and put it into the command byte. Remember that we have to flip | ||
+ | the side bit for a 1581. | ||
+ | |||
+ | % and #$01 | ||
+ | % asl | ||
+ | % asl | ||
+ | % asl | ||
+ | % asl | ||
+ | % bit msType | ||
+ | % bpl + | ||
+ | % eor #$10 | ||
+ | % + jsr sendU0 | ||
+ | % pla | ||
+ | % bcc + | ||
+ | % rts | ||
+ | % + jsr kernelCiout | ||
+ | % lda #1 ; | ||
+ | % jsr kernelCiout | ||
+ | % lda #9 ; | ||
+ | % jsr kernelCiout | ||
+ | % jsr kernelUnlsn | ||
+ | |||
+ | Prepare to receive the track data. | ||
+ | |||
+ | % sei | ||
+ | % clc | ||
+ | % jsr kernelSpinp | ||
+ | % bit ciaFlags | ||
+ | % jsr toggleClock | ||
+ | % lda #< | ||
+ | % ldy #> | ||
+ | % sta bufptr | ||
+ | % sty bufptr+1 | ||
+ | |||
+ | Get the sector data for each of the 9 sectors of the track. | ||
+ | |||
+ | % lda #0 | ||
+ | % sta secnum | ||
+ | % - bit msType | ||
+ | % bmi + | ||
+ | |||
+ | If we are dealing with a 1571, we have to set the buffer pointer for the next | ||
+ | sector, taking into account the soft interleave of 4. | ||
+ | |||
+ | % jsr get1571BufPtr | ||
+ | % + jsr readSector | ||
+ | % bcs trackExit | ||
+ | % inc secnum | ||
+ | % lda secnum | ||
+ | % cmp #9 | ||
+ | % bcc - | ||
+ | % clc | ||
+ | % trackExit = * | ||
+ | % cli | ||
+ | % rts | ||
+ | % | ||
+ | |||
+ | Get the buffer pointer for the next 1571 sector. | ||
+ | |||
+ | % get1571BufPtr = * | ||
+ | % lda #< | ||
+ | % sta bufptr | ||
+ | % ldx secnum | ||
+ | % clc | ||
+ | % lda #> | ||
+ | % adc bufptr1571, | ||
+ | % sta bufptr+1 | ||
+ | % rts | ||
+ | % | ||
+ | % bufptr1571 = * | ||
+ | % .byte 0, | ||
+ | % | ||
+ | |||
+ | Read an individual sector into memory at the specified address. | ||
+ | |||
+ | % readSector = * ;( bufptr ) : .CS=err | ||
+ | |||
+ | Get and check the burst status byte for errors. | ||
+ | |||
+ | % jsr getBurstByte | ||
+ | % sta errno | ||
+ | % and #$0f | ||
+ | % cmp #2 | ||
+ | % bcc + | ||
+ | % rts | ||
+ | % + ldx #2 | ||
+ | % ldy #0 | ||
+ | % | ||
+ | |||
+ | Receive the 512 sector data bytes into memory. | ||
+ | |||
+ | % readByte = * | ||
+ | % lda #$08 | ||
+ | % - bit ciaFlags | ||
+ | % beq - | ||
+ | % lda ciaClock | ||
+ | % eor #$10 | ||
+ | % sta ciaClock | ||
+ | % lda ciaData | ||
+ | % sta (bufptr),y | ||
+ | % iny | ||
+ | % bne readByte | ||
+ | % inc bufptr+1 | ||
+ | % dex | ||
+ | % bne readByte | ||
+ | % rts | ||
+ | % | ||
+ | % oldClock = 5 | ||
+ | % | ||
+ | |||
+ | Write an individual sector to disk, from a specified memory address. | ||
+ | |||
+ | % writeSector = * ;( bufptr, .A=track, .X=side, .Y=sector ) : .CS=err | ||
+ | % pha | ||
+ | % sty secnum | ||
+ | |||
+ | Get the side into the burst command byte | ||
+ | |||
+ | % txa | ||
+ | % and #$01 | ||
+ | % asl | ||
+ | % asl | ||
+ | % asl | ||
+ | % asl | ||
+ | % ora #$02 | ||
+ | % bit msType | ||
+ | % bpl + | ||
+ | % eor #$10 | ||
+ | % + jsr sendU0 | ||
+ | % pla | ||
+ | % bcc + | ||
+ | % rts | ||
+ | |||
+ | Send rest of parameters for burst command. | ||
+ | |||
+ | % + jsr kernelCiout | ||
+ | % lda secnum | ||
+ | % jsr kernelCiout | ||
+ | % lda #1 ; | ||
+ | % jsr kernelCiout | ||
+ | % jsr kernelUnlsn | ||
+ | % sei | ||
+ | % lda #$40 | ||
+ | % sta oldClock | ||
+ | % sec | ||
+ | % jsr kernelSpinp | ||
+ | % sei | ||
+ | % bit ciaFlags | ||
+ | % ldx #2 | ||
+ | % ldy #0 | ||
+ | % | ||
+ | |||
+ | Write the 512 bytes for the sector. | ||
+ | |||
+ | % writeByte = * | ||
+ | % lda ciaClock | ||
+ | % cmp ciaClock | ||
+ | % bne writeByte | ||
+ | % eor oldClock | ||
+ | % and #$40 | ||
+ | % beq writeByte | ||
+ | % lda (bufptr),y | ||
+ | % sta ciaData | ||
+ | % lda oldClock | ||
+ | % eor #$40 | ||
+ | % sta oldClock | ||
+ | % lda #8 | ||
+ | % - bit ciaFlags | ||
+ | % beq - | ||
+ | % iny | ||
+ | % bne writeByte | ||
+ | % inc bufptr+1 | ||
+ | % dex | ||
+ | % bne writeByte | ||
+ | % | ||
+ | |||
+ | Read back the burst status byte to see if anything went wrong with the write. | ||
+ | |||
+ | % clc | ||
+ | % jsr kernelSpinp | ||
+ | % bit ciaFlags | ||
+ | % jsr toggleClock | ||
+ | % jsr serialWait | ||
+ | % ldx ciaData | ||
+ | % jsr toggleClock | ||
+ | % txa | ||
+ | % sta errno | ||
+ | % and #$0f | ||
+ | % cmp #2 | ||
+ | % cli | ||
+ | % rts | ||
+ | % | ||
+ | |||
+ | This next level of routines deals with logical sectors and the track cache | ||
+ | rather than with hardware. | ||
+ | |||
+ | % ; | ||
+ | % | ||
+ | |||
+ | Invalidate the track cache if the MS-DOS drive number is changed or if a new | ||
+ | disk is inserted. | ||
+ | since it will be called from RAM0. Configuration $0E gives RAM0 from $0000 to | ||
+ | $BFFF, Kernal ROM from $C000 to $FFFF, and the I/O space over the Kernal from | ||
+ | $D000 to $DFFF. | ||
+ | subroutines. | ||
+ | |||
+ | % initPackage = * | ||
+ | % lda #$0e | ||
+ | % sta $ff00 | ||
+ | % lda #$ff | ||
+ | % sta bufCylinder | ||
+ | % sta bufSide | ||
+ | % ldx #7 | ||
+ | % - sta dirDirty,x | ||
+ | % dex | ||
+ | % bpl - | ||
+ | % sta fatDirty | ||
+ | % clc | ||
+ | % rts | ||
+ | % | ||
+ | |||
+ | Locate a sector (block) in the track cache, or read the corresponding physical | ||
+ | track into the track cache if necessary. | ||
+ | side, and sector numbers of the block. | ||
+ | |||
+ | % sectorSave = 5 | ||
+ | % | ||
+ | % readBlock = * ;( .A=cylinder, | ||
+ | |||
+ | Check if the correct track is in the track cache. | ||
+ | |||
+ | % cmp bufCylinder | ||
+ | % bne readBlockPhysical | ||
+ | % cpx bufSide | ||
+ | % bne readBlockPhysical | ||
+ | |||
+ | If so, then locate the sector' | ||
+ | |||
+ | % dey | ||
+ | % tya | ||
+ | % asl | ||
+ | % clc | ||
+ | % adc #> | ||
+ | % tay | ||
+ | % lda #< | ||
+ | % clc | ||
+ | % rts | ||
+ | % | ||
+ | |||
+ | Here, we have to read the physical track into the track cache. | ||
+ | input parameters and call the hardware-level track-reading routine. | ||
+ | |||
+ | % readBlockPhysical = * | ||
+ | % sta bufCylinder | ||
+ | % stx bufSide | ||
+ | % sty sectorSave | ||
+ | % jsr readTrack | ||
+ | |||
+ | Check for errors. | ||
+ | |||
+ | % bcc readBlockPhysicalOk | ||
+ | % lda errno | ||
+ | % and #$0f | ||
+ | % cmp #11 ;disk change | ||
+ | % beq + | ||
+ | % sec | ||
+ | % rts | ||
+ | |||
+ | If the error that happened is a "Disk Change" | ||
+ | try to read the physical track again. | ||
+ | |||
+ | % + jsr mountDisk | ||
+ | % lda bufCylinder | ||
+ | % ldx bufSide | ||
+ | % ldy sectorSave | ||
+ | % bcc readBlockPhysical | ||
+ | % rts | ||
+ | % | ||
+ | |||
+ | Here, the physical track has been read into the track cache ok, so we recover | ||
+ | the original input parameters and try the top of the routine again. | ||
+ | |||
+ | % readBlockPhysicalOk = * | ||
+ | % lda bufCylinder | ||
+ | % ldx bufSide | ||
+ | % ldy sectorSave | ||
+ | % jmp readBlock | ||
+ | % | ||
+ | |||
+ | Divide the given number by 18. This is needed for the calculations to convert | ||
+ | a logical sector number to the corresponding physical cylinder, side, and | ||
+ | sector numbers that the lower-level routines require. | ||
+ | subtraction is used. This routine would probably work faster if we tried to | ||
+ | repeatedly subtract 360 (18*20) at the top, but I didn't bother. | ||
+ | |||
+ | % divideBy18 = * ;( .AY=number ) : .A=quotient, | ||
+ | % ;** could repeatedly subtract 360 here | ||
+ | % ldx #$ff | ||
+ | % - inx | ||
+ | % sec | ||
+ | % sbc #18 | ||
+ | % bcs - | ||
+ | % dey | ||
+ | % bpl - | ||
+ | % clc | ||
+ | % adc #18 | ||
+ | % iny | ||
+ | % tay | ||
+ | % txa | ||
+ | % rts | ||
+ | % | ||
+ | |||
+ | Convert the given logical block number to the corresponding physical cylinder, | ||
+ | side, and sector numbers. | ||
+ | previous article with a few simplifying tricks. | ||
+ | |||
+ | % convertLogicalBlockNum = * ;( .AY=blockNum ) : .A=cyl, .X=side, | ||
+ | % jsr divideBy18 | ||
+ | % ldx #0 | ||
+ | % cpy #9 | ||
+ | % bcc + | ||
+ | % pha | ||
+ | % tya | ||
+ | % sbc #9 | ||
+ | % tay | ||
+ | % pla | ||
+ | % ldx #1 | ||
+ | % + iny | ||
+ | % rts | ||
+ | % | ||
+ | |||
+ | Copy a sequential group of logical sectors into memory. | ||
+ | by the directory loading routine to load the FAT and Root Directory, and is | ||
+ | used by the cluster reading routine to retrieve all of the blocks of a | ||
+ | cluster. | ||
+ | physical cylinder, side, and sector equivalent, the physical values are | ||
+ | incremented to get the address of successive sectors of the group. | ||
+ | avoids the overhead of the logical to physical conversion. | ||
+ | temporaries are needed. | ||
+ | |||
+ | % destPtr = 6 | ||
+ | % curCylinder = 8 | ||
+ | % curSide = 9 | ||
+ | % curSector = 10 | ||
+ | % blockCountdown = 11 | ||
+ | % sourcePtr = 12 | ||
+ | % | ||
+ | % copyBlocks = * ;( .AY=startBlock, | ||
+ | % stx blockCountdown | ||
+ | % jsr convertLogicalBlockNum | ||
+ | % sta curCylinder | ||
+ | % stx curSide | ||
+ | % sty curSector | ||
+ | % | ||
+ | % copyBlockLoop = * | ||
+ | % lda curCylinder | ||
+ | % ldx curSide | ||
+ | % ldy curSector | ||
+ | % jsr readBlock | ||
+ | % bcc + | ||
+ | % rts | ||
+ | % + sta sourcePtr | ||
+ | % sty sourcePtr+1 | ||
+ | % ldx #2 | ||
+ | % ldy #0 | ||
+ | |||
+ | Here I unroll the copying loop a little bit to cut the overhead of the branch | ||
+ | instruction in half. (A cycle saved... you know). | ||
+ | |||
+ | % - lda (sourcePtr), | ||
+ | % sta (destPtr),y | ||
+ | % iny | ||
+ | % lda (sourcePtr), | ||
+ | % sta (destPtr),y | ||
+ | % iny | ||
+ | % bne - | ||
+ | % inc sourcePtr+1 | ||
+ | % inc destPtr+1 | ||
+ | % dex | ||
+ | % bne - | ||
+ | |||
+ | Increment the cylinder, side, sector values. | ||
+ | |||
+ | % inc curSector | ||
+ | % lda curSector | ||
+ | % cmp #10 | ||
+ | % bcc + | ||
+ | % lda #1 | ||
+ | % sta curSector | ||
+ | % inc curSide | ||
+ | % lda curSide | ||
+ | % cmp #2 | ||
+ | % bcc + | ||
+ | % lda #0 | ||
+ | % sta curSide | ||
+ | % inc curCylinder | ||
+ | % + dec blockCountdown | ||
+ | % bne copyBlockLoop | ||
+ | % clc | ||
+ | % rts | ||
+ | % | ||
+ | |||
+ | Convert a given cluster number into the first corresponding logical block | ||
+ | number. | ||
+ | |||
+ | % convertClusterNum = * ;( .AY=clusterNum ) : .AY=logicalBlockNum | ||
+ | % sec | ||
+ | % sbc #2 | ||
+ | % bcs + | ||
+ | % dey | ||
+ | % + ldx clusterBlockCount | ||
+ | % cpx #1 | ||
+ | % beq + | ||
+ | % asl | ||
+ | % sty 7 | ||
+ | % rol 7 | ||
+ | % ldy 7 | ||
+ | % + clc | ||
+ | % adc firstFileBlock | ||
+ | % bcc + | ||
+ | % iny | ||
+ | % + rts | ||
+ | % | ||
+ | |||
+ | Read a cluster into the Cluster Buffer, given the cluster number. | ||
+ | number is converted to a logical sector number and then the sector copying | ||
+ | routine is called. | ||
+ | |||
+ | % readCluster = * ;( .AY=clusterNumber ) : clusterBuf, .CS=err | ||
+ | % jsr convertClusterNum | ||
+ | % | ||
+ | % ;** read logical blocks comprising cluster | ||
+ | % ldx #< | ||
+ | % stx 6 | ||
+ | % ldx #> | ||
+ | % stx 7 | ||
+ | % ldx clusterBlockCount | ||
+ | % jmp copyBlocks | ||
+ | % | ||
+ | |||
+ | Write a logical block out to disk. The real purpose of this routine is to | ||
+ | invalidate the read-track cache if the block to be written is contained in | ||
+ | the cache. | ||
+ | |||
+ | % writeLogicalBlock = * ;( .AY=logicalBlockNumber, | ||
+ | % jsr convertLogicalBlockNum | ||
+ | % cmp bufCylinder | ||
+ | % bne + | ||
+ | % cpx bufSide | ||
+ | % bne + | ||
+ | % pha | ||
+ | % lda #$ff | ||
+ | % sta bufCylinder | ||
+ | % sta bufSide | ||
+ | % pla | ||
+ | % + jsr writeSector | ||
+ | % rts | ||
+ | % | ||
+ | % writeClusterSave .buf 2 | ||
+ | % | ||
+ | |||
+ | Write a cluster-ful of data out to disk from the cluster buffer. | ||
+ | simply calls the write logical block routine once or twice, depending on the | ||
+ | cluster size of the disk involved. | ||
+ | |||
+ | % writeCluster = * ;( .AY=clusterNumber, | ||
+ | % jsr convertClusterNum | ||
+ | % ldx #< | ||
+ | % stx bufptr | ||
+ | % ldx #> | ||
+ | % stx bufptr+1 | ||
+ | % sta writeClusterSave | ||
+ | % sty writeClusterSave+1 | ||
+ | % jsr writeLogicalBlock | ||
+ | % bcc + | ||
+ | % rts | ||
+ | % + lda clusterBlockCount | ||
+ | % cmp #2 | ||
+ | % bcs + | ||
+ | % rts | ||
+ | % + lda writeClusterSave | ||
+ | % ldy writeClusterSave+1 | ||
+ | % clc | ||
+ | % adc #1 | ||
+ | % bcc + | ||
+ | % iny | ||
+ | % + jsr writeLogicalBlock | ||
+ | % rts | ||
+ | % | ||
+ | |||
+ | This next level of routines deal with the data structures of the MS-DOS disk | ||
+ | format. | ||
+ | |||
+ | % ;====MS-DOS format level==== | ||
+ | % | ||
+ | % bootBlock = 2 | ||
+ | % | ||
+ | |||
+ | Read the disk format parameters, directory, and FAT into memory. | ||
+ | |||
+ | % msDir = * ;( ) : .AY=dirbuf, .X=dirEntries, | ||
+ | % lda #$0e | ||
+ | % sta $ff00 | ||
+ | % | ||
+ | |||
+ | Read the boot sector and extract the parameters. | ||
+ | |||
+ | % ;** get parameters from boot sector | ||
+ | % lda #0 | ||
+ | % ldy #0 | ||
+ | % jsr convertLogicalBlockNum | ||
+ | % jsr readBlock | ||
+ | % bcc + | ||
+ | % rts | ||
+ | % + sta bootBlock | ||
+ | % sty bootBlock+1 | ||
+ | % ldy #13 ;get cluster size | ||
+ | % lda (bootBlock), | ||
+ | % sta clusterBlockCount | ||
+ | % cmp #3 | ||
+ | % bcc + | ||
+ | % | ||
+ | |||
+ | If a disk parameter is found to exceed the limits of LRR, error code #60 is | ||
+ | returned. | ||
+ | |||
+ | % invalidParms = * | ||
+ | % lda #60 | ||
+ | % sta errno | ||
+ | % sec | ||
+ | % rts | ||
+ | % | ||
+ | % + ldy #16 ;check FAT replication count, must be 2 | ||
+ | % lda (bootBlock), | ||
+ | % cmp #2 | ||
+ | % bne invalidParms | ||
+ | % ldy #22 ;get FAT size in sectors | ||
+ | % lda (bootBlock), | ||
+ | % sta fatBlocks | ||
+ | % cmp #4 | ||
+ | % bcs invalidParms | ||
+ | % ldy #17 ;get directory size | ||
+ | % lda (bootBlock), | ||
+ | % sta rootDirEntries | ||
+ | % cmp #129 | ||
+ | % bcs invalidParms | ||
+ | % lsr | ||
+ | % lsr | ||
+ | % lsr | ||
+ | % lsr | ||
+ | % sta rootDirBlocks | ||
+ | % ldy #19 ;get total sector count | ||
+ | % lda (bootBlock), | ||
+ | % sta totalSectors | ||
+ | % iny | ||
+ | % lda (bootBlock), | ||
+ | % sta totalSectors+1 | ||
+ | % ldy #24 ;check sectors per track, must be 9 | ||
+ | % lda (bootBlock), | ||
+ | % cmp #9 | ||
+ | % bne invalidParms | ||
+ | % ldy #26 | ||
+ | % lda (bootBlock), | ||
+ | % cmp #2 ; | ||
+ | % bne invalidParms | ||
+ | % ldy #14 ;check number of boot sectors, must be 1 | ||
+ | % lda (bootBlock), | ||
+ | % cmp #1 | ||
+ | % bne invalidParms | ||
+ | % | ||
+ | |||
+ | Calculate the derived parameters. | ||
+ | |||
+ | % ;** get derived parameters | ||
+ | % lda fatBlocks | ||
+ | % asl | ||
+ | % clc | ||
+ | % adc #1 | ||
+ | % sta firstRootDirBlock | ||
+ | % clc ;first file sector | ||
+ | % adc rootDirBlocks | ||
+ | % sta firstFileBlock | ||
+ | % lda totalSectors | ||
+ | % ldy totalSectors+1 | ||
+ | % sec | ||
+ | % sbc firstFileBlock | ||
+ | % bcs + | ||
+ | % dey | ||
+ | % + sta fileClusterCount | ||
+ | % sty fileClusterCount+1 | ||
+ | % lda clusterBlockCount | ||
+ | % cmp #2 | ||
+ | % bne + | ||
+ | % lsr fileClusterCount+1 | ||
+ | % ror fileClusterCount | ||
+ | % + clc | ||
+ | % lda fileClusterCount | ||
+ | % adc #2 | ||
+ | % sta lastFatEntry | ||
+ | % lda fileClusterCount+1 | ||
+ | % adc #0 | ||
+ | % sta lastFatEntry+1 | ||
+ | % | ||
+ | % ;** load FAT | ||
+ | % lda #<fatbuf | ||
+ | % ldy #>fatbuf | ||
+ | % sta 6 | ||
+ | % sty 7 | ||
+ | % lda #1 | ||
+ | % ldy #0 | ||
+ | % ldx fatBlocks | ||
+ | % jsr copyBlocks | ||
+ | % bcc + | ||
+ | % rts | ||
+ | % | ||
+ | % ;** load actual directory | ||
+ | % + lda #<dirbuf | ||
+ | % ldy #>dirbuf | ||
+ | % sta 6 | ||
+ | % sty 7 | ||
+ | % lda firstRootDirBlock | ||
+ | % ldy #0 | ||
+ | % ldx rootDirBlocks | ||
+ | % jsr copyBlocks | ||
+ | % bcc + | ||
+ | % rts | ||
+ | % + lda #<dirbuf | ||
+ | % ldy #>dirbuf | ||
+ | % ldx rootDirEntries | ||
+ | % clc | ||
+ | % rts | ||
+ | % | ||
+ | |||
+ | This routine locates the given FAT table entry number and returns the value | ||
+ | stored in it. Some work is needed to deal with the 12-bit compressed data | ||
+ | structure. | ||
+ | |||
+ | % entryAddr = 2 | ||
+ | % entryWork = 4 | ||
+ | % entryBits = 5 | ||
+ | % entryData0 = 6 | ||
+ | % entryData1 = 7 | ||
+ | % entryData2 = 8 | ||
+ | % | ||
+ | % locateFatEntry = * ;( .AY=fatEntryNumber ) : entryAddr, entryBits%1 | ||
+ | |||
+ | Divide the FAT entry number by two and multiply by three because two FAT | ||
+ | entries are stored in three bytes. | ||
+ | the address of the three bytes that contain the FAT entry we are interested | ||
+ | in. I retrieve the three bytes into zero-page memory for easy manipulation. | ||
+ | |||
+ | % sta entryBits | ||
+ | % ;** divide by two | ||
+ | % sty entryAddr+1 | ||
+ | % lsr entryAddr+1 | ||
+ | % ror | ||
+ | % | ||
+ | % ;** times three | ||
+ | % sta entryWork | ||
+ | % ldx entryAddr+1 | ||
+ | % asl | ||
+ | % rol entryAddr+1 | ||
+ | % clc | ||
+ | % adc entryWork | ||
+ | % sta entryAddr | ||
+ | % txa | ||
+ | % adc entryAddr+1 | ||
+ | % sta entryAddr+1 | ||
+ | % | ||
+ | % ;** add base, get data | ||
+ | % clc | ||
+ | % lda entryAddr | ||
+ | % adc #<fatbuf | ||
+ | % sta entryAddr | ||
+ | % lda entryAddr+1 | ||
+ | % adc #>fatbuf | ||
+ | % sta entryAddr+1 | ||
+ | % ldy #2 | ||
+ | % - lda (entryAddr), | ||
+ | % sta entryData0, | ||
+ | % dey | ||
+ | % bpl - | ||
+ | % rts | ||
+ | % | ||
+ | % getFatEntry = * ;( .AY=fatEntryNumber ) : .AY=fatEntryValue | ||
+ | % jsr locateFatEntry | ||
+ | % lda entryBits | ||
+ | % and #1 | ||
+ | % bne + | ||
+ | % | ||
+ | |||
+ | If the original given FAT entry number is even, then we want the first 12-bit | ||
+ | compressed field. | ||
+ | earlier. | ||
+ | |||
+ | % ;** case 1: first 12-bit cluster | ||
+ | % lda entryData1 | ||
+ | % and #$0f | ||
+ | % tay | ||
+ | % lda entryData0 | ||
+ | % rts | ||
+ | % | ||
+ | |||
+ | Otherwise, we want the second 12-bit field. | ||
+ | |||
+ | % ;** case 2: second 12-bit cluster | ||
+ | % + lda entryData1 | ||
+ | % ldx #4 | ||
+ | % - lsr entryData2 | ||
+ | % ror | ||
+ | % dex | ||
+ | % bne - | ||
+ | % ldy entryData2 | ||
+ | % rts | ||
+ | % | ||
+ | % fatValue = 9 | ||
+ | % | ||
+ | |||
+ | Change the value in a FAT entry. | ||
+ | routine. | ||
+ | |||
+ | % setFatEntry = * ;( .AY=fatEntryNumber, | ||
+ | % jsr locateFatEntry | ||
+ | % lda fatValue+1 | ||
+ | % and #$0f | ||
+ | % sta fatValue+1 | ||
+ | % lda entryBits | ||
+ | % and #1 | ||
+ | % bne + | ||
+ | % | ||
+ | % ;** case 1: first 12-bit cluster | ||
+ | % lda fatValue | ||
+ | % sta entryData0 | ||
+ | % lda entryData1 | ||
+ | % and #$f0 | ||
+ | % ora fatValue+1 | ||
+ | % sta entryData1 | ||
+ | % jmp setFatExit | ||
+ | % | ||
+ | % ;** case 2: second 12-bit cluster | ||
+ | % + ldx #4 | ||
+ | % - asl fatValue | ||
+ | % rol fatValue+1 | ||
+ | % dex | ||
+ | % bne - | ||
+ | % lda fatValue+1 | ||
+ | % sta entryData2 | ||
+ | % lda entryData1 | ||
+ | % and #$0f | ||
+ | % ora fatValue | ||
+ | % sta entryData1 | ||
+ | % | ||
+ | % setFatExit = * | ||
+ | % ldy #2 | ||
+ | % - lda entryData0, | ||
+ | % sta (entryAddr), | ||
+ | % dey | ||
+ | % bpl - | ||
+ | % sty fatDirty | ||
+ | % rts | ||
+ | % | ||
+ | |||
+ | Mark the directory sector corresponding to the given directory entry as being | ||
+ | dirty so it will be written out to disk the next time the msFlush routine is | ||
+ | called. | ||
+ | |||
+ | % dirtyDirent = * ;( writeDirent ) | ||
+ | % sec | ||
+ | % lda writeDirent | ||
+ | % sbc #<dirbuf | ||
+ | % lda writeDirent+1 | ||
+ | % sbc #>dirbuf | ||
+ | % lsr | ||
+ | % and #$07 | ||
+ | % tax | ||
+ | % lda #$ff | ||
+ | % sta dirDirty,x | ||
+ | % rts | ||
+ | % | ||
+ | % delCluster = 14 | ||
+ | % | ||
+ | |||
+ | Delete the MS-DOS file whose directory entry is given. | ||
+ | its filename, get its starting cluster and follow the chain of clusters | ||
+ | allocated to the file in the FAT, marking them as unallocated (value $000) | ||
+ | as we go. Exit by marking the directory entry as " | ||
+ | |||
+ | % msDelete = * ;( writeDirent ) | ||
+ | % ldy #$0e | ||
+ | % sty $ff00 | ||
+ | % lda writeDirent | ||
+ | % ldy writeDirent+1 | ||
+ | % sta 2 | ||
+ | % sty 3 | ||
+ | % lda #$e5 | ||
+ | % ldy #0 | ||
+ | % sta (2),y | ||
+ | % ldy #26 | ||
+ | % lda (2),y | ||
+ | % sta delCluster | ||
+ | % iny | ||
+ | % lda (2),y | ||
+ | % sta delCluster+1 | ||
+ | % - lda delCluster+1 | ||
+ | % cmp #5 | ||
+ | % bcc + | ||
+ | % jmp dirtyDirent | ||
+ | % + tay | ||
+ | % lda delCluster | ||
+ | % jsr getFatEntry | ||
+ | % pha | ||
+ | % tya | ||
+ | % pha | ||
+ | % lda #0 | ||
+ | % sta fatValue | ||
+ | % sta fatValue+1 | ||
+ | % lda delCluster | ||
+ | % ldy delCluster+1 | ||
+ | % jsr setFatEntry | ||
+ | % pla | ||
+ | % sta delCluster+1 | ||
+ | % pla | ||
+ | % sta delCluster | ||
+ | % jmp - | ||
+ | % | ||
+ | % flushBlock = 14 | ||
+ | % flushCountdown = $60 | ||
+ | % flushRepeats = $61 | ||
+ | % flushDirIndex = $61 | ||
+ | % | ||
+ | |||
+ | Write the FAT and directory sectors from memory to disk, if they are dirty. | ||
+ | |||
+ | % msFlush = * ;( msDevice, msType ) : .CS=error | ||
+ | % lda #$0e | ||
+ | % sta $ff00 | ||
+ | % lda fatDirty | ||
+ | % beq flushDirectory | ||
+ | % lda #0 | ||
+ | % sta fatDirty | ||
+ | % | ||
+ | % ;** flush fat | ||
+ | |||
+ | Flush both copies of the FAT, if there are two; otherwise, only flush the one. | ||
+ | |||
+ | % lda #2 | ||
+ | % sta flushRepeats | ||
+ | % lda #1 | ||
+ | % sta flushBlock | ||
+ | % | ||
+ | % masterFlush = * | ||
+ | % lda fatBlocks | ||
+ | % sta flushCountdown | ||
+ | % lda #<fatbuf | ||
+ | % ldy #>fatbuf | ||
+ | % sta bufptr | ||
+ | % sty bufptr+1 | ||
+ | % - lda flushBlock | ||
+ | % ldy #0 | ||
+ | % jsr writeLogicalBlock | ||
+ | % bcc + | ||
+ | % rts | ||
+ | % + inc flushBlock | ||
+ | % dec flushCountdown | ||
+ | % bne - | ||
+ | % dec flushRepeats | ||
+ | % bne masterFlush | ||
+ | % | ||
+ | % ;** flush directory | ||
+ | % flushDirectory = * | ||
+ | % lda firstRootDirBlock | ||
+ | % sta flushBlock | ||
+ | % lda rootDirBlocks | ||
+ | % sta flushCountdown | ||
+ | % lda #0 | ||
+ | % sta flushDirIndex | ||
+ | % lda #<dirbuf | ||
+ | % ldy #>dirbuf | ||
+ | % sta bufptr | ||
+ | % sty bufptr+1 | ||
+ | % - ldx flushDirIndex | ||
+ | % lda dirDirty,x | ||
+ | % beq + | ||
+ | % lda #0 | ||
+ | % sta dirDirty,x | ||
+ | % lda flushBlock | ||
+ | % ldy #0 | ||
+ | % jsr writeLogicalBlock | ||
+ | % dec bufptr+1 | ||
+ | % dec bufptr+1 | ||
+ | % + inc flushBlock | ||
+ | % inc flushDirIndex | ||
+ | % inc bufptr+1 | ||
+ | % inc bufptr+1 | ||
+ | % dec flushCountdown | ||
+ | % bne - | ||
+ | % clc | ||
+ | % rts | ||
+ | % | ||
+ | % bfFatEntry = 14 | ||
+ | % bfBlocks = $60 | ||
+ | % | ||
+ | |||
+ | Count the number of free FAT entries (value $000) from entry 2 up to the | ||
+ | highest FAT entry available for cluster allocation. | ||
+ | by the number of bytes per cluster (either 512 or 1024). | ||
+ | |||
+ | % msBytesFree = * ;( ) : .AYX=fileBytesFree | ||
+ | % ldy #$0e | ||
+ | % sty $ff00 | ||
+ | % lda #2 | ||
+ | % ldy #0 | ||
+ | % sta bfFatEntry | ||
+ | % sty bfFatEntry+1 | ||
+ | % sty bfBlocks | ||
+ | % sty bfBlocks+1 | ||
+ | % - lda bfFatEntry | ||
+ | % ldy bfFatEntry+1 | ||
+ | % jsr getFatEntry | ||
+ | % sty 2 | ||
+ | % ora 2 | ||
+ | % bne + | ||
+ | % inc bfBlocks | ||
+ | % bne + | ||
+ | % inc bfBlocks+1 | ||
+ | % + inc bfFatEntry | ||
+ | % bne + | ||
+ | % inc bfFatEntry+1 | ||
+ | % + lda bfFatEntry | ||
+ | % cmp lastFatEntry | ||
+ | % lda bfFatEntry+1 | ||
+ | % sbc lastFatEntry+1 | ||
+ | % bcc - | ||
+ | % ldx clusterBlockCount | ||
+ | % - asl bfBlocks | ||
+ | % rol bfBlocks+1 | ||
+ | % dex | ||
+ | % bne - | ||
+ | % lda #0 | ||
+ | % ldy bfBlocks | ||
+ | % ldx bfBlocks+1 | ||
+ | % rts | ||
+ | % | ||
+ | |||
+ | This is the file copying level. | ||
+ | MS-DOS files and copying the data they contain to/from the already-open CBM | ||
+ | Kernal file, possibly with ASCII/ | ||
+ | |||
+ | % ;====file copy level==== | ||
+ | % | ||
+ | % transMode = 14 | ||
+ | % lfn = 15 | ||
+ | % cbmDataPtr = $60 | ||
+ | % cbmDataLen = $62 | ||
+ | % cluster = $64 | ||
+ | % | ||
+ | |||
+ | Copy the given cluster to the CBM output file. This routine fetches the next | ||
+ | cluster of the file for the next time this routine is called, and if it hits | ||
+ | the NULL pointer of the last cluster of a file, it adjusts the number of valid | ||
+ | file data bytes the current cluster contains to FileLength % ClusterLength | ||
+ | (see note below). | ||
+ | |||
+ | % copyFileCluster = * ;( cluster, lfn, transMode ) : .CS=err | ||
+ | |||
+ | Read the cluster and setup to copy the whole cluster to the CBM file. | ||
+ | |||
+ | % lda cluster | ||
+ | % ldy cluster+1 | ||
+ | % jsr readCluster | ||
+ | % bcc + | ||
+ | % rts | ||
+ | % + lda #< | ||
+ | % ldy #> | ||
+ | % sta cbmDataPtr | ||
+ | % sty cbmDataPtr+1 | ||
+ | % lda #0 | ||
+ | % sta cbmDataLen | ||
+ | % lda clusterBlockCount | ||
+ | % asl | ||
+ | % sta cbmDataLen+1 | ||
+ | % | ||
+ | |||
+ | Fetch the next cluster number of the file, and adjust the cluster data length | ||
+ | for the last cluster of the file. | ||
+ | |||
+ | % ;**get next cluster | ||
+ | % lda cluster | ||
+ | % ldy cluster+1 | ||
+ | % jsr getFatEntry | ||
+ | % sta cluster | ||
+ | % sty cluster+1 | ||
+ | % cpy #$05 | ||
+ | % bcc copyFileClusterData | ||
+ | % lda lenML | ||
+ | % sta cbmDataLen | ||
+ | % lda #$01 | ||
+ | % ldx clusterBlockCount | ||
+ | % cpx #1 | ||
+ | % beq + | ||
+ | % lda #$03 | ||
+ | % + and lenML+1 | ||
+ | |||
+ | The following three lines were added in a last minute panic after realizing | ||
+ | that if FileLength % ClusterSize == 0, then the last cluster of the file | ||
+ | contains ClusterSize bytes, not zero bytes. | ||
+ | |||
+ | % bne + | ||
+ | % ldx lenML | ||
+ | % beq copyFileClusterData | ||
+ | % + sta cbmDataLen+1 | ||
+ | % | ||
+ | % copyFileClusterData = * | ||
+ | % jsr commieOut | ||
+ | % rts | ||
+ | % | ||
+ | |||
+ | Copy the file data in the MS-DOS cluster buffer to the CBM output file. | ||
+ | |||
+ | % cbmDataLimit = $66 | ||
+ | % | ||
+ | % commieOut = * ;( cbmDataPtr, cbmDataLen ) : .CS=err | ||
+ | |||
+ | If the the logical file number to copy to is 0 ("null device" | ||
+ | bother copying anything. | ||
+ | |||
+ | % ldx lfn | ||
+ | % bne + | ||
+ | % clc | ||
+ | % rts | ||
+ | |||
+ | Otherwise, prepare the logical file number for output. | ||
+ | |||
+ | % + jsr kernelChkout | ||
+ | % bcc commieOutMore | ||
+ | % sta errno | ||
+ | % rts | ||
+ | % | ||
+ | |||
+ | Process the cluster data in chunks of up to 255 bytes or the number of data | ||
+ | bytes remaining in the cluster. | ||
+ | |||
+ | % commieOutMore = * | ||
+ | % lda #255 | ||
+ | % ldx cbmDataLen+1 | ||
+ | % bne + | ||
+ | % lda cbmDataLen | ||
+ | % + sta cbmDataLimit | ||
+ | % ldy #0 | ||
+ | % - lda (cbmDataPtr), | ||
+ | % bit transMode | ||
+ | % bpl + | ||
+ | |||
+ | If we have to translate the current ASCII character, look up the PETSCII value | ||
+ | in the translation table and output that value. | ||
+ | entry value is $00, then don't output a character (filter out invalid | ||
+ | character codes). | ||
+ | |||
+ | % tax | ||
+ | % lda transBuf,x | ||
+ | % beq commieNext | ||
+ | % + jsr kernelChrout | ||
+ | % commieNext = * | ||
+ | % iny | ||
+ | % cpy cbmDataLimit | ||
+ | % bne - | ||
+ | % | ||
+ | |||
+ | Increment the cluster buffer pointer and decrement the cluster buffer character | ||
+ | count according to the number of bytes just processed, and repeat the above if | ||
+ | more file data remains in the current cluster. | ||
+ | |||
+ | % clc | ||
+ | % lda cbmDataPtr | ||
+ | % adc cbmDataLimit | ||
+ | % sta cbmDataPtr | ||
+ | % bcc + | ||
+ | % inc cbmDataPtr+1 | ||
+ | % + sec | ||
+ | % lda cbmDataLen | ||
+ | % sbc cbmDataLimit | ||
+ | % sta cbmDataLen | ||
+ | % bcs + | ||
+ | % dec cbmDataLen+1 | ||
+ | % + lda cbmDataLen | ||
+ | % ora cbmDataLen+1 | ||
+ | % bne commieOutMore | ||
+ | |||
+ | If we are finished with the cluster, then clear the CBM Kernal output channel. | ||
+ | |||
+ | % jsr kernelClrchn | ||
+ | % clc | ||
+ | % rts | ||
+ | % | ||
+ | |||
+ | The file copying main routine. | ||
+ | the cluster copying routine until end-of-file is reached. | ||
+ | NULL cluster pointer in the directory entry to handle zero-length files. | ||
+ | |||
+ | % msRead = * ;( cluster, lenML, .A=transMode, | ||
+ | % ldy #$0e | ||
+ | % sty $ff00 | ||
+ | % sta transMode | ||
+ | % stx lfn | ||
+ | % lda startCluster | ||
+ | % ldy startCluster+1 | ||
+ | % sta cluster | ||
+ | % sty cluster+1 | ||
+ | % jmp + | ||
+ | % - jsr copyFileCluster | ||
+ | % bcc + | ||
+ | % rts | ||
+ | % + lda cluster+1 | ||
+ | % cmp #$05 | ||
+ | % bcc - | ||
+ | % clc | ||
+ | % rts | ||
+ | % | ||
+ | % inLfn = $50 | ||
+ | % generateLf = $51 | ||
+ | % cbmDataMax = $52 | ||
+ | % reachedEof = $54 | ||
+ | % prevSt = $55 | ||
+ | % | ||
+ | |||
+ | Set the translation and input logical file number and set up for reading | ||
+ | from a CBM-Kernal input file. | ||
+ | |||
+ | % commieInInit = * ;( .A=transMode, | ||
+ | % sta transMode | ||
+ | % stx inLfn | ||
+ | % lda #0 | ||
+ | % sta generateLf | ||
+ | % sta reachedEof | ||
+ | % sta prevSt | ||
+ | % rts | ||
+ | % | ||
+ | |||
+ | Read up to " | ||
+ | CBM logical file number. | ||
+ | " | ||
+ | flag will be set. Regular error return. | ||
+ | |||
+ | % commieIn = * ;( cbmDataPtr++, | ||
+ | |||
+ | Establish input file, or return immediately if already past eof. | ||
+ | |||
+ | % lda #0 | ||
+ | % sta cbmDataLen | ||
+ | % sta cbmDataLen+1 | ||
+ | % ldx reachedEof | ||
+ | % beq + | ||
+ | % lda #0 | ||
+ | % clc | ||
+ | % rts | ||
+ | % + ldx inLfn | ||
+ | % jsr kernelChkin | ||
+ | % bcc commieInMore | ||
+ | % sta errno | ||
+ | % rts | ||
+ | % | ||
+ | |||
+ | Read next chunk of up to 255 bytes into input buffer. | ||
+ | |||
+ | % commieInMore = * | ||
+ | % lda #255 | ||
+ | % ldx cbmDataMax+1 | ||
+ | % bne + | ||
+ | % lda cbmDataMax | ||
+ | % + sta cbmDataLimit | ||
+ | % ldy #0 | ||
+ | % - jsr commieInByte | ||
+ | % bcc + | ||
+ | % rts | ||
+ | % + beq + | ||
+ | % sta (cbmDataPtr), | ||
+ | % iny | ||
+ | % cpy cbmDataLimit | ||
+ | % bne - | ||
+ | % | ||
+ | |||
+ | Prepare to read another chunk, or exit. | ||
+ | |||
+ | % + sty cbmDataLimit | ||
+ | % clc | ||
+ | % lda cbmDataPtr | ||
+ | % adc cbmDataLimit | ||
+ | % sta cbmDataPtr | ||
+ | % bcc + | ||
+ | % inc cbmDataPtr+1 | ||
+ | % + clc | ||
+ | % lda cbmDataLen | ||
+ | % adc cbmDataLimit | ||
+ | % sta cbmDataLen | ||
+ | % bcc + | ||
+ | % inc cbmDataLen+1 | ||
+ | % + sec | ||
+ | % lda cbmDataMax | ||
+ | % sbc cbmDataLimit | ||
+ | % sta cbmDataMax | ||
+ | % bcs + | ||
+ | % dec cbmDataMax+1 | ||
+ | % + lda reachedEof | ||
+ | % bne + | ||
+ | % lda cbmDataMax | ||
+ | % ora cbmDataMax+1 | ||
+ | % bne commieInMore | ||
+ | |||
+ | Shut down reading and exit. | ||
+ | |||
+ | % + jsr kernelClrchn | ||
+ | % lda cbmDataLen | ||
+ | % ora cbmDataLen+1 | ||
+ | % clc | ||
+ | % rts | ||
+ | % | ||
+ | |||
+ | Read a single byte from the CBM-Kernal input logical file number. | ||
+ | character into ASCII and expand CR into CR+LF if necessary. | ||
+ | previous character returned was last from disk input channel. | ||
+ | |||
+ | % commieInByte = * ;( ) : .A=char, .CS=err, .Z=eof, reachedEof | ||
+ | % ;** check for already past eof | ||
+ | % lda reachedEof | ||
+ | % beq + | ||
+ | % brk | ||
+ | % ;** check for generated linefeed | ||
+ | % + lda generateLf | ||
+ | % beq + | ||
+ | % lda #0 | ||
+ | % sta generateLf | ||
+ | % lda #$0a | ||
+ | % clc | ||
+ | % rts | ||
+ | % ;** check for eof | ||
+ | % + lda prevSt | ||
+ | % and #$40 | ||
+ | % beq + | ||
+ | % lda #$ff | ||
+ | % sta reachedEof | ||
+ | % lda #0 | ||
+ | % clc | ||
+ | % rts | ||
+ | % ;** read actual character | ||
+ | % + jsr kernelChrin | ||
+ | % ldx st | ||
+ | % stx prevSt | ||
+ | % bcc + | ||
+ | % sta errno | ||
+ | % jsr kernelClrchn | ||
+ | % rts | ||
+ | % ;** translate if necessary | ||
+ | % + bit transMode | ||
+ | % bpl + | ||
+ | % tax | ||
+ | % lda transBufToAscii, | ||
+ | % beq commieInByte | ||
+ | |||
+ | Note here that the translated character is checked to see if it is a carriage | ||
+ | return, rather than checking the non-translated character, to see if a | ||
+ | linefeed must be generated next. Thus, you could define that a Commodore | ||
+ | carriage return be translated into a linefeed (for Unix) and no additional | ||
+ | unwanted linefeed would be generated. | ||
+ | |||
+ | % cmp #$0d | ||
+ | % bne + | ||
+ | % sta generateLf | ||
+ | % ;** exit | ||
+ | % + ldx #$ff | ||
+ | % clc | ||
+ | % rts | ||
+ | % | ||
+ | % firstFreeFatEntry = $5a | ||
+ | % | ||
+ | |||
+ | Search FAT for a free cluster, and return the cluster (FAT entry) number. | ||
+ | global variable " | ||
+ | FAT entry that could possibly be free, to avoid wasting time searching from | ||
+ | the very beginning of the FAT every time. Clusters are allocated in | ||
+ | first-free order. | ||
+ | |||
+ | % allocateFatEntry = * ;( ) : .AY=fatEntry, | ||
+ | % - lda firstFreeFatEntry | ||
+ | % cmp lastFatEntry | ||
+ | % lda firstFreeFatEntry+1 | ||
+ | % sbc lastFatEntry+1 | ||
+ | % bcc + | ||
+ | % rts | ||
+ | % + lda firstFreeFatEntry | ||
+ | % ldy firstFreeFatEntry+1 | ||
+ | % jsr getFatEntry | ||
+ | % sty 2 | ||
+ | % ora 2 | ||
+ | % bne + | ||
+ | % lda firstFreeFatEntry | ||
+ | % ldy firstFreeFatEntry+1 | ||
+ | % clc | ||
+ | % rts | ||
+ | % + inc firstFreeFatEntry | ||
+ | % bne - | ||
+ | % inc firstFreeFatEntry+1 | ||
+ | % jmp - | ||
+ | % | ||
+ | % msFileLength = $5c ;(3 bytes) | ||
+ | % | ||
+ | |||
+ | Allocate a new cluster to a file, link it into the file cluster chain, and | ||
+ | write the cluster buffer to disk in that cluster, adding " | ||
+ | to the file. | ||
+ | |||
+ | % msWriteCluster = * ; (*) : .CS=err | ||
+ | % ;** get a new cluster | ||
+ | % jsr allocateFatEntry | ||
+ | % bcc + | ||
+ | % rts | ||
+ | % ;** make previous fat entry point to new cluster | ||
+ | % + sta fatValue | ||
+ | % sty fatValue+1 | ||
+ | % lda cluster | ||
+ | % ora cluster+1 | ||
+ | % beq + | ||
+ | % lda cluster | ||
+ | % ldy cluster+1 | ||
+ | % ldx fatValue | ||
+ | % stx cluster | ||
+ | % ldx fatValue+1 | ||
+ | % stx cluster+1 | ||
+ | % jsr setFatEntry | ||
+ | % jmp msClusterNew | ||
+ | |||
+ | Handle case of no previous cluster - make directory entry point to new | ||
+ | cluster. | ||
+ | |||
+ | % + lda writeDirent | ||
+ | % ldy writeDirent+1 | ||
+ | % sta 2 | ||
+ | % sty 3 | ||
+ | % ldy #26 | ||
+ | % lda fatValue | ||
+ | % sta (2),y | ||
+ | % sta cluster | ||
+ | % iny | ||
+ | % lda fatValue+1 | ||
+ | % sta (2),y | ||
+ | % sta cluster+1 | ||
+ | % | ||
+ | % ;** make new fat entry point to null | ||
+ | % msClusterNew = * | ||
+ | % lda #$ff | ||
+ | % ldy #$0f | ||
+ | % sta fatValue | ||
+ | % sty fatValue+1 | ||
+ | % lda cluster | ||
+ | % ldy cluster+1 | ||
+ | % jsr setFatEntry | ||
+ | % ;** write new cluster data | ||
+ | % + lda cluster | ||
+ | % ldy cluster+1 | ||
+ | % jsr writeCluster | ||
+ | % bcc + | ||
+ | % rts | ||
+ | % ;** add cluster length to file length | ||
+ | % + clc | ||
+ | % lda msFileLength | ||
+ | % adc cbmDataLen | ||
+ | % sta msFileLength | ||
+ | % lda msFileLength+1 | ||
+ | % adc cbmDataLen+1 | ||
+ | % sta msFileLength+1 | ||
+ | % bcc + | ||
+ | % inc msFileLength+2 | ||
+ | % + clc | ||
+ | % rts | ||
+ | % | ||
+ | |||
+ | Copy a CBM-Kernal file to an MS-DOS file, possibly with translation. | ||
+ | |||
+ | % msWrite = * ;( msDevice, msType, writeDirent, | ||
+ | % ldy #$0e | ||
+ | % sty $ff00 | ||
+ | % ;** initialize | ||
+ | |||
+ | Set input file translation and logical file number, init cluster, file length, | ||
+ | FAT allocation first free pointer (to cluster #2, the first data cluster). | ||
+ | |||
+ | % jsr commieInInit | ||
+ | % lda #0 | ||
+ | % sta cluster | ||
+ | % sta cluster+1 | ||
+ | % sta firstFreeFatEntry+1 | ||
+ | % sta msFileLength | ||
+ | % sta msFileLength+1 | ||
+ | % sta msFileLength+2 | ||
+ | % lda #2 | ||
+ | % sta firstFreeFatEntry | ||
+ | % | ||
+ | % ;** copy cluster from cbm file | ||
+ | % - lda #< | ||
+ | % ldy #> | ||
+ | % sta cbmDataPtr | ||
+ | % sty cbmDataPtr+1 | ||
+ | % lda clusterBlockCount | ||
+ | % asl | ||
+ | % tay | ||
+ | % lda #0 | ||
+ | % sta cbmDataMax | ||
+ | % sty cbmDataMax+1 | ||
+ | % jsr commieIn | ||
+ | % bcc + | ||
+ | % rts | ||
+ | % + beq + | ||
+ | % jsr msWriteCluster | ||
+ | % bcc - | ||
+ | % rts | ||
+ | % | ||
+ | % ;** wrap up after writing - set file length, dirty flag, exit. | ||
+ | % + lda writeDirent | ||
+ | % ldy writeDirent+1 | ||
+ | % sta 2 | ||
+ | % sty 3 | ||
+ | % ldx #0 | ||
+ | % ldy #28 | ||
+ | % - lda msFileLength, | ||
+ | % sta (2),y | ||
+ | % iny | ||
+ | % inx | ||
+ | % cpx #3 | ||
+ | % bcc - | ||
+ | % jsr dirtyDirent | ||
+ | % clc | ||
+ | % rts | ||
+ | % | ||
+ | |||
+ | This level deals exclusively with Commodore files. | ||
+ | |||
+ | % ;===== commodore file level ===== | ||
+ | % | ||
+ | |||
+ | Copy from an input disk logical file number to an output lfn, in up to 1024 | ||
+ | byte chunks. | ||
+ | " | ||
+ | used for both commieIn and commieOut. | ||
+ | |||
+ | % cbmCopy = * ;( .A=inLfn, .X=outLfn ) | ||
+ | % ldy #$0e | ||
+ | % sty $ff00 | ||
+ | % stx lfn | ||
+ | % tax | ||
+ | % lda #0 | ||
+ | % jsr commieInInit | ||
+ | % - lda #< | ||
+ | % ldy #> | ||
+ | % sta cbmDataPtr | ||
+ | % sty cbmDataPtr+1 | ||
+ | % lda #<1024 | ||
+ | % ldy #>1024 | ||
+ | % sta cbmDataMax | ||
+ | % sty cbmDataMax+1 | ||
+ | % jsr commieIn | ||
+ | % bcs + | ||
+ | % beq + | ||
+ | % lda #< | ||
+ | % ldy #> | ||
+ | % sta cbmDataPtr | ||
+ | % sty cbmDataPtr+1 | ||
+ | % jsr commieOut | ||
+ | % bcs + | ||
+ | % jmp - | ||
+ | % + rts | ||
+ | % | ||
+ | |||
+ | Read a single directory entry from the given logical file number, which is | ||
+ | assumed to be open for reading a directory (" | ||
+ | entry are returned in the interface variables. | ||
+ | |||
+ | % cbmDirent = * ;( .A=lfn ) | ||
+ | |||
+ | Initialize. | ||
+ | |||
+ | % ldy #$0e | ||
+ | % sty $ff00 | ||
+ | % tax | ||
+ | % jsr kernelChkin | ||
+ | % bcc + | ||
+ | % cdirErr = * | ||
+ | % lda #0 | ||
+ | % sta cdirFlen | ||
+ | % sta cdirBlocks | ||
+ | % sta cdirBlocks+1 | ||
+ | % rts | ||
+ | % ;** get block count | ||
+ | % + jsr cdirGetch | ||
+ | % jsr cdirGetch | ||
+ | % jsr cdirGetch | ||
+ | % sta cdirBlocks | ||
+ | % jsr cdirGetch | ||
+ | % sta cdirBlocks+1 | ||
+ | % ;** look for filename | ||
+ | % lda #0 | ||
+ | % sta cdirFlen | ||
+ | % - jsr cdirGetch | ||
+ | % cmp #34 | ||
+ | % beq + | ||
+ | % cmp #" | ||
+ | % bne - | ||
+ | % jsr kernelClrchn | ||
+ | % rts | ||
+ | % ;** get filename | ||
+ | % + ldy #0 | ||
+ | % - jsr cdirGetch | ||
+ | % cmp #34 | ||
+ | % beq + | ||
+ | % sta cdirName,y | ||
+ | % iny | ||
+ | % bne - | ||
+ | % + sty cdirFlen | ||
+ | |||
+ | Look for and get file type. | ||
+ | |||
+ | % - jsr cdirGetch | ||
+ | % cmp #" " | ||
+ | % beq - | ||
+ | % sta cdirType | ||
+ | |||
+ | Scan for end of directory entry, return. | ||
+ | |||
+ | % - jsr cdirGetch | ||
+ | % cmp #0 | ||
+ | % bne - | ||
+ | % jsr kernelClrchn | ||
+ | % rts | ||
+ | % | ||
+ | |||
+ | Get a single character of the directory entry, watching for end of file (which | ||
+ | would indicate error here). | ||
+ | |||
+ | % cdirGetch = * | ||
+ | % jsr kernelChrin | ||
+ | % bcs + | ||
+ | % bit st | ||
+ | % bvs + | ||
+ | % rts | ||
+ | % + pla | ||
+ | % pla | ||
+ | % jsr kernelClrchn | ||
+ | % jmp cdirErr | ||
+ | % | ||
+ | % ;===== data ===== | ||
+ | % | ||
+ | |||
+ | This is the translation table used to convert from ASCII to PETSCII. | ||
+ | modify it to suit your needs if you wish. If you cannot reassemble this file, | ||
+ | then you can sift through the binary file and locate the table and change it | ||
+ | there. | ||
+ | translated. | ||
+ | ASCII control characters into PETSCII: Backspace, Tab, Linefeed (CR), and | ||
+ | Formfeed. | ||
+ | according to what they probably would have been if Commodore wasn't so | ||
+ | concerned with the graphics characters. | ||
+ | |||
+ | % transBuf = * | ||
+ | % ;0 | ||
+ | % .byte $00, | ||
+ | % .byte $00, | ||
+ | % .byte $20, | ||
+ | % .byte $30, | ||
+ | % .byte $40, | ||
+ | % .byte $d0, | ||
+ | % .byte $c0, | ||
+ | % .byte $50, | ||
+ | % .byte $00, | ||
+ | % .byte $00, | ||
+ | % .byte $00, | ||
+ | % .byte $00, | ||
+ | % .byte $00, | ||
+ | % .byte $00, | ||
+ | % .byte $00, | ||
+ | % .byte $00, | ||
+ | % | ||
+ | |||
+ | This is the translation table used to convert from PETSCII to ASCII. | ||
+ | modify it to suit your needs, similar to the ASCII to PETSCII table. | ||
+ | of $00 means the corresponding PETSCII character will not be translated. | ||
+ | You'll notice that I have set up translations for the following PETSCII | ||
+ | control characters into ASCII: Delete (into Backspace), Tab, Carriage Return | ||
+ | (into CR+LF), and ClearScreen (into Fordfeed). | ||
+ | the ASCII characters {, }, ^, _, ~, \, and | are also set up. | ||
+ | |||
+ | % transBufToAscii = * | ||
+ | % ;0 | ||
+ | % .byte $00, | ||
+ | % .byte $00, | ||
+ | % .byte $20, | ||
+ | % .byte $30, | ||
+ | % .byte $40, | ||
+ | % .byte $70, | ||
+ | % .byte $00, | ||
+ | % .byte $00, | ||
+ | % .byte $00, | ||
+ | % .byte $00, | ||
+ | % .byte $00, | ||
+ | % .byte $00, | ||
+ | % .byte $60, | ||
+ | % .byte $50, | ||
+ | % .byte $00, | ||
+ | % .byte $00, | ||
+ | % | ||
+ | % ;====bss storage (size=11, | ||
+ | % | ||
+ | |||
+ | This is where the track cache, etc. are stored. | ||
+ | storage space but does not increase the length of the binary program file | ||
+ | since these storage areas are DEFINED rather than allocated with " | ||
+ | directives. | ||
+ | " | ||
+ | |||
+ | % bss = * | ||
+ | % trackbuf | ||
+ | % clusterBuf = trackbuf+4608 | ||
+ | % fatbuf | ||
+ | % dirbuf | ||
+ | % end = dirbuf+4096 | ||
+ | |||
+ | 5. USER-INTERFACE PROGRAM | ||
+ | |||
+ | This section presents the listing of the user-interface BASIC program. | ||
+ | should be aware that you can easily change some of the defaults to your own | ||
+ | preferences if you wish. In particular, you may wish to change the " | ||
+ | " | ||
+ | format that the assembler listing is since you can recover this listing from | ||
+ | the uuencoded binary program file. The listing is here in its entirety. | ||
+ | |||
+ | 10 print chr$(147);" | ||
+ | 11 print : print" | ||
+ | 12 : | ||
+ | 20 cd=peek(186): | ||
+ | 25 dv=9:dt=0 : rem ** ms-dos drive, type (0=1571, | ||
+ | 26 if dv=cd then dv=8:dt=0 : rem ** alternate ms-dos drive | ||
+ | 27 : | ||
+ | 30 print " | ||
+ | 40 bank0 : pk=dec(" | ||
+ | 50 if peek(pv+0)=dec(" | ||
+ | 55 print" | ||
+ | 60 poke pv+3,dv : poke pv+4,dt : sys pk | ||
+ | 70 dim t, | ||
+ | 71 cm$=" | ||
+ | 72 cm$=cm$+chr$(147)+"/ | ||
+ | 75 dl=-1 : cf=-1 : me=0 : ca=0 : ma=0 | ||
+ | 80 dim di$(1, | ||
+ | 90 if dt=255 then dt$=" | ||
+ | 100 fl$=chr$(19)+chr$(17)+chr$(17)+chr$(17)+chr$(17) | ||
+ | 110 il$=fl$: | ||
+ | 120 goto 500 | ||
+ | 130 : | ||
+ | 131 rem ** load ms-dos directory ** | ||
+ | 140 print" | ||
+ | 150 sys pk : sys pk+3 | ||
+ | 160 dl=0 | ||
+ | 170 rreg bl,dc,bh,s : e=peek(pv+2) | ||
+ | 180 if (s and 1) then gosub 380 : dl=-1 : return | ||
+ | 190 print" | ||
+ | 200 db=bl+256*bh | ||
+ | 205 sys pk+21 : rreg bl,x,bh : ma=bl+bh*256+x*65536 | ||
+ | 210 if dc=0 then 360 | ||
+ | 220 for dp=db to db+32*(dc-1) step 32 | ||
+ | 230 if peek(dp)=0 or peek(dp)=229 then 350 | ||
+ | 240 if peek(dp+11) and 24 then 350 | ||
+ | 250 dl=dl+1 | ||
+ | |||
+ | Line 260 sets the default selection, translation, | ||
+ | files. | ||
+ | |||
+ | 260 d$=right$(" | ||
+ | 270 a$="" | ||
+ | 280 a$=left$(a$, | ||
+ | 290 print dl; a$ | ||
+ | 300 d$=d$+a$+" | ||
+ | 310 cl(dl)=peek(dp+26)+256*peek(dp+27) | ||
+ | 320 sz=peek(dp+28)+256*peek(dp+29)+65536*peek(dp+30) | ||
+ | 330 di$(0, | ||
+ | 335 dp(dl)=dp | ||
+ | 340 sz(dl)=sz | ||
+ | 350 next dp | ||
+ | 360 return | ||
+ | 370 : | ||
+ | 371 rem ** report ms-dos disk error ** | ||
+ | 380 print chr$(18);" | ||
+ | 390 print " ($"; | ||
+ | 400 getkey a$ : return | ||
+ | 410 : | ||
+ | 411 rem ** screen heading ** | ||
+ | 420 print chr$(147); | ||
+ | 421 if me=0 then print" | ||
+ | 422 print chr$(146);" | ||
+ | 430 print" | ||
+ | 440 print : return | ||
+ | 450 : | ||
+ | 451 rem ** screen footing ** | ||
+ | 460 print il$;" | ||
+ | 470 print " | ||
+ | 480 return | ||
+ | 490 : | ||
+ | 491 rem ** main routine ** | ||
+ | 500 t=1 : c=0 | ||
+ | 501 r=0 | ||
+ | 510 if me=0 then mf=dl:mc=2 : else mf=cf:mc=1 | ||
+ | 520 gosub 420 | ||
+ | 521 if me<>0 then 542 | ||
+ | 530 print " | ||
+ | 540 print " | ||
+ | 541 goto 550 | ||
+ | 542 print " | ||
+ | 543 print " | ||
+ | 550 gosub 460 | ||
+ | 560 b=t+17 : if b>mf then b=mf | ||
+ | 570 print fl$;: if t>mf then 590 | ||
+ | 580 for i=t to b : print di$(me,i) : next | ||
+ | 590 if mf<0 then print chr$(18);"< | ||
+ | 591 if mf=0 then print chr$(18);"< | ||
+ | 600 if mf<=0 then 660 | ||
+ | 610 print left$(il$, | ||
+ | 620 on c+1 goto 630,640,650 | ||
+ | 630 print spc(4); | ||
+ | 640 print spc(7); | ||
+ | 650 print spc(12); | ||
+ | 660 getkey a$ | ||
+ | 670 i=instr(cm$, | ||
+ | 680 if mf>0 then print left$(il$, | ||
+ | 690 if i=0 then 600 | ||
+ | 700 on i goto 760, | ||
+ | 705 on i-15 goto 500, | ||
+ | 710 stop | ||
+ | 711 : | ||
+ | 712 rem ** various menu options ** | ||
+ | 713 me=-(me=0) | ||
+ | 714 goto500 | ||
+ | 730 print chr$(147);" | ||
+ | 740 end | ||
+ | 760 if me=1 then gosub 420 : gosub 2500 : goto 500 | ||
+ | 765 gosub 420 : gosub 140 : goto 500 | ||
+ | 770 r=r-1 : if r<0 then r=b-t | ||
+ | 780 goto 600 | ||
+ | 790 r=r+1 : if t+r>b then r=0 | ||
+ | 800 goto 600 | ||
+ | 810 c=c-1 : if c<0 then c=mc | ||
+ | 820 goto 600 | ||
+ | 830 c=c+1 : if c>mc then c=0 | ||
+ | 840 goto 600 | ||
+ | 850 r=0 : c=0 : goto 600 | ||
+ | 860 if mf<=0 then 600 | ||
+ | 870 x=t+r : on c+1 gosub 890,910,930 | ||
+ | 880 print left$(il$, | ||
+ | 890 if mid$(di$(me, | ||
+ | 900 mid$(di$(me, | ||
+ | 910 if mid$(di$(me, | ||
+ | 920 mid$(di$(me, | ||
+ | 930 if mid$(di$(me, | ||
+ | 940 mid$(di$(me, | ||
+ | 950 if mf<=0 then 600 | ||
+ | 960 for x=1 to mf | ||
+ | 970 on c+1 gosub 890,910,930 | ||
+ | 980 next x | ||
+ | 990 goto 520 | ||
+ | 1000 r=0:if b=mf then t=1 : goto 510 | ||
+ | 1010 t=t+18 : goto 510 | ||
+ | 1020 if mf<=0 then 660 | ||
+ | 1025 r=0:if t=1 then t=mf-(mf-int(mf/ | ||
+ | 1030 t=t-18 : if t<1 then t=1 | ||
+ | 1040 goto 510 | ||
+ | 1050 print il$; | ||
+ | 1060 input" | ||
+ | 1061 if cd=dv thenprint" | ||
+ | 1070 x=71 : input" | ||
+ | 1080 if x=8 or x=81 or x=1581 then dt=255: | ||
+ | 1090 poke pv+3,dv : poke pv+4,dt : sys pk : dl=-1 : ma=0 | ||
+ | 1100 goto 500 | ||
+ | 1110 print il$; | ||
+ | 1120 input " | ||
+ | 1130 if cd=dv thenprint" | ||
+ | 1140 cf=-1 : ca=0 : goto 500 | ||
+ | 1141 : | ||
+ | 1142 rem ** copy files ** | ||
+ | 1150 if me=1 then 2000 | ||
+ | 1151 print chr$(147);" | ||
+ | 1160 if dl<=0 then fc=0 : goto 1190 | ||
+ | 1170 fc=0 : for f=1 to dl : if mid$(di$(0, | ||
+ | 1180 next f | ||
+ | 1190 print : print" | ||
+ | 1191 getkey a$ : goto 520 | ||
+ | 1200 fc=fc+1 | ||
+ | 1210 x$=mid$(di$(0, | ||
+ | 1220 cf$="": | ||
+ | 1230 next | ||
+ | 1231 if right$(cf$, | ||
+ | 1232 cf$=cf$+"," | ||
+ | 1240 print str$(fc);" | ||
+ | 1245 print tab(35); | ||
+ | 1250 cl=cl(f) : lb=sz(f) - int(sz(f)/ | ||
+ | 1260 if cd>=8 then dopen# | ||
+ | 1265 if cd<8 then 1288 | ||
+ | 1270 if ds<> | ||
+ | 1275 x$=" | ||
+ | 1280 close 1 : input x$ : if x$=" | ||
+ | 1285 scratch(cf$), | ||
+ | 1286 dopen# | ||
+ | 1288 if cd<8 then 1320 | ||
+ | 1300 if ds<20 then 1320 | ||
+ | 1310 print chr$(18)+" | ||
+ | 1320 poke pv+6,cl/256 : poke pv+5, | ||
+ | 1330 poke pv+8,lb/256 : poke pv+7, | ||
+ | 1340 tr=0 : if mid$(di$(0, | ||
+ | 1346 x=1 : if cd=0 then x=0 | ||
+ | 1350 sys pk+6,tr,x | ||
+ | 1355 rreg x,x,x,s : e=peek(pv+2) | ||
+ | 1356 if (s and 1) then gosub 380 : fc=fc-1 | ||
+ | 1360 if cd<>0 and cd<8 then close1 | ||
+ | 1370 if cd>=8 then dclose#1 : if ds>=20 then 1310 | ||
+ | 1380 return | ||
+ | 1398 : | ||
+ | 1399 rem ** remove ms-dos file ** | ||
+ | 1400 print chr$(147);" | ||
+ | 1401 if me<>0 then print" | ||
+ | 1402 a$=" | ||
+ | 1403 print:if a$=" | ||
+ | 1410 if dl<=0 then fc=0 : goto 1440 | ||
+ | 1420 fc=0 : f=1 | ||
+ | 1425 if mid$(di$(0, | ||
+ | 1430 f=f+1 : if f<=dl then 1425 | ||
+ | 1434 print" | ||
+ | 1435 sys pk+12 | ||
+ | 1440 print : print" | ||
+ | 1445 sys pk+21 : rreg a,x,y : ma=a+y*256+x*65536 | ||
+ | 1450 getkey a$ : goto 500 | ||
+ | 1470 print" | ||
+ | 1490 poke pv+10, | ||
+ | 1492 sys pk+15 | ||
+ | 1494 di$(0, | ||
+ | 1495 dl=dl-1 | ||
+ | 1496 return | ||
+ | 1498 : | ||
+ | 1499 rem ** copy cbm files ** | ||
+ | 1500 print chr$(147);" | ||
+ | 1501 if cf<=0 then print" | ||
+ | 1502 x=0 : input" | ||
+ | 1503 if x<=0 or x>=64 then print" | ||
+ | 1504 if x=cd then print" | ||
+ | 1505 for f=1 to cf : if mid$(di$(1, | ||
+ | 1506 print di$(1,f) : open1, | ||
+ | 1507 if x<8 then open 2,x,7 : goto1550 | ||
+ | 1508 cf$=cn$(f)+"," | ||
+ | 1509 open2, | ||
+ | 1510 if ds<> | ||
+ | 1511 close2 | ||
+ | 1512 x$=" | ||
+ | 1520 scratch(cn$(f)), | ||
+ | 1525 open2, | ||
+ | 1530 if ds>20 then print chr$(18);" | ||
+ | 1550 sys pk+24,1,2 | ||
+ | 1560 close1 : close2 | ||
+ | 1570 next f | ||
+ | 1580 print : print" | ||
+ | 1998 : | ||
+ | 1999 rem ** copy cbm-dos to ms-dos ** | ||
+ | 2000 print chr$(147);" | ||
+ | 2010 if dl>=0 then 2035 | ||
+ | 2020 print" | ||
+ | 2030 print : print" | ||
+ | 2035 fc=0 | ||
+ | 2036 for f=1 to cf : if mid$(di$(1, | ||
+ | 2040 fc=fc+1 : c$=cn$(f) | ||
+ | 2041 printmid$(str$(fc), | ||
+ | 2042 gosub2050 : print left$(m$, | ||
+ | 2043 tr=0 : if mid$(di$(1, | ||
+ | 2044 gosub2100 | ||
+ | 2045 next | ||
+ | 2046 print" | ||
+ | 2047 sys pk+21 : rreg a,x,y : ma=a+y*256+x*65536 | ||
+ | 2048 print: print" | ||
+ | 2049 : | ||
+ | 2050 x=instr(c$," | ||
+ | 2055 x=len(c$)+1 : do : x=x-1 : loop until mid$(c$, | ||
+ | 2060 m$=left$(left$(c$, | ||
+ | 2070 x$=mid$(c$, | ||
+ | 2080 m$=m$+x$ | ||
+ | 2090 m$=left$(m$, | ||
+ | 2091 fori=1to11: | ||
+ | 2092 mid$(m$, | ||
+ | 2093 i=8 : do while i>1 and mid$(m$, | ||
+ | 2094 i=11 : do while i>8 and mid$(m$, | ||
+ | 2098 return | ||
+ | 2099 : | ||
+ | 2100 fori=0to0 | ||
+ | 2105 for dp=db to db+32*(dc-1) step 32 | ||
+ | 2110 if peek(dp)=0 or peek(dp)=229 then 2140 | ||
+ | 2120 next dp | ||
+ | 2130 print" | ||
+ | 2140 next i | ||
+ | 2160 fori=1tolen(m$): | ||
+ | 2170 fori=11to31: | ||
+ | 2180 pokedp+26, | ||
+ | 2190 poke pv+10, | ||
+ | 2200 open1, | ||
+ | 2300 sys pk+9,tr,1 : rreg x,x,x,s | ||
+ | 2301 close1 | ||
+ | 2305 if s and 1 then e=peek(pv+2) : gosub380 : return | ||
+ | |||
+ | Line 2310 sets the default MS-DOS selection, translation, | ||
+ | copying to MS-DOS disk, based on the CBM-DOS filetype. | ||
+ | |||
+ | 2310 x$=" | ||
+ | 2320 dl=dl+1 : d$=right$(" | ||
+ | 2330 d$=d$+left$(m$, | ||
+ | 2340 cl(dl)=peek(dp+26)+256*peek(dp+27) | ||
+ | 2350 sz=peek(dp+28)+256*peek(dp+29)+65536*peek(dp+30) | ||
+ | 2360 di$(0, | ||
+ | 2370 dp(dl)=dp | ||
+ | 2380 sz(dl)=sz | ||
+ | 2395 return | ||
+ | 2498 : | ||
+ | 2499 rem ** load commodore dos directory ** | ||
+ | 2500 print" | ||
+ | 2501 if cd<8 then print" | ||
+ | 2505 open1, | ||
+ | 2506 do | ||
+ | 2507 sys pk+27,1 : b=peek(pv+11)+256*peek(pv+12) : t$=chr$(peek(pv+13)) | ||
+ | 2510 x=peek(pv+14) | ||
+ | 2520 if x=0 then exit | ||
+ | 2530 x$="" | ||
+ | 2575 cf=cf+1 | ||
+ | 2590 if cf=0 then print" | ||
+ | 2600 cn$(cf)=x$ | ||
+ | 2610 a$=left$(x$+" | ||
+ | |||
+ | Lines 2620 and 2625 set the default CBM-DOS selection and translation modes | ||
+ | based on the filetype. | ||
+ | |||
+ | 2620 di$(1, | ||
+ | 2625 if t$<>" | ||
+ | 2630 print di$(1,cf) | ||
+ | 2650 loop | ||
+ | 2670 ca=b*256 : close1 : return | ||
+ | |||
+ | 6. UUENCODED FILES | ||
+ | |||
+ | Here are the binary executables in uuencoded form. The CRC32s of the two | ||
+ | files are as follows: | ||
+ | |||
+ | crc32 = 3896271974 for " | ||
+ | crc32 = 2918283051 for " | ||
+ | |||
+ | The " | ||
+ | the machine lanugage disk-accessing routines. | ||
+ | |||
+ | begin 640 lrr-128 | ||
+ | M`1PS' | ||
+ | M(# | ||
+ | M($, | ||
+ | M0T2R." | ||
+ | M.D14LC`@.B`@CR`J*B!-4RU$3U, | ||
+ | M, | ||
+ | M12!-4RU$3U, | ||
+ | M.B" | ||
+ | M*%!6JC`ILM$H(D-" | ||
+ | M3D< | ||
+ | M(BQ5*$-$*0# | ||
+ | MAB!4+%(L0BQ)+$$D+$, | ||
+ | M0RLM42`BJL< | ||
+ | M: | ||
+ | M1K*K, | ||
+ | M*# | ||
+ | M($14)+(B, | ||
+ | MJL< | ||
+ | M)*K' | ||
+ | M4R!$25)%0U1/ | ||
+ | M+BXN(B`Z()D`GA^6`)X@4$L@.B"> | ||
+ | M+$1# | ||
+ | M1$RRJS$@.B" | ||
+ | M(# | ||
+ | M2" | ||
+ | M`($@1%" | ||
+ | MPBA$4" | ||
+ | M`$1, | ||
+ | M(" | ||
+ | M2; | ||
+ | M(B`@(JK)*$$D+#, | ||
+ | M(38!0TPH1$PILL(H1%" | ||
+ | M." | ||
+ | M1" | ||
+ | MLE-: | ||
+ | M3U, | ||
+ | M3U(@(R([RBC$*$4I+# | ||
+ | M15-3($M%62XB.\< | ||
+ | M(%-# | ||
+ | M346R,"" | ||
+ | MF2#' | ||
+ | M0T)-/ | ||
+ | MC@!I(\(!.@" | ||
+ | M(D0]1$E2($T]35-$158@1CU# | ||
+ | M`9D@(" | ||
+ | M4$< | ||
+ | M`52R, | ||
+ | MU2!-1K)# | ||
+ | MF2`B3E5-(" | ||
+ | M)!P" | ||
+ | M(@# | ||
+ | M(" | ||
+ | M+2TM+2TM(" | ||
+ | M348@IR!" | ||
+ | MI" | ||
+ | M1$E214-43U)9($Y/ | ||
+ | MQR@Q." | ||
+ | M)F(" | ||
+ | M, | ||
+ | MIC< | ||
+ | M220H344L5*I2*2PQ, | ||
+ | M)" | ||
+ | M)K(" | ||
+ | M-3`L, | ||
+ | M9B? | ||
+ | M< | ||
+ | MLJLH346R," | ||
+ | M344@1$%9+B(@.B# | ||
+ | M, | ||
+ | M4JLQ(# | ||
+ | MJE*Q0B" | ||
+ | M`(4H-`.)(# | ||
+ | M`+XH4@-2LC`@.B!# | ||
+ | M6+)4JE(@.B" | ||
+ | M1$DD*$U%+%@I(# | ||
+ | MIR!8)+(B*B(@.M4@6" | ||
+ | MC@"/ | ||
+ | MLB)!4T, | ||
+ | M)" | ||
+ | MRBA$220H344L6" | ||
+ | MP`.!(%BR, | ||
+ | M1BK> | ||
+ | M5*HQ." | ||
+ | MIR!4LDU& | ||
+ | M!%2R5*LQ." | ||
+ | M-RD[(D`B.P`> | ||
+ | M`& | ||
+ | M4U0@0D4@1$E& | ||
+ | M159)0T4@5%E012`@*#< | ||
+ | M-3@Q(*< | ||
+ | M0@27(%!6JC, | ||
+ | M``DL3`2)(# | ||
+ | M4R!$159)0T4@3E5-0D52(" | ||
+ | M1$]3($%.1" | ||
+ | M, | ||
+ | M0T]062!& | ||
+ | M(D-/ | ||
+ | MLC`@.B" | ||
+ | M+$8I+# | ||
+ | M($-/ | ||
+ | M, | ||
+ | M220H," | ||
+ | M, | ||
+ | M+# | ||
+ | MJLHH1$DD*# | ||
+ | MQR@S-" | ||
+ | M*2PY+#, | ||
+ | M-BFL-C4U, | ||
+ | M.M4@BR!# | ||
+ | M($13L[$V, | ||
+ | M4SL@3U9%4E=2251%(" | ||
+ | M(B" | ||
+ | M)*HB+%< | ||
+ | M(# | ||
+ | MLD9# | ||
+ | MPBA05JHV*: | ||
+ | M5JHX*: | ||
+ | M4K(R-34`K3!" | ||
+ | MV# | ||
+ | M, | ||
+ | M1+& | ||
+ | M!8\@*BH@4D5-3U9%($U3+41/ | ||
+ | M3U9%(" | ||
+ | M346SL3`@IR" | ||
+ | M,#, | ||
+ | M(" | ||
+ | M(*< | ||
+ | M+$8I+# | ||
+ | MLD: | ||
+ | MGB!02ZHQ, | ||
+ | M4U, | ||
+ | MK# | ||
+ | M-" | ||
+ | M, | ||
+ | M, | ||
+ | M3" | ||
+ | M(" | ||
+ | M1$]3(%1/ | ||
+ | M1$E214-43U)9($Y/ | ||
+ | M5DE# | ||
+ | MLC8T(*< | ||
+ | M0T0@IR" | ||
+ | M-> | ||
+ | M`$@UX@69($1))" | ||
+ | MLS@@IR"? | ||
+ | M*# | ||
+ | MIR`Q-3, | ||
+ | M4DE412`H62].*2([6" | ||
+ | M*2Q5*%@I`!DV]06?, | ||
+ | M0T)-($1/ | ||
+ | M: | ||
+ | M4R!!($M%62(@.B" | ||
+ | M0DTM1$]3(%1/ | ||
+ | M4R!43R!-4RU$3U, | ||
+ | MF2)-4RU$3U, | ||
+ | MF2`Z()DB4%)%4U, | ||
+ | M`)LW]`> | ||
+ | M-# | ||
+ | M(CO**$1))" | ||
+ | M, | ||
+ | M220H, | ||
+ | M!YDB1DQ54TA)3D< | ||
+ | M6" | ||
+ | M1" | ||
+ | M(*< | ||
+ | M.B# | ||
+ | M+%BK, | ||
+ | M(`A-)+)-)*I8)`!U.2H(322RR" | ||
+ | M*, | ||
+ | M", | ||
+ | M22PQ*; | ||
+ | M(# | ||
+ | M2; | ||
+ | MI" | ||
+ | M(*< | ||
+ | M4ED@14Y425)%4R(@.B" | ||
+ | M, | ||
+ | M@@`W.X0(ET10JC(V+# | ||
+ | M.I< | ||
+ | M4$NJ.2Q44BPQ(# | ||
+ | MLL(H4%: | ||
+ | MBR!44K(P(*< | ||
+ | M)++)*" | ||
+ | M320L, | ||
+ | MLL(H1%" | ||
+ | M*# | ||
+ | M`, | ||
+ | M3T1/ | ||
+ | M12!$3U, | ||
+ | M($1%5DE# | ||
+ | M," | ||
+ | M2ZHR-RPQ(# | ||
+ | M, | ||
+ | M(($@2; | ||
+ | M# | ||
+ | M, | ||
+ | M(" | ||
+ | M*# | ||
+ | M5" | ||
+ | B0T8I``0_6@KL`!H_; | ||
+ | ` | ||
+ | end | ||
+ | begin 640 lrr.bin | ||
+ | M`(!,# | ||
+ | M```````````````````````````````````````````````````````````` | ||
+ | M````2*D`A9" | ||
+ | MC2" | ||
+ | MD## | ||
+ | M`6" | ||
+ | M(*C_J0D@J/ | ||
+ | ML`GF!*4$R0F0ZQA88*GVA0*F!!BIBGU5@84# | ||
+ | MR0*0`6" | ||
+ | M" | ||
+ | M+`W< | ||
+ | MT-@8($? | ||
+ | M!YU-@, | ||
+ | M&: | ||
+ | MB!# | ||
+ | M" | ||
+ | M" | ||
+ | MD`' | ||
+ | MC(%@```@XX*B]H8" | ||
+ | MD`' | ||
+ | M.&" | ||
+ | M@, | ||
+ | MC42`K4*`K$.`..U$@+`!B(U& | ||
+ | M1X!I`(U)@*GVH*" | ||
+ | MD8*0`6" | ||
+ | MI0-IH(4# | ||
+ | M"& | ||
+ | MI0< | ||
+ | MC`# | ||
+ | M2)A(J0" | ||
+ | M`84.K3^`A6" | ||
+ | M8*D`A6& | ||
+ | M8-# | ||
+ | MY@^E# | ||
+ | MA6" | ||
+ | M+2: | ||
+ | M)`X0!JJ]]HCP`R# | ||
+ | MQ"#, | ||
+ | M4*D`A5& | ||
+ | MH``@((> | ||
+ | M9H52L`+& | ||
+ | M*4# | ||
+ | M_QA@I5K-2(" | ||
+ | M8X> | ||
+ | MI0F1`H5DR*4*D0*%9: | ||
+ | MI5UE8X5=D`+F7AA@H`Z, | ||
+ | M8: | ||
+ | MZ.`# | ||
+ | M$*GVH)R%8(1A(# | ||
+ | MY(@@Y(B-*8`@Y(B-*H" | ||
+ | M@, | ||
+ | MB```````````%`D-`), | ||
+ | M+" | ||
+ | MV=I; | ||
+ | M```````````````````````````````````````````````````````````` | ||
+ | M```````````````````````````````````````````````````````````` | ||
+ | M```````````````````````````````````````````````````````)```` | ||
+ | M# | ||
+ | M.CL\/ | ||
+ | M```````````````````````````````````````````````````````````` | ||
+ | M``````````````````````````````````````````````````````````!@ | ||
+ | M04)# | ||
+ | 2``````````````````````!^```` | ||
+ | ` | ||
+ | end | ||
+ | |||
+ | 7. THE FUTURE | ||
+ | |||
+ | Future improvements to this program would include implementation of MS-DOS | ||
+ | formatting, more file manipluation commands (such as Rename), re-writing the | ||
+ | user-interface BASIC program in machine language, and making a file buffering | ||
+ | facility for those people with only one disk drive. | ||
+ | to do much more to this program. | ||
+ | into a device driver for a new operating system (or at least, operating | ||
+ | environment) for the C-128. | ||
+ | |||
+ | ============================================================================= | ||
+ | </ | ||
+ | ====== In the Next Issue: ====== | ||
+ | < | ||
+ | TWO-KEY ROLLOVER | ||
+ | |||
+ | This article will examine how a two-key rollover mechanism would work for the | ||
+ | keyboards of the C-128 and 64 and will present Kernal-wedge implementations | ||
+ | for both machines. | ||
+ | means that the machine will act sensibly if you are holding down one key and | ||
+ | then press another without releasing the first. | ||
+ | touch typers. | ||
+ | |||
+ | The Second Rob Hubbard Music Routine | ||
+ | |||
+ | In this article, the second Rob Hubbard music routine will be presented in | ||
+ | the same way as the first. Future issues will hopefully examine various other | ||
+ | music routines including various Martin Galway, Benn Daglish, Jeoren Tel, | ||
+ | and Manaics of Noise routines. Note: Unfortunately the author completes | ||
+ | university (and thus loses internet access) in August 1993. | ||
+ | |||
+ | 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 only 8 characters - one character per sprite, but a real demo | ||
+ | coder won't be satisfied with that. | ||
+ | |||
+ | Multi-Tasking on the C=128 - Part 2 | ||
+ | |||
+ | This article will examine the actual code that makes up the multi-tasking | ||
+ | kernal in detail and include some example programs explaining it use. | ||
+ | |||
+ | The 1351 Mouse Demystified | ||
+ | |||
+ | This article will explain how the 1351 mouse works as well as provide an easy | ||
+ | to use interface in machine language for both basic and machine language | ||
+ | programmers. | ||
+ | ========================================================================END=== | ||
+ | </ |
magazines/chacking5.txt · Last modified: 2015-04-17 04:34 by 127.0.0.1