Classic Computer Magazine Archive COMPUTE! ISSUE 48 / MAY 1984 / PAGE 162

MACHINE LANGUAGE

Jim Butterfield, Associate Editor

A Program Critique

Part 2

This month we continue with comments on Bud Rasmussen's program to copy files on the Commodore 64 with a single disk unit. At this point the program has obtained a filename. The filename is kept in two forms: the short form ("FILENAME") and the longer form for writing ("FILENAMES, W"). We will use the short form when we open the file for reading.

In this session, we'll track the mnemonics that open the error channel, initialize the disk, and input the file into RAM memory.

             ;DISK I/O ROUTINE
             ;
             ;
C18A A9 00  DIOR LDA #0  ;CLEAR
C18C 8D 60 03   STA ISF  INPUTSTATFLAG
C18F 8D 61 03   STA IEC  INPUTERR CODE
             ;

This is probably overkill. The flags should be zeroed close to where they are used, if necessary.

C192 A2 22      LDX #IPBML ;PRINT
C194 A0 C1      LDY #>IPBM ;'INPUT
C196 A9 AD      LDA #<IPBM ;PHASE BEGUN'
C198 20 75 C1   JSR PR     ;MSG
    ;

A Friendly Message

In keeping with the friendly style, a message is printed telling the user what's going on. We'll find the message in-line very shortly.

C19B A9 0F      LDA #15    ;SET
C19D A2 08      LDX #8     ;COMMAND
C19F A0 0F      LDY #15    ;CANNEL
C1A1 20 BA FF   JSR SETLFS
C1A4 20 C0 FF   JSR OPEN   ;OPEN COMMD CH
    ;

The command channel is opened. This is quite important: We'll get all our error messages from this channel. It should always be opened before other disk activities are started.

C1A7 20 3F C4  JSR ID   ;INITDISK
C1AA 4C CF C1  JMP SNI  ;GOTO SET NAME INPUT

We send the initialize command to the disk over the command channel. This is not vital, but a good precaution. It's a subroutine within the program; we'll meet it much later.

We need to jump over the message to continue with the program. Here's the message:

               ;
               ;
               ; INPUT PHASE BEGUN MESSAGE
               ;
               ;
C1AD 0D 0D 12 IPBM  .BYTE$0D,$0D,$12
C1B0 0D 0D          .ASC"***INPUT PHASE BEGUN***"
C1CD 0D 0D          .BYTE$0D,$0D
             IPBMML = *-IPBM
              ;

Now we're ready to open the input file in preparation for reading it. We use the short name, since the last four characters (,S,W) aren't needed or wanted for an input file.

               ;
               ;OPEN INPUT
               ;
               ;
C1CF AD AA 02 SNI  LDA IFNL      ;LOAD INPUT
                                 FNAMELEN
C1D2 A2 40         LDX #<FNA     ;LOADFILENAME LO
C1D2 A0 03         LDY #>FNA     ;LOADFILENAME HI
C1D6 20 BD FF      JSR SETNAM
C1D6 20 BD FF      JSR SETNAM
    ;

We're doing things backwards from the equivalent BASIC coding. If we code OPEN 2,8,2,"HOTDOG" in BASIC, we've now placed the "HOTDOG" part of the command. Now let's put in the 2,8,2 sequence:

             ;
             ;SET LOGICAL FILE (INPUT)
             ;
C1D9 A9 02  SLFI LDA #2   ;LOAD LOGICAL
                           FILE #
C1DB A2 08 08    LDX #8    ;LOADDEVICE
		          ;ADDRESS
C1DD A0 02     LDY #2	  ;LOADSEC.
		           ADDRESS
C1DF 20 BA FF  JSR SERLFS
	;	

And finally, the OPEN itself:

	       ;;
	       ;;OPEN FILE (INPUT)
	       ;;
	       ;;
C1E2 20 C0 FF OFI JSR OPEN
	       ;

Error Check

Now we'll check to see if the OPEN took place without error:

C1E5 A5 90	LDA IOS  ;TEST
C1E7 F0 0B	BEQ OCI  ;STATUS
C1E9 8D 60 03   STA ISF  ;STORE STATUS
			  FLAG
C1EC A9 01	LDA #1	 ;SET/STORE
C1EE 8D 61 03   STA IEC  ERROR CODE
C1F1 4C 4F C2   JMP IE   ;INPUT ERROR
	;

Location $90—called IOS here—is the familiar BASIC ST flag. If it's zero, we are OK and can proceed to read the file. If not, we must advise, abort, or take other appropriate action.

But this flag is not enough. ST, or hex 90, tells us only if the transfer of information (in this case, filename) has been passed to the disk correctly. After the information gets to the disk, there may be other problems.

If the file does not exist, or for any other reason cannot be opened, the disk will know there's an error; but the computer will not. The computer must ask the disk to deliver information on possible errors over its command channel. The command channel is open and ready to receive this data (we opened 15, remember), but we must ask for it.

To do the job right, we must think about coding along the following lines:

LDX #15   ;command channel
JSR FFC6  ;input
JSR $FFE4 ;get a character
PHA	  ;stash it
JSR FFCC  ;close chaneel
PHA	  ;unstash character
CMP #$30  ;is it 0?
BNE ERROR ;nope, we have problem

A Better Way

The above is minimum coding. It would be better to create a more elaborate subroutine which brings in the whole message from the error channel and stores it in memory. (The message would end with $0D, the Return character.) Then we could check the first character for $30 (ASCII zero, start of the OK message); if not, we'd be able to print the whole error message.

Here comes the coding for a good OPEN:

	    ;OPEN CHANNEL (INPUT)
	    ;
	    ;
C1F4 A2 02  OCI  LDX #2    ;OPEN
C1F6 20 C6 FF    JSR CHKIN ;CHANNEL #2
	    ;
C1F9 A5 90	 LDA IOS   ;TEST
1FB F0 0B	 BEQ LBSA  ;STATUS
C1FD 8D 60 03	 STA ISF   ;STORE STATUS
			    FLAG
C200 A9 02       LDA #2    ;SET/STORE
C202 8D 61 03    STA IEC   ;ERROR CODE
C205 4C 4F C2    JMP IE   ;INPUT ERROR

I wish the comments said "connect channel" rather than "open channel." The OPEN (as we know it in BASIC) has been performed successfully. Now, we're establishing a connection to the input file preparatory to reading.

	     ;
	     ;LOAD BUFFER START ADDRESS
	     ;
	     ;
C208 A9 00  LBSA LDA #0   ;LOAD BFR
C20A 85 FB	 STA BAL  ;ADDRLO
C20C AD 3D C4    STA BAH  ;ADDRHI
	    ;
C211 A0 00	 LDY #0   ;BUFFER INDEX = 0
	    ;

Just before reading, we set up the memory address into which we will start to read. The low part of the address is zero; the high part is stored as a constant in the program (SP undoubtedly stands for Start Page). Immediate addressing could be used to set the start page if preferred.

	       ;
 	       ;INPUT LOOP
	       ;
	       ;
C213 20 CF FF IL JSR CHRIN   ;GET CHARACTER
C216 91 FB	 STA (BAL),Y ;STORE CHARACTER
C218 E6 FB	 INC BAL     ;INCR LO BYTE
C21A D0 0C	 BNE TIS     ;IF NOT 0, TEST STAT
C21C E6 FC	 INC BAH     ;INCR HI BYTE
C220 CD 3E C4	 CMP EP      ;CHECK FOR END
			      ADDR
C223 90 03	 BCC TIS     ;IF LO, TEST STAT
C225 4C 3B C2    JMP DSP

CHRIN Or CHRGET

Rasmussen uses the CHRIN routine ($FFCF) to get from the file. I prefer CHRGET ($FFE4), but the difference is minor with files. Either call gets from the file rather than keyboard/screen because we have switched the input channel with our call to CHKIN ($FFC6).

Some programmers would prefer to step the Y register through its range rather than change the indirect address each time. In principle, the Y register technique is faster; but in this case, it's doubtful that the speed difference could be observed. Timing of this whole section is governed almost totally by disk speed.

The program checks carefully to make sure that the data does not overrun the memory space available.

               ;
            ; TEST INPUT STATUS
               ;
               ;
C228 A5 90 TIS LDA IOS       ;LOAD STATUS
C22A F0 E7	  BEQ IL	;IF 0, CARRY ON
C22C C9 40	  CMP #EOFI	;TEST FOR
C22E F0 23	  BEQ EOF	;EOF
C230 8D 60 03  STA ISF	;STORESTATUS
				 FLAG
C233 A9 03	  LDA #3	;SET/STORE
C235 8D 61 03  STA IEC	;ERROR CODE
C238 4C 4F C2  JMP IE	;INPUTERROR

Again we test the ST status byte (IOS); in this case, we're primarily interested in an end-of-file signal which would be flagged by a value of hex 40 (decimal 64) in ST.

Once again, the error routines are quite elaborate. It's my opinion that there is little need to check the disk error channel during the read phase; error notices will wait until we ask for them at end of file.

Opening The File

If we run out of memory, we come to DSP:

         ;
         ;DECREMENT START PG BY HEX 10
         ;AND TRY AGAIN,
         ;TO GIVE YOU 16 MORE BLKS.
         ;
         ;
C23B 38	       DSP SEC
C23C AD 3D C4	   LDA SP	;LOAD START PG
C23F E9 10 SBC	   #H10	;SUBT HEX 10
C241 8D 3D C4	   STA  SP	;STORE IT BACK
C244 20 CC FF	   JSR CLRCHN	;CLEAR CHANNEL
C247 A9 02	   LDA #2	;SET CH2
C249 20 C3 FF	   JSR CLOSE	;FOR CLOSE
C24C 4C CF C1	   JMP SNI	;START ALL OVER

I'm not sure what is going on here. The coding intention is this: If it doesn't fit, allocate an extra 4K and try again.

An Endless Loop

This is puzzling. If the 4K was available, why not make it available in the first read and save the trouble?

There's also a pitfall here. Suppose we allocate the extra 4K, and the program still doesn't fit into memory. We'll end up in an endless loop, since we will come back to DSP, do it again, and so on, and so on.

I'd prefer to allocate as much memory as possible right away, and quit if the program doesn't fit.

              ;
              ;INPUT ERROR
              ;
              ;
C24F 20 E7 FF IE	JSR CLALL	;CLOSE ALL FILES
C252 00	        BRK
              ;

This is a programmer's error termination. The program will stop and break to the monitor, if there is a monitor in place. The programmer can then examine memory locations to see what the trouble is.

If there is not a monitor in the machine, the program will terminate with a READY statement and no other explanation.

Extra Work

For general use, the program would benefit from additional work in this area so that the user would see a meaningful message. This is almost out of character: The messages are so well presented in other parts of the program that their absence here is very noticeable indeed.

                ;
                ;END OF FILE
                ;
                ;
C253	          EOF = *
C253 A5 FB	      LDA BAL	;SAVE
C255 85 FD	      STA EAL	;LAST
C257 A5 FC	      LDA BAH	;ADDRESS
C259 85 FE	      STA EAH	;OF FILE
C25B 20 CC FF      JSR CLRCHN;CLEAR CHANNEL
               ;

Wrapping It Up

The end address (plus one, of course) is stored away, and the file disconnected. I would check the disk error channel at this point. Any errors that may have accumulated during the input phase will be waiting.

Now we may close the file and print an advisory message:

C25E A9 02	           LDA #2          ;SET CH2
C260 20 C3 FF	   JSR CLOSE       ;FOR CLOSE
                   ;
C263 A2 88              LDX #IPFML      ;PRINT
C265 A0 C2              LDY #>IPFM   ;'INPUT
C267 A9 6F              LDA #<IPFM   ;PHASE FINISHED'
C269 20 75 C1           JSR PR	        ;MSG
                   ;
C26C 4C F7 C2	   JMP SOP	        ;GOTOSTARTOUT PHASE
                  ;
                  ;
                  ;INPUT PHASE FINISHED MESSAGE
                  ;
                  ;
C26F 12	   IPFM    .BYTE$12
C270 20 20 49	   .ASC" INPUT PHASE FINISHED."
C28F 0D 0D 12	   .BYTE$0D, $0D, $12
C292 20 20 52	   .ASC" REMOVE INPUT DISKETTE."
C2B1 0D 0D 12	   .BYTE$0D, $0D, $12
C2B4 20 20 49	   .ASC" INSERT OUTPUT DISKETTE."
C2D3 0D 0D 12	   .BYTE$0D, $0D, $12
C2D6 20 20 50	   .ASC" PRESS RETURN KEY WHEN READY."
C2F5 0D 0D              .BYTE$0D, $0D
C2F7	        IPFML = *-IPFM
              ;
              ;
              ;STARTOUTPUT PHASE
              ;
C2F7         SOP =  *

The input phase is complete. Next time, we'll take a look at output.