Machine Language - Part VI
Machine Language Part VI
by Lyle Giese (LYLEG on DELPHI)
Last month I started explaining my SEQ file reader for you. I got up to putting the opening message on the screen.
Next we must get from you (the user) the filename of the file you want printed out. We have to start by deciding where we are going to store the filename. In BASIC, BASIC takes care of that automatically. But of course that convenience is offset by the slow speed of BASIC.
In this program I decided to use the cassette buffer at $033C. Again I will use the index and index+1 memory locations to point to the filename. I will also need another variable for the length of the filename. For that I used $FD and initialized it by putting a Zero byte in it.
I issued a call to the KERNAL CLRCHN routine next, just to make sure I was getting my input from the keyboard. Next call the GETIN routine to get the keypresses. If no keys are pressed it will return with a zero in the Accumulator, which the instruction in line 700 checks for.
Next we need to check for the delete key (we don't make typing mistakes do we?). If we haven't put any characters in the filename, it would be hard to delete one. So in line 730 & 740 we check to make sure we have characters there. Then delete the last character printed on the screen and DECrement the FLEN variable in line 770.
Next we have to check for a carriage return. The carriage return is used for two things. It will mark the end of the filename after we have typed it in. Also, we use it to exit the routine. By checking FLEN after getting a carriage return (in lines 810 & 820), if there are no characters yet, we exit by the RTS instruction in 830. Or Branch if Not Equal to the DOPEN routine.
Then if we get to line 840, we have a character we want to add to the filename. We need to first check the length of the filename before we add it to the filename. If the filename is too long we really don't want the extra characters. At this point, we branch back (line 860) to make you press the return button before trying to find the file.
But 18 characters? Filenames can be only 16 characters long! That's right, but add the prefix '0:' or '1:' for those of us that have dual drives and the max length now becomes 18.
I did not check for valid characters by limiting input to numbers and/or letters only. I only checked for the delete key and the return key. So that is something to watch out for or a feature for you to add.
Now echo the character to the screen for the you to see and jump back for more characters to NOKEY, which is at line 690.
After we have finished typing the filename, we need to go to the disk drive and open a read channel from which to get the characters. But since we are making a SEQ file reader, I added ',S,R' to the end of the filename and adjusted FLEN accordingly.
Now, open the command channel to the disk drive so we can see any disk errors that might occur. That takes us down to line 1090. That checks for errors opening the error channel.
Now we can open our read channel. We start by setting the filename for the system with the SETNAM routine. Next the SETLFS and the OPEN routine. Now we just performed the equivalent of OPEN8,8,8,"FNAM,S,R". Again go back to your Programmers Reference Guide if you have problems using the KERNAL routines. Knowing how to read that portion of the PRG is essential to programming in ML on a Commodore computer.
Note in opening the disk channel, I didn't have to remember many numbers? By using the names (which are defined at the beginning of the program), I didn't have to remember where I put the filename or where I put the file length number either. Sure makes it easy to write a program that way.
Also, what if I made a mistake in placing my filename buffer? If you didn't use a name for it, you would have to go through the program very closely changing every reference to it. But here I only have to change it in one location, in the beginning. It would be very easy to miss one spot in the program making debugging even harder!
Now that we have done that we must check to make sure that the file was there and the disk drive properly opened a channel for us. We do that two ways. The first one actually checks to make sure the computer was able to properly open the file to the drive. We do that in line 1190.
Next we will read the disk's error channel via a call to the subroutine CKERROR (line 1210). That takes us to line 4000. We open an input channel to file #$0F and read the error channel.
If the first two characters out are $30 and $30, that means an error of 00 occurred, which of course means no error. Then we would just take all of the characters out of the error channel to tidy things up and ReTurn from Subroutine.
If anything else is returned in the first two characters, something went wrong. Then we want to read and print out the error message on the screen. That's why we store the first two characters. While printing the error message, we check for the end of the message by checking for a carriage return.
Now that we found an error, we can't return to where we were in the program, because that would lead us down into the file read and print routines. We want to go back to the beginning of the program.
So in line 4140, we start by printing the disk error message on the screen to the user. Now we have think about how ML handles a subroutine. When the JSR instruction is called, the return address has to go somewhere so that we can find our place when the RTS instruction is issued. That place is the stack. The 6510 pushes the return address as two bytes onto the stack.
Since I haven't used the stack for any other storage, it should be the last two entries on the stack. So we pull two bytes off of the stack and throw them away. Now this is an advanced technique, but it is important to understand it. In this case it can be quite handy. But if I had used the stack for other storage I could not have done this that easily.
At line 4220, we start closing things up by first restoring default I/O by calling CLRCHN. And then we close the read channel and then the error channel.
Now I decided to print a short message to indicate I wanted the user to press the return key and wait for him to press the return key. Why? It is to give you a chance to read the error message before clearing the screen when we go back to the beginning of the program.
So at this point we would restart the program. But I have rambled on enough for one month. Next month we will look at how I handled the screen or printer option and some of the problems encountered doing that.
[ED. NOTE: The partial source code to accompany this article is the next file in this month's edition of the *StarBoard* Journal.]
mlcol6-s
FILE00010 ==0801==
50 sys700
75 .opt p,oo
80 ;****************
81 ;* *
82 ;* lyle giese *
83 ;* version 1.0 *
84 ;* 06/21/86 *
85 ;* *
86 ;****************
120 readst = $ffb7
130 setlfs = $ffba
140 setnam = $ffbd
150 open = $ffc0
160 close = $ffc3
170 chkin = $ffc6
180 chkout = $ffc9
190 clrchn = $ffcc
200 chrin = $ffcf
210 chrout = $ffd2
220 getin = $ffe4
230 clall = $ffe7
300 index = $fb
310 fnam = $033c;using cassette buffer to store filenames
320 flen = $fd;for length of filename
330 outfile = $fe
340 rtemp = $03ff
350 atemp = $03fe
360 pdev = $03fd
370 shflag = $028d
380 high = $03fc
390 low = $03fb
490 *= $c000
500 ;set up screen
510 start lda #$00;make border and background black
520 sta $d020
530 sta $d021
540 lda #$07;make the characters yellow
550 sta $0286
555 ;send first message to screen
560 lda #<amess
570 sta index
580 lda #>amess
590 sta index+1
600 jsr messout;send message to screen
610 ;get filename for disk read
620 lda #<fnam;load index with location for filename
630 sta index
640 lda #>fnam
650 sta index+1
660 lda #$00
670 sta flen
680 jsr clrchn;make sure screen and keyboard is active
690 nokey jsr getin;get a char from the keybuf
700 beq nokey;if nothing is in keybuf a reg will hold a zero
710 cmp #$14;ck for delete key
720 bne ckrt
730 lda flen;before deleting make sure that there
740 beq nokey;is a char in fnam
750 lda #$14;delete char on screen
760 jsr chrout
770 dec flen
780 jmp nokey
790 ckrt cmp #$0d;ck for return to either exit program
800 bne valkey;or open disk file
810 lda flen;ck number of char in keybuf
820 bne dopen;if any chars then open disk file
830 rts;if return pressed with no char in keybuf then exit routine
840 valkey ldy flen;load y reg with flen
850 cpy #$12;ck for max char allowable
860 beq nokey
870 sta (index),y;store in fnam buffer
880 jsr chrout;and echo char to screen
890 inc flen;inc flen pointer
900 jmp nokey
910 dopen ldy flen;add ,s to fnam
920 lda #","
930 sta (index),y
940 iny
950 lda #"s"
960 sta (index),y
970 iny
980 sty flen
1000 ;open channels to disk drive
1010 lda #$00;first command channel
1020 jsr setnam;no file name needed here
1030 lda #$0f;open15,8,15
1040 ldx #$08
1050 ldy #$0f
1060 jsr setlfs;open logical file
1070 jsr open;command channel opened
1080 bcc ropen;ck if open was ok
1090 jmp derror;could not open channel
1100 ropen lda flen;get length of filename
1110 ldx #<fnam
1120 ldy #>fnam
1130 jsr setnam
1140 lda #$08
1150 ldx #$08
1160 ldy #$00
1170 jsr setlfs
1180 jsr open;open8,8,8,"fnam,s"
1190 bcc ckerror
1200 jmp derror;could not open read file
1210 ckerror jsr rerror;ck error chan on disk drive
3000 derror jsr clrchn;make sure i/o initialized
3010 jsr clall;make sure all open channels are closed
3020 lda #<cmess;unable to open error chan
3030 sta index
3040 lda #>cmess
3050 sta index
3060 jsr messout
3070 rts;this error is fatal and we don't want to try again
4000 rerror ldx #$0f;read and ck disk error chan
4010 jsr chkin
4020 null jsr getin
4030 beq null
4040 sta high
4050 jsr getin
4060 sta low
4070 cmp #$30;if first two chars are
4080 bne ck;ascii zero and zero
4090 lda high;then no error occured
4100 cmp #$30;
4110 beq empty;remove the entire error message
4120 ck lda #$0d;if not then print error
4130 jsr chrout;error message
4140 lda high
4150 jsr chrout
4160 lda low
4170 repeat jsr chrout
4180 jsr getin
4190 cmp #$0d
4200 bne repeat
4210 pla:pla;remove return address from stack
4220 jsr clrchn
4230 lda #$08:jsr close
4240 lda #$0f:jsr close;close disk chan
4250 lda #<emess
4260 sta index
4270 lda #>emess
4280 sta index+1
4290 jsr messout
4300 ncr jsr getin
4310 cmp #$0d
4320 bne ncr
4330 jmp start
4340 empty jsr getin;no disk error but
4350 cmp #$0d;should still read the entire
4360 bne empty;message out of error chan
4370 jsr clrchn
4380 rts
5000 messout ldy #$00;send message to screen
5010 lpy lda (index),y;index points to start of message
5020 beq end;and it must end with a zero byte
5030 jsr chrout
5040 iny
5050 bne lpy;has y looped around to zero
5060 inc index+1;if it has we must inc
5070 jmp lpy;the hi byte of our index address
5080 end rts
7000 amess =*;opening message
7010 .byte $93;clear/home
7020 .byte $0d,$0d,$0d,$0e
7030 .asc " SEQUENTIAL FILE READER"
7040 .byte $0d,$0d
7050 .asc " By Lyle Giese"
7060 .byte $0d,$0d
7070 .asc " Use the Shift and Shift Lock to pause
7080 .byte $0d
7090 .asc " listing. Press the Ctrl and Shift to"
7100 .byte $0d
7110 .asc " exit current file. To exit just"
7120 .byte $0d
7130 .asc " hit (rvon)return(rvof) at the Filename > prompt."
7140 .byte $0d,$0d
7150 .asc " Filename > "
7160 .byte $00
7300 cmess =*;unable to open error channel to disk drive
7310 .byte $0d
7320 .asc " Unable to open error channel to disk
7330 .byte $0d
7340 .asc " drive. Try again later!"
7350 .byte $0d,$00
7400 dmess =*;disk error trying to open read channel
7410 .byte $0d
7420 .asc " (rvon) DISK ERROR (rvof)"
7430 .byte $0d,$00
7500 emess =*;end of file
7510 .byte $0d
7520 .asc " (rvon) Press return to continue (rvof) "
7530 .byte $0d,$00