In This Issue...
Have we got news for you this month!
First, the simple announcements: We now have a new S-C Macro Cross Assembler for the Zilog Z-8 microprocessor. For only $32.50 Macro Assembler owners can add the ability to develop code for this popular chip.
And some good news for you Corvus hard disk owners: The problem in the Macro Assembler with having your Target File on a different volume from your source files is now whipped. Just send in your original Version 1.1 diskette for a free update.
Now the big story: After repeated requests from many users, we have decided to make available the complete Source Code for S-C Macro Assembler Version 1.1. See the last page of this issue for details.
Another product for which we have held back selling source code is the Double Precision Floating Point package for Applesoft (DPFP). From now on that product will be sold WITH source code, at the unforgiveably low price of $50. If you already are a registered owner of DPFP, or can supply other proof-of- purchase, we will send you the source code for $15. In case you never heard of DPFP, it is a 2048-byte &-module that provides 21-digit arithmetic and I/O for Applesoft.
In the May 1983 AAL I wrote about checksums and parity, two ways to guarantee the integrity of data. In the world of microprocessors, you may encounter checksums at the end of data records on tape or disk, and parity bits on characters sent via a modem between computers. Tacking on parity bits and checksums to data helps in checking whether the data has been changed. However, there are more secure methods.
The best method I have ever heard of is commonly called Cyclic Redundancy Check, or CRC for short. Since it appears a lot more complicated than parity or checksum, it stands to reason it should have a more complex name. Right? But programmers have a way of reducing all complexity to three capital letters, so we will call it CRC.
First, a little review. The kind of parity I most frequently see adds an 8th bit on the left of a 7-bit value. The parity bit is chosen so that the total number of one-bits in the 8-bit byte is odd. For example, the seven bit number 1011010 (which might stand for an ASCII "Z") becomes 11011010, or $DA. If we run into the value 01011010 ($5A), we know there has been an error somewhere. Of course we don't know which bit is wrong, but we know at least one is because the total number of one-bits is even.
Checksums I run into are normally 8-bit or 16-bit "sums" of a large number of bytes or double bytes. I put "sums" in quotation marks because the checksum may be formed by the exclusive-or operation rather than true addition. In fact, it usually is. Eight-bit checksums formed with exclusive-or are in reality a kind of lengthwise parity. Each bit of the checksum is a parity bit for the column of bits in that position in the block of data.
In the old days, when dinosaurs first began to associate with herds of wildly spinning tape drives, you heard the words "vertical parity" and "longitudinal parity". Vertical parity was in those days a seventh bit for each six-bit character written on the tape, and longitudinal parity was a 7-bit character tacked on the end of each tape record, just like a checksum.
Enough review.
CRC is a much better scheme. A typical CRC implementation would add a 16-bit code to the end of a 256-byte block of data. A simple checksum would warn you of all single-bit errors, and some errors involving more than one bit. But CRC could detect all single and double bit errors, all errors with an odd number of error bits, all bursts of errors up to 16-bits in a row, and nearly all bursts of 17 or 18 bits in a row. Wow!
Also, you can even use CRC codes to CORRECT single-bit errors, if you trade off against some detection of longer error bursts.
You will run into CRC if you look into hard disks, or well- written modem software.
I like to write well-written programs, so I have been trying to understand CRC for some time now. A long time ago I had access to a book called "Error Correcting Codes", which is a classic. But I can't locate a copy now. I have seen numerous articles on the topic, especially in Computer Design. There was even one in Byte, Sept. 83, page 438. But I couldn't make the program in Byte work the way CRC's are supposed to, and I don't save my old Computer Design magazines.
Bobby Deen to the rescue. Bobby had a copy of a public domain routine by Paul Hansknecht, of Carpenter Associates, Box 451, Bloomfield Hills, MI 48013. Actually four little subroutines, to:
What is the basic idea of CRC? You perform an algorithm on each bit of a block of data, and get a CRC value. You append the CRC value to the data, and transmit both data and CRC. The receiver performs the same algorithm on the total record, both the data and the CRC code; when finished, the result of the receiver's CRC algorithm should be zero. If not zero, there was an error.
I am speaking in terms of sending and receiving, as in transmitting data with a modem. It all applies equally to writing and reading records on a disk, or even in adding check codes to a ROM. The programs I wrote and will list here merely operate on a buffer in RAM, so that I can see what is happening. You can extend them to practical uses from this base.
Which brings us to algorithms. The one Bobby gave me works like this:
The 16-bit value is initialized to zero. Then each bit in the data buffer is presented one at a time where the input arrow is. The bits in the 16-bit value are all shifted left one position, and the new data bit comes in the right end to become the new bit 0. The bit which shifts out the left end is Exclusive-ORed with the bits now found in bits 12, 5, and 0. That is, if the bit shifted out was a zero, nothing happens. If the bit shifted out was a one, exclusive or the 16 bit value with $1021.
If you understand the math of cyclic polynomials (I don't), this is supposed to be equivalent to X^16 + X^12 + X^5 + 1. An organization known to me only as CCITT recommends this polynomial. Another good one is reputed to be X^16 + X^15 +X^2 + 1, which is implemented by changing the exclusive or value from $1021 to $8005.
After all the bits of the data have been processed through the algorithm, 16 more zero bits are shifted through. After the zeroes, the value in the CRC register is the CRC code we append to the data.
The "receiver" processes the data the same way, starting by zeroing the CRC register. But instead of ending by shifting in 16 more zeroes, the receiver ends by shifting in the CRC code received.
I wanted to see if it really could find all those kinds of errors mentioned above. I wrote a program which would compute the CRC value and append it to a data block. Then I wrote another program which would "receive" the block and print out the resulting CRC value. Then I modified it to one-by-one toggle each bit position in the entire block, simulating a single bit error in each bit position in the whole buffer. My buffer is 256 bytes long, so that means 8*256 or 2048, different error positions. Actually 2064, because of the two bytes of CRC.
This way I experimentally "discovered" that the value produced by the CRC computation on the received message is dependent on the error bit position. It doesn't matter what the data was. Therefore, if I had a lookup table of 2064 16-bit entries, I could search through it and find out which bit position was wrong. There must be an easier way to figure out which bit position is wrong, but that is one of the things I still need to learn.
Okay. CRC.BYTE (lines 2890-3060) is a subroutine to process the eight bits of one byte through the CRC algorithm. CRC.BYTE needs to be called once for each byte of data in the buffer, plus either two zero bytes for a SEND routine or two CRC bytes for a RECV routine.
CRC.BUFFER (lines 2700-2850) is a little subroutine which calls CRC.BYTE once for each byte in the extended buffer. I assume it is called with PNTR pointing at the first byte in the buffer, and LIMIT is equated to the byte just beyond the end. The extended buffer includes either two zeroes on the end, or the two CRC bytes.
SETUP (lines 2610-2690) is a subroutine to initialize the CRC value register to zeroes, and to set PNTR to point at the beginning of the buffer.
The SEND and RECV routines at lines 1160-1380 simulate "sending" and "receiving" the buffer. Note that both SEND and RECV finish by displaying the calculated CRC value. SEND also stores the calculated CRC value into the end of the extended buffer. RECV should end up with a CRC value of $0000, unless there have been bits changed between calls to SEND and RECV.
TEST.SINGLE.BIT.ERRORS (lines 1390-1800) is the testing subroutine which I described above. It calls CRC.BUFFER 2064 times. Each time a different bit is changed. I print out the resulting CRC code each time, eight to a line, with the address of the byte containing the error bit leading the line. Before running TEST.SINGLE.BIT.ERRORS, you should run SEND to be sure a valid CRC code is installed in the extended test buffer.
I wrote another test routine which tests all two-bit errors. See TEST.DOUBLE.BIT.ERRORS, lines 1810-2410. The only trouble is it would take about 72 hours to run, so I haven't let it go all the way. I designed it to step through every bit position in two nested loops. If both loops happen to be at the same bit position, the bit will be toggled twice resulting in no error. I designed the program to print the address of the current byte whenever there was no error.
You might experiment with error bursts of various lengths, which should take no longer than TEST.SINGLE.BIT.ERRORS to run.
I would really be interested in finding out how to go backwards from a non-zero received CRC value to correct single-bit errors. Is there some easy way, without either a huge table or a long computation? If any of you know how, or have an article that tells how, pass it along.
1000 *SAVE S.CRC GENERATOR (HANSKNECHT) 1010 *-------------------------------- 1020 BUFFER .EQ $4000 1030 LIMIT .EQ $4102 1040 *-------------------------------- 1050 CRC .EQ $00,01 1060 PNTR .EQ $02,03 1070 TPTR .EQ $04,05 1080 TMASK .EQ $06 1090 SPTR .EQ $07,08 1100 SMASK .EQ $09 1110 *-------------------------------- 1120 PRNTAX .EQ $F941 1130 CROUT .EQ $FD8E 1140 PRBYTE .EQ $FDDA 1150 COUT .EQ $FDED 1160 *-------------------------------- 1170 * SIMULATE SENDING A BUFFER-FULL 1180 *-------------------------------- 1190 SEND JSR SETUP CLEAR CRC, POINT AT BUFFER 1200 LDA #0 CLEAR CRC BYTES IN BUFFER 1210 STA LIMIT-1 1220 STA LIMIT-2 1230 JSR CRC.BUFFER COMPUTE CRC OF 258 BYTES 1240 LDX CRC STORE CRC INTO LAST 2 BYTES 1250 LDA CRC+1 1260 STX LIMIT-1 1270 STA LIMIT-2 1280 JSR PRNTAX DISPLAY THE CRC 1290 JMP CROUT <RETURN> AND RETURN 1300 *-------------------------------- 1310 * SIMULATE RECEIVING A BUFFER-FULL 1320 *-------------------------------- 1330 RECV JSR SETUP CLEAR CRC, POINT AT BUFFER 1340 JSR CRC.BUFFER COMPUTE CRC OF 258 BYTES 1350 LDX CRC DISPLAY CRC IN HEX 1360 LDA CRC+1 1370 JSR PRNTAX 1380 JMP CROUT 1390 *-------------------------------- 1400 * TRY "RECEIVING" THE 258 BYTES 1410 * WITH A KNOWN SINGLE-BIT ERROR. 1420 *-------------------------------- 1430 TEST.SINGLE.BIT.ERRORS 1440 LDA #BUFFER 1450 STA TPTR FOR TPTR = BUFFER TO LIMIT 1460 LDA /BUFFER 1470 STA TPTR+1 1480 .1 LDA TPTR+1 PRINT TPTR"-" 1490 LDX TPTR 1500 JSR PRNTAX 1510 LDA #"-" 1520 JSR COUT 1530 LDA #$80 FOR TMASK = 1540 STA TMASK $80,40,20,10,8,4,2,1 1550 .2 LDY #0 1560 LDA (TPTR),Y INVERT BIT, MAKING ERROR 1570 EOR TMASK 1580 STA (TPTR),Y 1590 JSR SETUP CLEAR CRC, POINT AT BUFFER 1600 JSR CRC.BUFFER COMPUTE CRC 1610 LDA #" " PRINT " "CRC 1620 JSR COUT 1630 LDA CRC+1 1640 LDX CRC 1650 JSR PRNTAX 1660 LDA (TPTR),Y FIX ERRONEOUS BIT 1670 EOR TMASK 1680 STA (TPTR),Y 1690 LSR TMASK NEXT TMASK 1700 BNE .2 ...MORE 1710 JSR CROUT PRINT<CR> 1720 INC TPTR NEXT TPTR 1730 BNE .3 1740 INC TPTR+1 1750 .3 LDA TPTR 1760 CMP #LIMIT 1770 LDA TPTR+1 1780 SBC /LIMIT+1 1790 BCC .1 ...MORE 1800 RTS 1810 *-------------------------------- 1820 TEST.DOUBLE.BIT.ERRORS 1830 LDA #BUFFER 1840 STA SPTR FOR SPTR=BUFFER TO LIMIT 1850 LDA /BUFFER 1860 STA SPTR+1 1870 *-------------------------------- 1880 .1 LDA #$80 FOR SMASK=80,40,20,10,8,4,2,1 1890 STA SMASK 1900 *-------------------------------- 1910 .2 LDA #BUFFER FOR TPTR=BUFFER TO LIMIT 1920 STA TPTR 1930 LDA /BUFFER 1940 STA TPTR+1 1950 *-------------------------------- 1960 .3 LDA #$80 FOR TMASK=80,40,20,10,8,4,2,1 1970 STA TMASK 1980 *-------------------------------- 1990 .4 LDY #0 2000 LDA (TPTR),Y MAKE FIRST ERROR 2010 EOR TMASK 2020 STA (TPTR),Y 2030 LDA (SPTR),Y MAKE SECOND ERROR 2040 EOR SMASK 2050 STA (SPTR),Y 2060 JSR SETUP CLEAR CRC, POINT AT BUFFER 2070 JSR CRC.BUFFER COMPUTE CRC 2080 LDA (SPTR),Y FIX BOTH ERRORS 2090 EOR SMASK 2100 STA (SPTR),Y 2110 LDA (TPTR),Y 2120 EOR TMASK 2130 STA (TPTR),Y 2140 *-------------------------------- 2150 LDA CRC IF CRC=0, DISPLAY POINTERS 2160 ORA CRC+1 2170 BNE .5 ...CRC .NE. 0, SO CONTINUE 2180 JSR DISPLAY.POINTERS 2190 *-------------------------------- 2200 .5 LSR TMASK NEXT TMASK 2210 BNE .4 ...MORE 2220 INC TPTR NEXT TPTR 2230 BNE .6 2240 INC TPTR+1 2250 .6 LDA TPTR 2260 CMP #LIMIT 2270 LDA TPTR+1 2280 SBC /LIMIT+1 2290 BCC .3 ...MORE 2300 *-------------------------------- 2310 LSR SMASK NEXT SMASK 2320 BNE .2 ...MORE IN THIS BYTE 2330 INC SPTR NEXT SPTR 2340 BNE .7 2350 INC SPTR+1 2360 .7 LDA SPTR 2370 CMP #LIMIT 2380 LDA SPTR+1 2390 SBC /LIMIT+1 2400 BCC .1 ...MORE 2410 RTS 2420 *-------------------------------- 2430 DISPLAY.POINTERS 2440 LDA TPTR+1 PRINT TPTR"-"TMASK" "; 2450 LDX TPTR 2460 JSR PRNTAX 2470 LDA #"-" 2480 JSR COUT 2490 LDA TMASK 2500 JSR PRBYTE 2510 LDA #" " 2520 JSR COUT 2530 LDA SPTR+1 PRINT SPTR"-"SMASK 2540 LDX SPTR 2550 JSR PRNTAX 2560 LDA #"-" 2570 JSR COUT 2580 LDA SMASK 2590 JSR PRBYTE 2600 JMP CROUT 2610 *-------------------------------- 2620 SETUP LDA #0 CLEAR CRC 2630 STA CRC 2640 STA CRC+1 2650 LDA #BUFFER SET UP PNTR TO BUFFER 2660 STA PNTR 2670 LDA /BUFFER 2680 STA PNTR+1 2690 RTS 2700 *-------------------------------- 2710 * COMPUTE CRC FROM (PNTR) THRU LIMIT 2720 *-------------------------------- 2730 CRC.BUFFER 2740 .1 LDY #0 SCAN THRU THE BUFFER 2750 LDA (PNTR),Y 2760 JSR CRC.BYTE 2770 INC PNTR NEXT BYTE 2780 BNE .2 2790 INC PNTR+1 2800 .2 LDA PNTR CHECK LIMIT 2810 CMP #LIMIT 2820 LDA PNTR+1 2830 SBC /LIMIT 2840 BCC .1 MORE TO GO 2850 RTS 2860 *-------------------------------- 2870 * COMPUTE CRC ON A SINGLE BYTE 2880 *-------------------------------- 2890 CRC.BYTE 2900 LDX #8 DO 8 BITS 2910 .1 ASL MSB OF BYTE TO CARRY 2920 ROL CRC 2930 ROL CRC+1 2940 BCC .2 --> 0, GET NEXT BIT 2950 PHA --> 1, TOGGLE POLYNOMIAL BITS 2960 LDA CRC 2970 EOR #$21 TOGGLE BITS 0 AND 5 2980 STA CRC 2990 LDA CRC+1 3000 EOR #$10 TOGGLE BIT 12 3010 STA CRC+1 3020 PLA 3030 .2 DEX NEXT BIT 3040 BNE .1 3050 RTS 3060 *-------------------------------- 3070 * FIND WHICH BIT IS BAD IN BUFFER+CRC 3080 * 3090 * RESULT IS BIT POSITION IN MESSAGE, 3100 * WHERE THE FIRST BIT OF THE MESSAGE IS BIT 0 3110 * AND (IN THIS CASE) THE LAST CRC BIT IS BIT $80F. 3120 * 3130 * ALGORITHM BY BRUCE LOVE, NEW ZEALAND 3140 *-------------------------------- 3150 BIT.NUMBER .EQ $10,11 3160 DUMMY.CRC .EQ $12,13 3170 *-------------------------------- 3180 FIND.BAD.BIT 3190 LDA #$80F TOTAL # BITS - 1 3200 STA BIT.NUMBER (WE WILL COUNT BACKWARDS) 3210 LDA /$80F 3220 STA BIT.NUMBER+1 3230 LDA #$0001 STARTING POINT FOR BIT FINDER 3240 STA DUMMY.CRC 3250 LDA /$0001 3260 STA DUMMY.CRC+1 3270 .1 LDA CRC COMPARE RECEIVED CRC WITH 3280 CMP DUMMY.CRC PROCESSED VALUE; 3290 BNE .2 IF THEY MATCH, WE HAVE FOUND THE 3300 LDA CRC+1 BAD BIT. 3310 CMP DUMMY.CRC+1 3320 BEQ .4 ...FOUND BAD BIT! 3330 .2 LDA BIT.NUMBER DECREMENT BIT COUNTER 3340 BNE .3 3350 DEC BIT.NUMBER+1 3360 BMI .5 WENT TOO FAR 3370 .3 DEC BIT.NUMBER 3380 ASL DUMMY.CRC 3390 ROL DUMMY.CRC+1 3400 BCC .1 3410 LDA DUMMY.CRC 3420 EOR #$21 3430 STA DUMMY.CRC 3440 LDA DUMMY.CRC+1 3450 EOR #$10 3460 STA DUMMY.CRC+1 3470 JMP .1 3480 .4 LDA BIT.NUMBER+1 PRINT THE BIT NUMBER 3490 JSR PRBYTE (IF $8000, THE ERROR WAS 3500 LDA BIT.NUMBER NOT A SINGLE BIT) 3510 JSR PRBYTE 3520 JMP CROUT 3530 .5 BRK 3540 *-------------------------------- |
Some more clock cards have been brought to my attention recently.
Prometheus Versacard includes a clock, and it is compatible with ProDOS due to its ability to emulate a Thunderclock. List price is $199.
Naturally, there is a clock on the Mountain Computer CPS/Multifunction Card. Naturally, because "CPS" stands for Clock Parallel Serial, the three functions the card supports. I cannot find a current price for this card.
Practical Peripherals is advertising ProClock, no price mentioned.
Well, maybe not a whole evening, but a good portion of it. And certainly not alone, there were about 150 others in the room. But I did have the opportunity to attend a dinner sponsored by the River City Apple Corps, in Austin, Texas, and hear a speech by Steve Wozniak, the designer of our favorite pastime.
Most of Steve's speech was devoted to the history of his involvement with computers, and the development of the Apple II. That story is pretty well-known by now, so I won't mention too much of it here. The most interesting facets to me were hearing how much of a prankster Woz has always been, and finding out how many features of the Apple II were motivated only by Steve's desire to write a Breakout game in BASIC.
My favorite part of the evening was the question-and-answer session and the informal chats afterward, when we all got our chance to ask about what we really wanted to know. The first question is mine, the rest came from all around the room. These are the items that seem to be of most concern to AAL readers:
How about 65816 machines?
We're heavily involved in a computer based around that chip. But, final computer becoming a full-fledged product is subject to too many other variations, such as: you start working on it and you decide, no, this computer didn't come out right, it's too long, the actual date it will be done, it's not enough, we have to do a different product. So, it may be as soon as a few months, and it may be as long as a couple years before Apple has a product based around that new processor. Fortunately it is 100% compatible with what we've done before. Meaning it's a compatible upgrade, and that's what the Apple II has to do.
When can we expect a portable //e?
It's ... in the works. We're certainly thinking about it and delving into it and unless the project gets cancelled, probably very soon, but you can never say for sure until it's out.
How about color on the Macintosh?
There is no color on the Macintosh. ... Laser printers ... (and) ... LCD displays ... are converging on black and white technology being appropriate for that product line. There is no color for the Macintosh at this time.
Do you expect to see the 3 1/2 inch disks on the //e?
Apple believes that it's time to start moving the entire company toward higher density, better technology, more modern technology disk drives, and the 3 1/2 inch disk drives from Sony that is in the Lisa and Macintosh computers now is the proper direction to move in. It'll be interesting to see how it unfolds over time, as to how the conversion is made and yet extreme compatibility and support taken into account. All the software exists today on 5 1/4 inch disks. How do we get there?
It could be like your second disk can be a nice 3 1/2 inch with a lot more storage capability, but it may be years before it's proper to expect bootable software, to be able to boot on 3 1/2 inch drives. It's a challenge, and it just can't be turned over overnight. We could come out with a product for the Apple II today that uses a 3 1/2 inch drive as your only drive, and you know you can't run any of your software on it.... The sales of such a product would not start until there was a software base established.
What are you personally working on?
I'm interested in the future Apple II families. We're always pursuing higher performance-to-cost versions of the Apple II. And sometimes that's achieved by integrating several chips down into one custom chip, or by looking at accessories that are very commonplace, that almost everyone's going to buy for their //e. You can build one version of it with a lot of those accessories in and save a lot of money in the end, a lot of hassle. There are ways to improve the cost/performance ratio.
The other end, we're always trying to improve the capabilities of the machine. How are we going to eventually, someday, challenge IBM in the multi-megabyte computer world, the high-end? How are we going to improve performance?, increase screen resolution?, all those sort of questions, those sort of enhancements. I've been working very closely on one of those projects in Apple since returning.
... I think we've got to start heading towards a real, more useful home machine that does have a few of the things that we originally pursued, that we now believe is only about 10% of our market. Things such as: speech recognition and speech generation, built in, because they're relatively inexpensive and easy to do now to some level of quality. And it should also have more of the home-ish features, the features that are used in a personal, home environment built in.
So, that's the gist of it. I would like to thank Stuart Greenfield, of the River City Apple Corps, for the invitation to attend their dinner, and of course thank you, Woz, for coming to visit us.
One last note: Steve referred to a portable Apple //e as "probably very soon". Lately we've been hearing about the Apple //c, a 9-pound machine sporting 128K RAM, one disk drive, built-in serial and parallel ports, and no slots. Also no monitor, which sounds a little strange. Price -- $1200. The //c announcement is expected in late April.
The Prom Burners reviewed elsewhere in this issue all were designed especially for Apple owners, and plug directly into an Apple slot. Believe it or not, there are other computers.... There are many brands of industrial grade prom burners which are not specifically designed for a particular computer host. Most of these connect to a serial port on whatever host computer you choose.
Many of these expect to receive data in a special format, called by some the Intellec Hex Paper Tape Format. Or, since at least two of those capitalized words are rather old- fashioned, the Intellec Hex Format. Intellec is also used to communicate with a variety of Emulation hardware, and Development Systems.
The S-C Assemblers produce either binary files or the binary image in memory of the object code. Can we convert a file or range of RAM to the Intellec format, and send it via a serial port? Sure, it only takes a little software....
Let's first simplify a little by assuming we can BLOAD a binary file first into Apple RAM. Then we only need a program which can translate and send a block of Apple RAM.
I would like to be able to operate the program by a control-Y monitor command. I want to type what looks like the memory move ("M") command: the first address specifies to the target system where the data should load; the second and third addresses specify the Apple RAM to be sent. I also would like to be able to specify which slot the serial port is in, or where in RAM a subroutine to send bytes to the target system can be found if there is no intelligent interface card.
The program I wrote fulfills those wishes. It loads at $300, and self-installs a control-Y vector for the monitor. Location $0000 and $0001 must then be set to the low- and high-bytes of the port, and the "M"-like control-Y command can be typed. For example:
:BRUN B.INTELLEC :$0:2 0 :$F800<800.FFF^O^Y
The port value is 0002, which means slot 2. I wrote the program so that a port value 0001 thru 0007 means a slot number; 0100 thru FFFF means a subroutine address for your own driver; 0000 means send the output where it already is directed when you type the control-Y command. The "^O^Y" on the third line above means "control-O control-Y", which is how you type a control-Y when you are in the S-C Assembler. If you type the command from the monitor (*-prompt), omit the control-O.
I chose to send the data in a form that is compatible with both Intel and Zilog specifications, as I understand them. I do not have any equipment which expects this format around here, so I cannot test the program with live data. If you do, and you find I have mis-interpreted something, I would sure appreciate some feedback.
There are two types of records sent: data and end-of-file records. Each record begins with a colon (:) and ends with carriage return linefeed (CRLF, which is $8D8A). The records consist of five fields.
Record length field: two hex digits which specify how many bytes of data will be in the data field. Will be 00 for an end-of-file record. In keeping with Zilog's standard, the maximum length will be 32 bytes.
Address field: four hex digits which specify the load address in a data record, and the run address in an end-of-file record.
Record type field: 00 for a data record, and 01 for an end-of-file record.
Data field: two hex digits for each byte of data specified by the record length field. Empty for an end-of-file record.
Checksum field: two hex digits which represent the complement of the 8-bit sum of the 8-bit bytes which result from converting each pair of hex digits in the other four fields of this record to 8-bit binary values.
Lines 1250-1330 of the program set up the control-Y vector for the Apple Monitor. If this is unfamiliar to you, you might like to read all about it in the October 1981 issue of Apple Assembly Line, pages 14-17.
Briefly, once set up, a control-Y command will branch to your own code. It gives a way to extend the Apple monitor. You can type up to three addresses before the control-Y, and they will be parsed by the monitor and saved in five two-byte variables (called A1, A2, A3, A4, and A5). If you type "aaaa<bbbb.cccc" before the control-Y:
aaaa will be saved in A4 and A5 bbbb will be saved in A1 and A3 cccc will be saved in A2
If you wish to pass more than three items of information to the control-Y routine, you can pre-store them in other locations. In my routine, you must pre-store the port value in $0000 and $0001.
The whole control-Y routine is shown in just four lines of code: lines 1470-1500. Of course, these are all subroutine calls.
TURN.ON.OUTPUT.PORT (lines 1510-1650) examines locations $0000 and 0001. If they contain 0000, then the output port is not changed. If they contain 0001 thru 00FF, the lower three bits are used to select an intelligent interface card in slot 1 through 7. A larger value indicates your own driver routine address.
TURN.OFF.OUTPUT.PORT (lines 2010-2030) sets the output back to the Apple screen.
SEND.DATA.RECORDS (lines 1660-1890) divides the area to be transmitted into a number of 32-byte blocks. Each block is send as one data record. The final block may be less than 32 bytes.
SEND.EOF.RECORD (lines 1900-2000) sends the end-of-file record. The original loading address is assumed to be the run address. If you would rather send 0000 for a run address, you can change lines 1960 and 1980 to "LDA #0".
SEND.RECORD (lines 2050-2330) formats and transmits one record of either type, using the count, address, and type information already setup by the caller. It also updates A1 and A4 for the next record.
SEND.BYTE (lines 2340-2420) accumulates a byte in the checksum, and then converts it to two hex digits and transmits it.
You can use this program with any of the S-C Macro Assemblers or Cross Assemblers, exactly as shown. If you are using some other brand of assembler, you will probably have to leave the assembler environment to load this program, load the object code you wish to transmit, and run the program.
1000 *SAVE S.INTELLEC HEX FORMATTER 1010 .OR $300 1020 *-------------------------------- 1030 PORT .EQ $00,01 1040 CHECK.SUM .EQ $02 1050 TYPE .EQ $03 1060 COUNT .EQ $04 1070 REMAINING .EQ $05,06 1080 *-------------------------------- 1090 A1 .EQ $3C,3D 1100 A2 .EQ $3E,3F 1110 A3 .EQ $40,41 1120 A4 .EQ $42,43 1130 A5 .EQ $44,45 1140 *-------------------------------- 1150 CTRLY.VECTOR .EQ $3F8 THRU $3FA 1160 DOS.REHOOK .EQ $3EA 1170 *-------------------------------- 1180 MON.NXTA4 .EQ $FCB4 1190 MON.CROUT .EQ $FD8E 1200 MON.PRHEX .EQ $FDDA 1210 MON.COUT .EQ $FDED 1220 MON.SETVID .EQ $FE93 1230 *-------------------------------- 1240 * SETUP CONTROL-Y 1250 *-------------------------------- 1260 SETUP LDA #SEND.DATA 1270 STA CTRLY.VECTOR+1 1280 LDA /SEND.DATA 1290 STA CTRLY.VECTOR+2 1300 LDA #$4C 1310 STA CTRLY.VECTOR 1320 RTS 1330 *-------------------------------- 1340 * *0:XX YY (LO,HI OF PORT) 1350 * *TARGET<START.END<Y> 1360 * IF PORT IS 0, DO NOT CHANGE OUTPUT 1370 * IF PORT IS 1...7, OUTPUT TO SLOT. 1380 * ELSE OUTPUT TO SUBROUTINE 1390 * SEND BYTES START...END 1400 * 1410 * 1. TURN ON OUTPUT PORT 1420 * 2. SEND DATA RECORDS 1430 * 3. SEND EOF RECORD 1440 * 4. TURN OFF OUTPUT PORT 1450 *-------------------------------- 1460 SEND.DATA 1470 JSR TURN.ON.OUTPUT.PORT 1480 JSR SEND.DATA.RECORDS 1490 JSR SEND.EOF.RECORD 1500 JMP TURN.OFF.OUTPUT.PORT 1510 *-------------------------------- 1520 TURN.ON.OUTPUT.PORT 1530 LDX PORT+1 HI-BYTE OF PORT SPECIFIED 1540 BNE .1 1550 LDA PORT LO-BYTE, MUST BE SLOT 1560 AND #$07 1570 BEQ .3 SLOT 0, JUST SCREEN 1580 ORA #$C0 1590 BNE .2 ...ALWAYS 1600 .1 TXA HI-BYTE OF SUBROUTINE 1610 LDX PORT LO-BYTE OF SUBROUTINE 1620 .2 STA $37 1630 STX $36 1640 JSR DOS.REHOOK 1650 .3 RTS 1660 *-------------------------------- 1670 SEND.DATA.RECORDS 1680 LDA #0 1690 STA TYPE 1700 INC A2 POINT JUST BEYOND THE END 1710 BNE .1 1720 INC A2+1 1730 .1 SEC 1740 LDX #32 1750 LDA A2 SEE HOW MANY BYTES LEFT 1760 SBC A1 1770 STA REMAINING 1780 LDA A2+1 1790 SBC A1+1 1800 STA REMAINING+1 1810 BNE .2 USE MIN(32,A2-A1+1) 1820 CPX REMAINING 1830 BCC .2 1840 LDX REMAINING 1850 BEQ .3 ...FINISHED 1860 .2 STX COUNT 1870 JSR SEND.RECORD 1880 JMP .1 ...ALWAYS 1890 .3 RTS 1900 *-------------------------------- 1910 SEND.EOF.RECORD 1920 LDY #0 1930 STY COUNT 1940 INY 1950 STY TYPE 1960 LDA A5 RUN ADDRESS (LO) 1970 STA A4 1980 LDA A5+1 RUN ADDRESS (HI) 1990 STA A4+1 2000 JMP SEND.RECORD 2010 *-------------------------------- 2020 TURN.OFF.OUTPUT.PORT 2030 JSR MON.SETVID 2040 JMP DOS.REHOOK 2050 *-------------------------------- 2060 SEND.RECORD 2070 LDA #":" 2080 JSR MON.COUT 2090 LDA #0 2100 STA CHECK.SUM 2110 LDA COUNT 2120 JSR SEND.BYTE 2130 LDA A4+1 2140 JSR SEND.BYTE 2150 LDA A4 2160 JSR SEND.BYTE 2170 LDA TYPE 2180 JSR SEND.BYTE 2190 LDA COUNT 2200 BEQ .2 2210 LDY #0 2220 .1 LDA (A1),Y 2230 JSR SEND.BYTE 2250 JSR MON.NXTA4 2260 DEC COUNT 2270 BNE .1 2280 .2 SEC 2285 LDA #0 2290 SBC CHECK.SUM 2300 JSR SEND.BYTE 2310 JSR MON.CROUT 2320 LDA #$8A LINEFEED 2330 JMP MON.COUT 2340 *-------------------------------- 2350 SEND.BYTE 2360 PHA 2370 CLC 2380 ADC CHECK.SUM 2390 STA CHECK.SUM 2400 PLA 2410 JMP MON.PRHEX 2420 *-------------------------------- |
When DOS was young, Apples tended to have varying amounts of memory under 48K. Some had 16K, which was the standard purchase at a computer store; others 24K, with one row of 16K and two of 4K; others 32K; and some 48K. Trying to write a DOS image that would fit all of these memories was quite a task.
Apple introduced the concept of a "master" and a "slave" disk. Master disks have a generic image of DOS. The boot process first loads the DOS image as though the machine only has 16K RAM, and then the image is relocated as high as possible in memory. Slave disks have a frozen image, already relocated for a particular memory size. The INIT command always creates a slave disk. In order to make a master disk you either copy and old master using COPYA (or equivalent copy program), or you use the MASTER.CREATE program on the DOS System Master Disk. (For a while the MASTER.CREATE program was called UPDATE 3.3.)
But now! But now you will have a difficult time finding an Apple with less than 48K memory. After all, the chips are only about a dollar apiece, or $8 to $12 for a set of eight. Who needs master disks anymore?
A lot of people think they do, because MASTER.CREATE is there and the reference manual makes such a big deal about it. And this causes a problem. What if I want a master disk with a modified DOS? MASTER.CREATE always reads the DOS image off the system master disk, and it is unmodified. Well, you can use a disk zap program on a copy of the system master.
Or, you can forget all about MASTER.CREATE and use my handy- dandy little patch installer. The program which follows reads the DOS image from the first 3 tracks into memory from $4000 thru $64FF. Then it installs patches from a table of patches; this part is almost identical to the patch installer published in the April 1983 issue of AAL. Finally it writes the patched DOS back on the first three tracks. And it does all this so fast you'll think it never happened.
Once you have coded the patches you want, and have tested them, you can update all your old DOS 3.3 disks almost as fast as you can open and close the drive door. With slight modifications, you could have it write the patched image on successive disks without re-reading and re-patching each time.
Looking at the program, Lines 1200-1240 do the overall job. Just below that, lines 1260-1290 give two entry points to a block of code that sets up an IOB for RWTS and then calls RWTS. The only difference between the two calls is the opcode, either READ or WRITE. Below that point, there is a backwards loop that counts from track 2, sector 4, back to track 0, sector 0. Just for fun, I print out the track and sector numbers just before reading or writing each sector. (If you get tired of the fun, simply delete line 1450, the JSR $F941.)
The DOS image on tracks 0, 1, and 2 is not in exactly the same order as you find it in memory after booting. Therefore the patcher maps patch addresses to the new locations. Lines 1060-1080 define the remapping constants. Addresses which in the running image will be between $B600 and $BFFF will be located from $4000 thru $49FF. If the original was a master, code which does the relocating part of the boot will be found from $4A00 thru $4BFF. The code between $9D00 and $B5FF will be found from $4C00 thru $64FF. The two constants DOS.9D and DOS.B6 are used in figuring the application points of the patches in lines 2110, 2350, and 2540.
For a full explanation of lines 1590-1900, see the April 1983 AAL, pages 24-27. The patch set up to be installed in lines 2020-2580 is the fast LOAD, BLOAD, RUN, BRUN patch from pages 2-8 of the same issue.
1000 *SAVE S.APPLY DOS PATCHES 1010 *-------------------------------- 1020 PNTR .EQ $00,01 1030 PATCH .EQ $02,03 1040 SECTOR.CNT .EQ $04 1050 *-------------------------------- 1060 DOS.IMAGE .EQ $4000 - $64FF 1070 DOS.9D .EQ $9D00-DOS.IMAGE-$0C00 1080 DOS.B6 .EQ $B600-DOS.IMAGE 1090 *-------------------------------- 1100 GETIOB .EQ $3E3 1110 RWTS .EQ $3D9 1120 *-------------------------------- 1130 IOB .EQ $B7E8 1140 IOB.VOLUME .EQ IOB+3 1150 IOB.TRACK .EQ IOB+4 1160 IOB.SECTOR .EQ IOB+5 1170 IOB.BUFADR .EQ IOB+8 1180 IOB.OPCODE .EQ IOB+12 1190 *-------------------------------- 1200 PATCH.DOS 1210 JSR READ.DOS.IMAGE 1220 JSR PATCHER 1230 JSR WRITE.DOS.IMAGE 1240 RTS 1250 *-------------------------------- 1260 READ.DOS.IMAGE 1270 LDA #$01 READ OPCODE 1280 .HS 2C 1290 WRITE.DOS.IMAGE 1300 LDA #$02 WRITE OPCODE 1310 STA IOB.OPCODE 1320 LDA #0 1330 STA IOB.BUFADR 1340 STA IOB.VOLUME 1350 LDA #DOS.IMAGE/256+16+16+5-1 1360 STA IOB.BUFADR+1 1370 LDA #2 TRACK 2 1380 STA IOB.TRACK 1390 LDA #4 SECTOR 4 1400 STA IOB.SECTOR 1410 LDA #16+16+5 1420 STA SECTOR.CNT 1430 .1 LDA IOB.TRACK 1440 LDX IOB.SECTOR 1450 JSR $F941 1460 JSR GETIOB 1470 JSR RWTS 1480 LDY IOB.SECTOR 1490 DEY 1500 BPL .2 1510 LDY #15 1520 DEC IOB.TRACK 1530 .2 STY IOB.SECTOR 1540 DEC IOB.BUFADR+1 1550 DEC SECTOR.CNT 1560 BNE .1 1570 RTS 1580 *-------------------------------- 1590 PATCHER 1600 LDA #PATCHES-1 1610 STA PNTR 1620 LDA /PATCHES-1 1630 STA PNTR+1 1640 LDY #0 1650 1660 .1 JSR GET.BYTE LENGTH OF NEXT PATCH 1670 BEQ .4 FINISHED 1680 TAX SAVE LENGTH IN X 1690 JSR GET.BYTE ADDRESS OF PATCH 1700 STA PATCH 1710 JSR GET.BYTE 1720 STA PATCH+1 1730 1740 .2 JSR GET.BYTE 1750 STA (PATCH),Y 1760 INC PATCH 1770 BNE .3 1780 INC PATCH+1 1790 .3 DEX 1800 BNE .2 1810 BEQ .1 ...ALWAYS 1820 1830 .4 RTS 1840 *-------------------------------- 1850 GET.BYTE 1860 INC PNTR 1870 BNE .1 1880 INC PNTR+1 1890 .1 LDA (PNTR),Y 1900 RTS 1910 *-------------------------------- 1920 PATCHES 1930 *-------------------------------- 1940 * S.FAST LOAD 1950 * 1960 * FAST "LOAD" AND "BLOAD" 1970 * 1980 * INSTALLED IN UNUSED AREAS IN DOS 3.3: 1990 * $BA69-$BA95 (45 BYTES FREE) 2000 * $BCDF-$BCFF (33 BYTES FREE) 2010 *-------------------------------- 2020 READ.RANGE .EQ $AC96 2030 READ.NEXT.SECTOR .EQ $B0B6 2040 END.OF.DATA.ERROR .EQ $B36F 2050 RANGE.LENGTH .EQ $B5C1,C2 2060 RANGE.ADDRESS .EQ $B5C3,C4 2070 BUFFER.ADDRESS .EQ $B5CB,CC 2080 SECTOR.COUNT .EQ $B5E4,E5 2090 BYTE.OFFSET .EQ $B5E6 2100 *-------------------------------- 2110 .DA #P1.LENGTH,$BA69-DOS.B6 2120 .PH $BA69 2130 PATCH1 LDA BYTE.OFFSET LAST BYTE OF 2140 BNE GO.READ.RANGE A SECTOR? 2150 LDA RANGE.LENGTH+1 WHOLE SECTOR LEFT? 2160 BEQ GO.READ.RANGE NO. 2170 LDA BUFFER.ADDRESS SAVE BUFFER ADDRESS 2180 PHA 2190 LDA BUFFER.ADDRESS+1 2200 PHA 2210 LDA RANGE.ADDRESS READ DIRECTLY 2220 STA BUFFER.ADDRESS INTO RANGE 2230 LDA RANGE.ADDRESS+1 2240 STA BUFFER.ADDRESS+1 2250 READ.LOOP 2260 JSR READ.NEXT.SECTOR 2270 BCS .1 2280 JMP PATCH2 2290 .1 JMP END.OF.DATA.ERROR 2300 GO.READ.RANGE 2310 JMP READ.RANGE 2320 P1.LENGTH .EQ *-PATCH1 2330 .EP 2340 *-------------------------------- 2350 .DA #P2.LENGTH,$BCDF-DOS.B6 2360 .PH $BCDF 2370 PATCH2 INC SECTOR.COUNT 2380 BNE .1 2390 INC SECTOR.COUNT+1 2400 .1 INC RANGE.ADDRESS+1 NEXT PAGE 2410 INC BUFFER.ADDRESS+1 2420 DEC RANGE.LENGTH+1 2430 BNE .2 2440 PLA RESTORE BUFFER 2450 STA BUFFER.ADDRESS+1 2460 PLA 2470 STA BUFFER.ADDRESS 2480 JMP READ.RANGE 2490 2500 .2 JMP READ.LOOP 2510 P2.LENGTH .EQ *-PATCH2 2520 .EP 2530 *-------------------------------- 2540 .DA #P3.LENGTH,$ACA5-DOS.9D 2550 .PH $ACA5 2560 PATCH3 JMP PATCH1 2570 P3.LENGTH .EQ *-PATCH3 2580 .EP 2590 *-------------------------------- 2600 .DA #0 END OF PATCHES |
We get a lot of questions about EPROM burners and erasers. Perhaps this list will help...
Burners:
PROM Blaster System, $119, Apparat, 4401 South Tamarac Parkway, Denver, CO 80237. Phone (303) 741-1778 or (800) 525-7674. Will burn most 24-pin EPROMS. Price includes personality modules for 2704, 2708, 2508, 2758, 2716(TI), 2516, 2716, 2532, 2732, 2732A, 68764, 2815, and 2816. ZIF socket for EPROM. No power switch, so you must power down the Apple whenever you insert or remove an EPROM.
Apple-PROM, $149.95, Computer Technology Associates, 1704 Moon N.E., Suite 14, Albuquerque, NM 87112. Phone (505)298-0942. Will burn most 24-pin EPROMS. DIP switch selection for 2708, 2716, 2516, 2532, 2732, 2732A, 2764, 2564. Low insertion force socket for EPROM.
Romwriter, $175, Mountain Computer....(I cannot find any recent ads, but they are still listed in distributor catalogs). We have heard that they are no longer manufacturing this card, but there are still many available. Only burns 2716 (single voltage version, not TI). ZIF for EPROM. Power switch on card allows you to safely insert and remove EPROMs without turning off your Apple. I have been using this one for several years with no problems, although I did rewrite the software to suit my own tastes and needs.
Quick EPROM Writer, $149, available from Handwell Corp., 4962 El Camino Real, Suite 119, Los Altos, CA 94022. Phone (415) 962-9265. Made in Taiwan by "COPAM". Burns both 24- and 28- pin EPROMs. All software is in firmware on the card. Nice menu select for 2716, 2516, 2532, 2732, 2732A, 2564, 2764, and 27128. No personality modules or switch selection required, as all configuration is software controlled. Power is applied to and removed from the ZIF socket under software control, so that EPROMs can be inserted and removed without turning off your Apple. Manual includes schematic, pinout diagrams for EPROMs, and a (sparsely) commented listing of firmware. The firmware apparently implements an intelligent burning algorithm, which burns twice as long as it takes to get the byte burned, rather than using a fixed delay for each byte. The result is much faster burn times than most other burners listed here.
HM3264, $395, Hollister Microsystems, 5081 Fairview, Hollister, CA 95023. Phone (408) 637-0753. Programs 2716, 2732, 2732A, 2764, and 27128. Henry Spragens uses this one, and says it is very well designed and built, though expensive. Henry has modified the software Hollister provides to use the intelligent burn algorithm (it was pretty slow until he did this). Hollister use the C800-CFFF address space, like Mountain Computer does, as a 2048-byte window into the EPROM. Bank switching on the card lets you program larger EPROMS. Power switch on card allows you to safely insert and remove chips. A program switch helps prevent inadvertent programming.
Model EP-2A-79, $169 plus $17 to $35 each for personality modules and $19 to $40 for software. Optimal Technology, Earlysville, VA 22936. Phone (804) 973-5482. Programs full range from 2708 through 27128, plus 38E70 and 8751 MPUs, assuming you purchase the corresponding personality modules and software. It is not clear to me whether this plugs directly into an Apple or requires a separate serial interface card.
Erasers:
QUV-T8 EPROM Erasers, Logical Devices, 1321E N.W. 65 Place, Fort Lauderdale, FL 33309. Phone (305) 974-0967 or (800) EE1-PROM (that is 331-7766). Four models, ranging from $49.95 to $124.95. I use and recommend the $97.50 model, which includes a slide-out tray, anti-static foam pad, UV indicator lens, timer, and safety interlock switch.
Spectronics, marketed by JDR Microdevices, 1224 S. Bascom Avenue, San Jose, CA 95128. Phone (800) 662-6279 or (408) 995-5430. Six models from $83 to $595. The $83 unit has no timer, all the others do. [ JDR's latest ad in Byte shows eight 250nsec 4116's for $7.95! ]
Jade Computer Products carries both brands of EPROM Erasers. Their price on the least expensive Spectronics is only $69.95.
Jameco Electronics lists an eraser for $79.95.
[ Bob Kovacs is the author of DISASM, owner of Rak-Ware ]
I recently received a phone call from Alan Lloyd who had just purchased DISASM. He wanted to use it to disassemble the Autostart ROM so he could customize the code for a particular application. He was frustrated by the limited editing capabilities of DISASM which makes you start all over again if you don't catch your mistake before hitting RETURN. Since he had to enter the starting and ending addresses of over a dozen data tables, he began searching for an easier (and less painful) way of entering the data. He decided to try using an EXEC file with DISASM, and it worked! Well, sort of.
I thought about the problems he ran into, and found out some interesting things about the S-C Macro Assembler along the way. It turns out that with the help of a small patch to DISASM that it is possible to run the entire program via "remote control" using an EXEC file.
The first step is to create the TEXT file that will later be EXECed. You can do this in a word processor, if your word processor makes ordinary DOS text files. Or you can write an Applesoft program to help you build an array of addresses and the proper answers to the various prompts in DISASM, and then write a complete EXEC file. I decided to use the S-C Macro Assembler, because you can use the TEXT <filename> command to write a text file. You can have the assembler in the language card, DISASM at $800, the thing to be disassembled wherever you want, and pop back and forth fast as lightning.
Just enter each line of "source" as if you were responding to the questions put to you be DISASM. You can even include lines to turn on display of DOS commands and I/O (MONIOC), and the BLOADing of DISASM and NAMETABLE.
The S-C Macro Assembler does make one thing difficult. Some of the questions asked by DISASM require a null line (a RETURN all by itself), and S-C makes it very hard to get a null line. The first of these is used to terminate the entry of data table addresses. (Alan was satisfied to have his EXEC file stop here and take over manually.)
Normally, S-C does not let you enter totally empty lines. Typing a line number without any following text is one of the ways to DELETE a line, just as in both BASIC's. After a little experimenting I discovered a trick to fool the S-C input routine. I still don't get a totally empty line, but I can put extra RETURNs into an existing line. Here's how:
The next problem I ran into was the Y/N responses for the "Full Cross-Reference" and "Generate Text File" questions. DISASM looks directly at the keyboard for those two responses, so it is blind to any EXEC file inputs. A five byte patch fixes all that, so you can use EXEC file as well as keyboard inputs. Just change the code starting at location $C5A from AD 00 C0 10 FB to 20 18 FD 09 80.
The following arbitrary example illustrates how an EXEC file might look when typed into the S-C assembler (extra RETURNs are indicated by <M>):
1000 MONIOC 1010 BLOAD DISASM 1020 BLOAD NAMETABLE 1030 $800G (Use call 2048 to EXEC from BASIC) 1040 2 (select S-C Assembler format) 1050 F800 (starting physical address) 1060 F9B9 (ending physical address) 1070 F800 (starting execution address) 1080 F8CD (table #1 start) 1090 F8CF (table #1 end) 1100 3 (table #1 format) 1110 F962 (table #2 start) 1120 F9A5 (table #2 end) 1130 5 (table #2 format) 1140 F9A6 (table #3) 1150 F9B3 1160 8 1170 F9B4 (table #4) 1180 F9B9 1190 6 1200 <M>2000 (end of tables, and NAMETABLE address) 1210 0 (no printer output) 1220 <M>NYDEMO (RETURN for no single cross reference, N for no full cross reference, Y for creating a textfile named DEMO)
(Of course, you realize that the explanatory comments in parentheses are not supposed to be typed.) I advise you to SAVE the lines on a file as S-C source code, using the SAVE <filename> command. This will become the copy you re-LOAD when you want to make changes. Then use the TEXT <filename> command to write out the EXEC file. Finally, EXEC <filename> to run the disassembly!
When EXECing, the table addresses are entered at a blinding speed that is almost imposssible to follow. If your text file has an error in it such that it does not conform to the DISASM input syntax, then things can go very wrong very fast. For those of you who would rather not see things move along quite so fast, I suggest adding a small patch to the COUT vector which provides a short delay. The following program works fine:
$300:48 PHA A9 80 LDA #$80 20 A8 FC JSR $FCA8 68 PLA 4C F0 FD JMP $FDF0
You can hook this into DOS from the assembler by typing "$36:00 03 N 3EAG". Then change line 1030 above to $812G (or CALL 2048+18 for EXEC from BASIC) to bypass DISASM's effort to setup the default DOS vectors.
Or you can even include all this stuff along with the original EXEC file. Either way, it is easier to use DISASM with an EXEC file when there are lots of data tables to be entered and you have fumble-fingers at the keyboard.
From now on, DISASM will be shipped with the five-byte patch indicated above already installed, and with two sample EXEC files designed to be EXECed from BASIC.
We have finally become convinced that we should make the source code of our S-C Macro Assembler available for purchase. Many of you have requested, for a long time now. We have resisted, I suppose through a mild case of the same paranoia which causes so many other software publishers to use copy protection and license agreements (which we eschew).
We have absolutely no experiential basis for mistrust. You have all treated our previous offerings of source code in the most honorable fashion, and we expect you will continue to do so.
Effective immediately, registered owners of Version 1.1 of the S-C Macro Assembler can purchase the source code for $100. You will be able to assemble it to obtain a paper listing, study it to learn techniques, and modify it to your own tastes. We hope many of you will make improvements and send them back to us for inclusion in future versions.
The code resides on two nearly-full diskettes. You need at least two drives to assemble it. The source is fully commented, and is organized in a logical easy-to-follow manner.
If you do not yet own Version 1.1, you may purchase or upgrade to it simultaneously with the purchase of the source code, if you wish. If you are one of those who purchased the Version 4.0 source code, we will give you $40 credit toward the purchase of the Macro 1.1 source.
Another product for which we have held back selling source code is the Double Precision Floating Point package for Applesoft (DPFP). From now on that product will be sold WITH source code, at the unforgiveably low price of $50. If you already are a registered owner of DPFP, or can supply other proof-of-purchase, we will send you the source code for $15. In case you never heard of DPFP, it is a 2048-byte &-module that provides 21-digit arithmetic and I/O for Applesoft.
Apple Assembly Line is published monthly by S-C SOFTWARE CORPORATION, P.O. Box 280300, Dallas, Texas 75228. Phone (214) 324-2050. Subscription rate is $18 per year in the USA, sent Bulk Mail; add $3 for First Class postage in USA, Canada, and Mexico; add $12 postage for other countries. Back issues are available for $1.80 each (other countries add $1 per back issue for postage).
All material herein is copyrighted by S-C SOFTWARE, all rights reserved.
Unless otherwise indicated, all material herein is authored by Bob Sander-Cederlof.
(Apple is a registered trademark of Apple Computer, Inc.)