While learning 6502 assembly I was trying to keep it simple and keep all the source for each program self contained, meaning no includes from external files. In resolving the issues discussed in my last post it has become increasingly difficult to manage the source in this fashion. Time to incorporate the Include files to make managing the source easier. In this post I demonstrate how Mac/65 handles the includes, and some nuances I ran into.
To include source code in one file into another file, you use the “.INCLUDE” directive. I set about changing the string input program I wrote a previous post here: String Input. First I pulled all the defines from the source and placed them in their own file named “DEFINES.LIB”. I used the extension LIB because include files must be in the tokenized SAVE format, not LIST atascii format; and because it’s a library of sorts. I also renumbered it which will make it easier to manage going forward as I add and organize it.
The new “DEFINES.LIB” file contents. I’ll show where it’s included in just a bit.
10 ; Defines 20 CIOV = $E456 30 ICHID = $0340 ;IOCB 0 S: 40 ICCOM = $0342 ;IOCB Command 50 ICBAL = $0344 ;Xfer Buffer Adr 60 ICBAH = $0345 70 ICBLL = $0348 ;Buffer Len 80 ICBLH = $0349 90 ROWCRS = 84 0100 COLCRS = 85 0110 EOL = $9B 0120 EOS = $00 0130 CLS = $7D
Next, I took all the routines I’ve been working on in the previous posts (such as printing strings, printing lines, positioning the cursor, and inputing a string), and placed them in their own file. I named this one “A8FUNCS.LIB”. Again in tokenized SAVE format. This one also needed the storage and pointer definitions included in it. Note using it means that memory locations $CC/$CD, $CD/$CE are reserved for use by the routines.
The new “A8FUNCS.LIB” file contents:
100 ; Storage & Pointers 0110 PRINDX *= *+1 0120 PRSPTR = $CB 0130 INSPTR = $CD 5000 ; Print Subroutine 5005 PRTCH 5010 ; Store Starting String Address 5015 STX PRSPTR 5020 STA PRSPTR+1 5030 LDA #$00 5040 STA PRINDX 5050 ; Set IOCB Command 5052 LDX #$00 5055 LDA #$0B 5060 STA ICCOM,X 5070 ; Set Max Buffer Length 5072 LDA #$00 5080 STA ICBLL,X 5090 STA ICBLH,X 5100 ; Call CIO to Print 5105 PRCHLP LDY PRINDX 5108 LDA (PRSPTR),Y 5115 BEQ PRCHDN 5116 JSR CIOV 5120 INC PRINDX 5130 BNE PRCHLP 5140 PRCHDN RTS 6000 ; Position Subroutine 6005 CURXY 6010 STX COLCRS 6020 STY ROWCRS 6030 RTS 7000 ; Input String 7005 INPSTR 7010 ; Store Starting String Address 7015 STX ICBAL 7020 STA ICBAH 7030 ; Store Str Address in P0 Ptr 7035 STX INSPTR 7040 STA INSPTR+1 7050 ; Set IOCB Command 7052 LDX #$00 7055 LDA #$05 ; Get Record 7060 STA ICCOM,X 7070 ; Set Max Buffer Length 7072 LDA #30 7080 STA ICBLL,X 7085 LDA #0 7090 STA ICBLH,X 7100 ; Call CIO to Print 7110 JSR CIOV 7115 ; Get # chars input 7120 LDY ICBLL 7130 ; Reduce char count by 1 7135 DEY 7140 ; Store EOS at EOL position 7145 LDA #EOS 7150 STA (INSPTR),Y 7160 RTS
A quick note about lines 100 to 130. These were previously at the head of the program. Since they are specific to the functions I’ve placed them inside the function library source. The downside to this is that my library source now requires that memory locations $CB through $CE not be used by any program the library is included in – because the library uses them. I have actually byte aligned these pairs to $CC/$CD, $CE/$CF in current source, but they were still starting at $CB when I documented this.
Now there are two files to be included in the main source. The defines come at the beginning, and the functions come at the end. The functions should come AFTER the programs main code since the assembler will set the default run address to be the beginning of the code. You don’t want the first instruction of the first function in the file to be the first line executed. This is different than other languages like C where includes typically come first. The updated “INPUTSTR.M65” file is:
10 *= $3600 20 .INCLUDE #D2:DEFINES.LIB 0300 ; Test printing 0302 TEST 0310 LDA #SCLR/256 0312 LDX #SCLR&255 0314 JSR PRTCH 0320 LDX #0 0322 LDY #0 0324 JSR CURXY 0326 LDA #SRULE/256 0328 LDX #SRULE&255 0330 JSR PRTCH 0340 LDX #0 0350 LDY #2 0360 JSR CURXY 0370 LDA #SP1/256 0380 LDX #SP1&255 0390 JSR PRTCH 0430 LDA #VSNAME/256 0440 LDX #VSNAME&255 0450 JSR INPSTR 0460 LDX #0 0470 LDY #4 0480 JSR CURXY 0490 LDA #SP2/256 0500 LDX #SP2&255 0510 JSR PRTCH 0520 LDA #VSNAME/256 0530 LDX #VSNAME&255 0540 JSR PRTCH 0550 LDA #SWELC/256 0560 LDX #SWELC&255 0570 JSR PRTCH 0580 RTS 010000 ; String Data 010001 SCLR .BYTE CLS 010002 SRULE .BYTE "-| Hello |------------------------",EOS 010005 SP1 .BYTE "Name? ",EOS 010010 SP2 .BYTE "Hello ",EOS 010020 SWELC .BYTE ", Welcome!",EOS 010050 VSNAME *= *+30 063500 .INCLUDE #D2:A8FUNCS.LIB 064000 .END
Breakdown
Now let’s break down the changes.
- Line 20: This single include replaced lines 20 through 82 in the previous source. The include pulls the file “DEFINES.LIB” into the source during assembly.
- Line 63500: This single include replaced lines 5000 through 7160 in the previous source. The include pulls the file “A8FUNCS.LIB” into the source during assembly. Remember we don’t want these executing unless called by the main program, so we pull them in last.
- Line 64000: Previously line 15000. I moved it to one of the last acceptable line numbers. This just tells the assembler there is nothing more to build.
Assembly and Testing
By using the assemblers ability to dump the object listing to a printer (or file), I see that it is assembled correctly.
0000 10 *= $3600 20 .INCLUDE #D2:DEFINES.LIB 10 ; Defines =E456 20 CIOV = $E456 =0340 30 ICHID = $0340 ;IOCB 0 S: =0342 40 ICCOM = $0342 ;IOCB Command =0344 50 ICBAL = $0344 ;Xfer Buffer Adr =0345 60 ICBAH = $0345 =0348 70 ICBLL = $0348 ;Buffer Len =0349 80 ICBLH = $0349 =0054 90 ROWCRS = 84 =0055 0100 COLCRS = 85 =009B 0110 EOL = $9B =0000 0120 EOS = $00 =007D 0130 CLS = $7D 0300 ; Test printing 3600 0302 TEST 3600 A936 0310 LDA #SCLR/256 3602 A247 0312 LDX #SCLR&255 3604 20A636 0314 JSR PRTCH 3607 A200 0320 LDX #0 3609 A000 0322 LDY #0 360B 20CE36 0324 JSR CURXY 360E A936 0326 LDA #SRULE/256 3610 A248 0328 LDX #SRULE&255 3612 20A636 0330 JSR PRTCH 3615 A200 0340 LDX #0 3617 A002 0350 LDY #2 3619 20CE36 0360 JSR CURXY 361C A936 0370 LDA #SP1/256 361E A26E 0380 LDX #SP1&255 3620 20A636 0390 JSR PRTCH 3623 A936 0430 LDA #VSNAME/256 3625 A287 0440 LDX #VSNAME&255 3627 20D336 0450 JSR INPSTR 362A A200 0460 LDX #0 362C A004 0470 LDY #4 362E 20CE36 0480 JSR CURXY 3631 A936 0490 LDA #SP2/256 3633 A275 0500 LDX #SP2&255 3635 20A636 0510 JSR PRTCH 3638 A936 0520 LDA #VSNAME/256 363A A287 0530 LDX #VSNAME&255 363C 20A636 0540 JSR PRTCH 363F A936 0550 LDA #SWELC/256 3641 A27C 0560 LDX #SWELC&255 3643 20A636 0570 JSR PRTCH 3646 60 0580 RTS 010000 ; String Data 3647 7D 010001 SCLR .BYTE CLS 3648 1204A0C8 010002 SRULE .BYTE "-| Hello |------------------------",EOS 364C E5ECECEF 3650 A0011212 3654 12121212 3658 12121212 365C 12121212 3660 12121212 3664 12121212 3668 12121212 366C 1200 366E 4E616D65 010005 SP1 .BYTE "Name? ",EOS 3672 3F2000 3675 48656C6C 010010 SP2 .BYTE "Hello ",EOS 3679 6F2000 367C 2C205765 010020 SWELC .BYTE ", Welcome!",EOS 3680 6C636F6D 3684 652100 3687 010050 VSNAME *= *+30 063500 .INCLUDE #D2:A8FUNCS.LIB 0100 ; Storage & Pointers 36A5 0110 PRINDX *= *+1 =00CB 0120 PRSPTR = $CB =00CD 0130 INSPTR = $CD 5000 ; Print Subroutine 36A6 5005 PRTCH 5010 ; Store Starting String Address 36A6 86CB 5015 STX PRSPTR 36A8 85CC 5020 STA PRSPTR+1 36AA A900 5030 LDA #$00 36AC 8DA536 5040 STA PRINDX 5050 ; Set IOCB Command 36AF A200 5052 LDX #$00 36B1 A90B 5055 LDA #$0B 36B3 9D4203 5060 STA ICCOM,X 5070 ; Set Max Buffer Length 36B6 A900 5072 LDA #$00 36B8 9D4803 5080 STA ICBLL,X 36BB 9D4903 5090 STA ICBLH,X 5100 ; Call CIO to Print 36BE ACA536 5105 PRCHLP LDY PRINDX 36C1 B1CB 5108 LDA (PRSPTR),Y 36C3 F008 5115 BEQ PRCHDN 36C5 2056E4 5116 JSR CIOV 36C8 EEA536 5120 INC PRINDX 36CB D0F1 5130 BNE PRCHLP 36CD 60 5140 PRCHDN RTS 6000 ; Position Subroutine 36CE 6005 CURXY 36CE 8655 6010 STX COLCRS 36D0 8454 6020 STY ROWCRS 36D2 60 6030 RTS 7000 ; Input String 36D3 7005 INPSTR 7010 ; Store Starting String Address 36D3 8E4403 7015 STX ICBAL 36D6 8D4503 7020 STA ICBAH 7030 ; Store Str Address in P0 Ptr 36D9 86CD 7035 STX INSPTR 36DB 85CE 7040 STA INSPTR+1 7050 ; Set IOCB Command 36DD A200 7052 LDX #$00 36DF A905 7055 LDA #$05 ; Get Record 36E1 9D4203 7060 STA ICCOM,X 7070 ; Set Max Buffer Length 36E4 A91E 7072 LDA #30 36E6 9D4803 7080 STA ICBLL,X
36E9 A900 7085 LDA #0 36EB 9D4903 7090 STA ICBLH,X 7100 ; Call CIO to Print 36EE 2056E4 7110 JSR CIOV 7115 ; Get # chars input 36F1 AC4803 7120 LDY ICBLL 7130 ; Reduce char count by 1 36F4 88 7135 DEY 7140 ; Store EOS at EOL position 36F5 A900 7145 LDA #EOS 36F7 91CD 7150 STA (INSPTR),Y 36F9 60 7160 RTS 36FA 064000 .END ASSEMBLY ERRORS: 0 28950 BYTES FREE SYMBOLS =E456 CIOV =007D CLS =0055 COLCRS 36CE CURXY =009B EOL =0000 EOS =0345 ICBAH =0344 ICBAL =0349 ICBLH =0348 ICBLL =0342 ICCOM =0340 ICHID 36D3 INPSTR =00CD INSPTR 36CD PRCHDN 36BE PRCHLP 36A5 PRINDX =00CB PRSPTR 36A6 PRTCH =0054 ROWCRS 3647 SCLR 366E SP1 3675 SP2 3648 SRULE 367C SWELC 3600 TEST 3687 VSNAME
And jumping back out to DOS and testing it reveals it worked properly just like the last time:
Now back to solving my other code problems…