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.