From: "French Luser" Newsgroups: comp.os.cpm References: <419f187a$0$7241$8fcfb975@news.wanadoo.fr> Subject: Re: AFTER8 File Format Date: Wed, 24 Nov 2004 10:51:21 +0100 X-Priority: 3 X-MSMail-Priority: Normal X-Newsreader: Microsoft Outlook Express 6.00.2800.1158 X-MimeOLE: Produced By Microsoft MimeOLE V6.00.2800.1165 Lines: 1007 Message-ID: <41a45904$0$9082$8fcfb975@news.wanadoo.fr> Organization: les newsgroups par Wanadoo NNTP-Posting-Host: 81.248.43.163 X-Trace: 1101289733 news.wanadoo.fr 9082 81.248.43.163:13514 X-Complaints-To: abuse@wanadoo.fr AFTER8B.TXT by Emmanuel ROCHE ----------- Looking after 8 segments: a demo, at last! Ok. So, last time, I published a BASIC program (named AFTER8.BAS) used to concatenate 2 CP/M-86 (Plus) ComManD files with 4 segments (and relocating the Auxiliary segments in the resulting CMD file Header Record), producing a file with 8 segments. But this was (unfortunately) pretty hypothetical, with 2 CMD files filled with ASCII characters... (Even if it proved that it could be automated). Since I am a programmer, I decided that a good demo was needed. Just by coincidence, a few days ago, I published a SYSVAR utility (displaying the contents of the "System Variables" of CP/M-86 Plus) in the comp.os.cpm Newsgroup. Looking at it, I wondered if such a small utility could not be a good basis for an AFTER8 demo? So, I first made a version of this utility with the "logic" of the program in the Extra segment, and still using the subroutines in the Code segment, and some Data in the Data segment. After a few massages, it finally run. So, it could be the basis for the demo. Time to think to the AFTER8 file format. So, those files have 8 segments: Code, Data, Extra, Stack, Auxiliary #1, Auxiliary #2, Auxiliary #3, and Auxiliary #4. In any program, there is usually some subroutines needed. So, I deciced to put them in the Code segment. (Before going any further, I must also explain that I am also working on the DOIT Compiler (a compiler written in Turbo Pascal Version 3.01A, running under CP/M 2.2, CP/M-86, and MS-DOS). So, when I say: "the program", I was also thinking: "the Run-Time System of any Compiler"... Once DOIT will be done, I plan to produce a DIY Compiler, with some features that I would like to have under CP/M-86 Plus. Back to AFTER8.) Some subroutines in the Code segment usually need to display some strings on the console. So, the Data segment is used to store those. Just to show that the following demo has an Extra segment (you will remember that, previously, this Extra segment contained the "logic" of a program), I simply filled it with a string and some ASCII characters. Regarding the Stack segment, so far none of my CMD files for CP/M-86 Plus has needed one. But, for the demo, I added a string and some ASCII characters to fill it. Now, we have a conventional CP/M-86 Compact Memory Model ComManD file, with the 4 usual segments: Code, Data, Extra, and Stack. But where is the program? At the beginning, I just wanted to present you a demo with a little program in the Auxiliary #1 segment. But, upon thinking about it, it seemed that a demo involving the 4 Auxiliary segments would be more demonstrative. Since I was wondering where all this would lead me, I decided to "keep it simple, stupid": do it the simplest way possible. So, I simply retyped the "logic" of the SYSVAR program 4 times, one in each Auxiliary segment. Now, how to know which Auxiliary segment is being processed? So, I added a "X1", "X2", etc string after the title. Now, let us talk about RTS: most compilers have a RTS. Some are external (CRUN for CBASIC), but most are integral into the command files produced by the compiler. Turbo Pascal's RTS is in the first 2K of any command file produced. The big problem, for the compiler writer, is the details of the RTS, and how to generate a command file. In this case, I decided that the first ComManD file processed by AFTER8.BAS would contain the RTS of a future compiler. That's mean that the RTS is a totally separate CMD file. Since this future compiler don't exist yet, that meant that I had to produce by hand a demo "program" (as explained above, I chose to run 4 times the "logic" of SYSVAR). Note that (as explained in my previous message), there are 4 Auxiliary segments, but only one Code segment... Since RTS are typically smaller than the "program" produced by any compiler, it follows that it is more logical to use the Code segment to store the RTS, and the 4 Auxiliary segments to store the program. At the moment, I am thinking to load those Auxiliary segments without gaps between them (SID-86 shows that GENCMD fills the "slack bytes" between segments with 00h -- this could be a useful feature that the RTS could exploit, for example by excluding any "opcode" of its program to run to be 00h. If the RTS was to encounter a 00h byte in the "program", it would simply loop to the next "opcode" -- until the end of the Auxiliary segments), but the demo was made with 4 separate Auxiliary segments. This obliged me to introduce 3 commands to update the Auxiliary segments, in this particular case. Whew! So much talking for a down-to-heart demo! Time to show something: here is what is displayed on screen when the demo ComMand file produced by AFTER8 is run (the previous message showed an AFTER8 session): A>demo System Variables (X1) ---------------- Console Width: 80 Console Page Length: 24 Console Page Mode: OFF System Ticks per Second: 60 Temporary File Drive: @ Date: 04F3 Time: 21:26:06 8087 Present: TRUE Program ID: 00 Drive Search Chain: @ In the Background: FALSE Number of Running Processes: 01 Foreground Ratio: 16 Press ENTER to Continue System Variables (X2) ---------------- Console Width: 80 Console Page Length: 24 Console Page Mode: OFF System Ticks per Second: 60 Temporary File Drive: @ Date: 04F3 Time: 21:26:07 8087 Present: TRUE Program ID: 00 Drive Search Chain: @ In the Background: FALSE Number of Running Processes: 01 Foreground Ratio: 16 Press ENTER to Continue System Variables (X3) ---------------- Console Width: 80 Console Page Length: 24 Console Page Mode: OFF System Ticks per Second: 60 Temporary File Drive: @ Date: 04F3 Time: 21:26:09 8087 Present: TRUE Program ID: 00 Drive Search Chain: @ In the Background: FALSE Number of Running Processes: 01 Foreground Ratio: 16 Press ENTER to Continue System Variables (X4) ---------------- Console Width: 80 Console Page Length: 24 Console Page Mode: OFF System Ticks per Second: 60 Temporary File Drive: @ Date: 04F3 Time: 21:26:11 8087 Present: TRUE Program ID: 00 Drive Search Chain: @ In the Background: FALSE Number of Running Processes: 01 Foreground Ratio: 16 So, as you can see, this is the output of my SYSVAR utility, but with the titles lines saying that there were 4 copies in the 4 Auxiliary segments. Let us now see what constitute the "program" (the second CMD file processed by AFTER8): ; DIYPGM.A86 ; ---------- ; ; CP/M-86 Plus -- DIYPGM.CMD ; ; Programmed by: ; ; Mr Emmanuel ROCHE ; Chemin de Boisrond ; 17430 Tonnay-Charente ; FRANCE ; ; Demo PGM for DIY Compiler ; (This one done by hand.) ; ; Generation: ; A>ASM DIYPGM! GENCMD DIYPGM ; ;-------------------------------- ; AFTER8 Memory Model. ; (Compact Memory model without ORG 0100h after DSEG.) ; CSEG ; Will become X1 Segment DSEG ; Will become X2 Segment ESEG ; Will become X3 Segment SSEG ; Will become X4 Segment ; ;--------------------------------- ; Equates of Subroutines in DIYRTS. ; ; Note that we could use other names, ; and that this demo does NOT check ; the validity of the number... ; Chk_CPMP EQU 0 Bak_CPMP EQU 1 Pr_Seg_ES EQU 2 Pr_Wor_AX EQU 3 Pr_Byt_AL EQU 4 Pr_Nib_AL EQU 5 Con_Out_DL EQU 6 Ret_Opc EQU 7 Txt_Eol EQU 8 New_Line EQU 9 Eol_Txt EQU 10 Txt EQU 11 Pr_Dec_AL EQU 12 Pr_Dec_BX EQU 13 Get_SCB EQU 14 Dis_Width EQU 15 Dis_Length EQU 16 Dis_Mode EQU 17 Dis_Ticks EQU 18 Dis_Temp EQU 19 Dis_Date EQU 20 Dis_Time EQU 21 Dis_8087 EQU 22 Dis_ProgID EQU 23 Dis_DsChain EQU 24 Dis_Back EQU 25 Dis_NRproc EQU 26 Dis_Fore EQU 27 Dis_Penter EQU 28 Set_Segment_X2 EQU 29 Set_Segment_X3 EQU 30 Set_Segment_X4 EQU 31 ; ;-------------------------------- CSEG $ ;-------------------------------- DB ' CP/M-86 Plus -- X1 Segment ' Program_X1: DB Chk_CPMP, 'Requires CP/M-86 Plus to run$' Doit_X1: DB Eol_Txt, 'System Variables (X1)$' DB Eol_Txt, '----------------$' DB Dis_Width, 'Console Width: $' DB Dis_Length, 'Console Page Length: $' DB Dis_Mode, 'Console Page Mode: $' DB Dis_Ticks, 'System Ticks per Second: $' DB Dis_Temp, 'Temporary File Drive: $' DB Dis_Date, 'Date: $' DB Dis_Time, 'Time: $' DB Dis_8087, '8087 Present: $' DB Dis_ProgID, 'Program ID: $' DB Dis_DsChain, 'Drive Search Chain: $' DB Dis_Back, 'In the Background: $' DB Dis_NRproc, 'Number of Running Processes: $' DB Dis_Fore, 'Foreground Ratio: $' DB Dis_Penter DB Set_Segment_X2 ; ;-------------------------------- DSEG $ ;-------------------------------- DB ' CP/M-86 Plus -- X2 Segment ' Program_X2: DB Eol_Txt, 'System Variables (X2)$' DB Eol_Txt, '----------------$' DB Dis_Width, 'Console Width: $' DB Dis_Length, 'Console Page Length: $' DB Dis_Mode, 'Console Page Mode: $' DB Dis_Ticks, 'System Ticks per Second: $' DB Dis_Temp, 'Temporary File Drive: $' DB Dis_Date, 'Date: $' DB Dis_Time, 'Time: $' DB Dis_8087, '8087 Present: $' DB Dis_ProgID, 'Program ID: $' DB Dis_DsChain, 'Drive Search Chain: $' DB Dis_Back, 'In the Background: $' DB Dis_NRproc, 'Number of Running Processes: $' DB Dis_Fore, 'Foreground Ratio: $' DB Dis_Penter DB Set_Segment_X3 ; ;-------------------------------- ESEG $ ;-------------------------------- DB ' CP/M-86 Plus -- X3 Segment ' Program_X3: DB Eol_Txt, 'System Variables (X3)$' DB Eol_Txt, '----------------$' DB Dis_Width, 'Console Width: $' DB Dis_Length, 'Console Page Length: $' DB Dis_Mode, 'Console Page Mode: $' DB Dis_Ticks, 'System Ticks per Second: $' DB Dis_Temp, 'Temporary File Drive: $' DB Dis_Date, 'Date: $' DB Dis_Time, 'Time: $' DB Dis_8087, '8087 Present: $' DB Dis_ProgID, 'Program ID: $' DB Dis_DsChain, 'Drive Search Chain: $' DB Dis_Back, 'In the Background: $' DB Dis_NRproc, 'Number of Running Processes: $' DB Dis_Fore, 'Foreground Ratio: $' DB Dis_Penter DB Set_Segment_X4 ; ;-------------------------------- SSEG $ ;-------------------------------- DB ' CP/M-86 Plus -- X4 Segment ' Program_X4: DB Eol_Txt, 'System Variables (X4)$' DB Eol_Txt, '----------------$' DB Dis_Width, 'Console Width: $' DB Dis_Length, 'Console Page Length: $' DB Dis_Mode, 'Console Page Mode: $' DB Dis_Ticks, 'System Ticks per Second: $' DB Dis_Temp, 'Temporary File Drive: $' DB Dis_Date, 'Date: $' DB Dis_Time, 'Time: $' DB Dis_8087, '8087 Present: $' DB Dis_ProgID, 'Program ID: $' DB Dis_DsChain, 'Drive Search Chain: $' DB Dis_Back, 'In the Background: $' DB Dis_NRproc, 'Number of Running Processes: $' DB Dis_Fore, 'Foreground Ratio: $' DB New_Line DB Bak_CPMP ; ;-------------------------------- ; END No doubt about it: this program contains absolutely no Code subroutines. It is just 4 copies of a program expressed as byte opcodes, with the strings corresponding to each "command". So, if the "program" is so simple, how is the RTS? ; DIYRTS.A86 ; ---------- ; ; CP/M-86 Plus -- DIYRTS.CMD ; ; Programmed by: ; ; Mr Emmanuel ROCHE ; Chemin de Boisrond ; 17430 Tonnay-Charente ; FRANCE ; ; Demo RTS for DIY Compiler ; (Using AFTER8 Segments.) ; ; Generation: ; A>ASM DIYRTS! GENCMD DIYRTS ; ;-------------------------------- ; Compact Memory model. ; CSEG DSEG ORG 001Bh X1base DW 0000h ; X1 Segment in Base Page ORG 0021h X2base DW 0000h ; X2 Segment in Base Page ORG 0027h X3base DW 0000h ; X3 Segment in Base Page ORG 002Dh X4base DW 0000h ; X4 Segment in Base Page ORG 0100h ESEG SSEG ; ;--------------------------------- DSEG $ ;--------------------------------- ; Who? ; DB ' ' DB ' _ _ ' DB ' ( o o ) ' DB '+-oOOo-()-oOOo-+' DB '| |' DB '|Emmanuel ROCHE|' DB '| |' DB '| was here... |' DB '| |' DB '| .oooO Oooo. |' DB '+-( )--( )-+' DB ' \ ( ) / ' DB ' \_) (_/ ' DB ' ' DB ' ' DB ' ' ; ;-------------------------------- CSEG $ ;-------------------------------- ; We use BP to get command bytes (from X1), ; and DI to execute those commands (in CS). ; MOV AX, X1base ; Get Segment of X1 MOV ES, AX ; Xseg_Loop: ; (Demo) MOV BP, 0020h ; Init Offset of X1 (Demo) Main_Loop: MOV AL, ES:[BP] ; CBW ; Make it a word SHL AX, 1 ; * 2, as it is a word MOV DI, AX ; Make it an address CALL Sub_Tab [DI] ; = ON xx GOSUB INC BP ; Increment X1:BP JMPS Main_Loop ; = GOTO Main_Loop ; ;-------------------------------- DSEG $ ;-------------------------------- Sub_Tab: ; Table of Subroutines ; Chk_CPMP EQU 0! DW Sub_Chk_CPMP ; Bak_CPMP EQU 1! DW Sub_Bak_CPMP ; Pr_Seg_ES EQU 2! DW Sub_Pr_Seg_ES ; Pr_Wor_AX EQU 3! DW Sub_Pr_Wor_AX ; Pr_Byt_AL EQU 4! DW Sub_Pr_Byt_AL ; Pr_Nib_AL EQU 5! DW Sub_Pr_Nib_AL ; Con_Out_DL EQU 6! DW Sub_Con_Out_DL ; Ret_Opc EQU 7! DW Sub_Ret_Opc ; Txt_Eol EQU 8! DW Sub_Txt_Eol ; New_Line EQU 9! DW Sub_Eol ; Eol_Txt EQU 10! DW Sub_Eol_Txt ; Txt EQU 11! DW Sub_Txt ; Pr_Dec_AL EQU 12! DW Sub_Pr_Dec_AL ; Pr_Dec_BX EQU 13! DW Sub_Pr_Dec_BX ; Get_SCB EQU 14! DW Sub_Get_SCB ; Dis_Width EQU 15! DW Sub_Dis_Width ; Dis_Length EQU 16! DW Sub_Dis_Length ; Dis_Mode EQU 17! DW Sub_Dis_Mode ; Dis_Ticks EQU 18! DW Sub_Dis_Ticks ; Dis_Temp EQU 19! DW Sub_Dis_Temp ; Dis_Date EQU 20! DW Sub_Dis_Date ; Dis_Time EQU 21! DW Sub_Dis_Time ; Dis_8087 EQU 22! DW Sub_Dis_8087 ; Dis_ProgID EQU 23! DW Sub_Dis_ProgID ; Dis_DsChain EQU 24! DW Sub_Dis_DsChain ; Dis_Back EQU 25! DW Sub_Dis_Back ; Dis_NRproc EQU 26! DW Sub_Dis_NRproc ; Dis_Fore EQU 27! DW Sub_Dis_Fore ; Dis_Penter EQU 28! DW Sub_Dis_Penter ; Set_Segment_X2 EQU 29! DW Sub_Set_Segment_X2 ; Set_Segment_X3 EQU 30! DW Sub_Set_Segment_X3 ; Set_Segment_X4 EQU 31! DW Sub_Set_Segment_X4 ; ;-------------------------------- Start of Subroutines CSEG $ ;-------------------------------- 0 Sub_Chk_CPMP: ; MOV CL, 12! INT 224 CMP AX, 1031h! JNE Abort MOV BP, 003Eh! JMP Main_Loop ; (BP = Demo) ; ; Print "Requires CP/M-86 Plus to run" on console. ; Abort: CALL Sub_Eol_Txt CALL Sub_Eol ; ;................................ 1 Sub_Bak_CPMP: ; ; Returns to CP/M-86 Plus. ; MOV CL, 0! INT 224 ; ;-------------------------------- 2 Sub_Pr_Seg_ES: ; ; Print "Seg:Off" from ES:BX. ; PUSH BX MOV AX, ES! CALL Sub_Pr_Wor_AX MOV DL, ':'! CALL Sub_Con_Out_DL POP AX ; ;................................ 3 Sub_Pr_Wor_AX: ; ; Print AX in hex ("1234"). ; PUSH AX MOV AL, AH! CALL Sub_Pr_Byt_AL POP AX ; ;................................ 4 Sub_Pr_Byt_AL: ; ; Print AL in hex ("12"). ; PUSH AX MOV CL, 4! SHR AL, CL! CALL Sub_Pr_Nib_AL POP AX ; ;................................ 5 Sub_Pr_Nib_AL: ; ; Intel trick circa 1972-1973? ; AND AL, 0Fh ADD AL, 90h DAA ADC AL, 40h DAA MOV DL, AL ; ;................................ 6 Sub_Con_Out_DL: ; ; "Cooked mode" output one char to console. ; PUSH BX MOV CL, 2! INT 224 POP BX ; ;................................ 7 Sub_Ret_Opc: ; ; After much thinking, I finally make this RET a subroutine... ; (It will be used by keywords producing nothing.) ; RET ; ;-------------------------------- 8 Sub_Txt_Eol: ; ; In : String$ ; Out: Sends String, cr, lf to console. ; CALL Sub_Txt ; ;................................ 9 Sub_Eol: ; ; In : None ; Out: Sends cr, lf to console. ; PUSH BX MOV DL, 0Dh! MOV CL, 2! INT 224 MOV DL, 0Ah! MOV CL, 2! INT 224 POP BX RET ; ;-------------------------------- 10 Sub_Eol_Txt: ; ; In : String$ ; Out: Sends cr, lf, String to console. ; CALL Sub_Eol ; ;................................ 11 Sub_Txt: ; ; In : String$ ; Out: Sends String to console. ; (One character at a time, to update BP.) ; INC BP! MOV AL, ES:[BP] CMP AL, '$'! JE Sub_Ret_Opc MOV DL, AL! CALL Sub_Con_Out_DL JMPS Sub_Txt ; ;-------------------------------- 12 Sub_Pr_Dec_AL: ; ; Print Dec number 00-99 from AL. ; DSEG $ ; num DB 'Ok' ; CSEG $ ; XOR AH, AH AAM ADD AX, 3030h MOV WORD PTR num, AX MOV DL, BYTE PTR num+1! CALL Sub_Con_Out_DL MOV DL, BYTE PTR num+0! JMP Sub_Con_Out_DL ; ;-------------------------------- 13 Sub_Pr_Dec_BX: ; ; Print binary number 0-65535 from BX. ; PUSH BX ; Save value MOV CX, 10000! CALL Adjust MOV CX, 1000! CALL Adjust MOV CX, 100! CALL Adjust MOV CX, 10! CALL Adjust ADD DL, '0'! CALL Sub_Con_Out_DL MOV BYTE PTR SuLeZe, 00h ; = Suppress Leading Zeroes (Default) PUSH BX ; Restore value RET ; Done ; DSEG $ ; SuLeZe DB 00h ; 00h (false) = Suppress Leading Zeroes ; 0FFh (true ) = Display Leading Zeroes ; CSEG $ ; Adjust: MOV DH, 0FFh ; = -1 Adjust_1: SUB BX, CX ; INC DH ; JNC Adjust_1 ; Repeat if no Borrow ADD BX, CX ; Add divisor MOV DL, DH ; PUSH BX ; MOV BX, OFFSET SuLeZe CMP [BX], AL ; Suppress Leading Zeroes? JNE Adjust_2 ; MOV DL, ' ' ; CALL Sub_Con_Out_DL ; JMPS Adjust_3 ; Adjust_2: MOV BYTE PTR SuLeZe, 0FFh ; Display Leading Zeroes ADD DL, '0' ; CALL Sub_Con_Out_DL ; Adjust_3: POP BX ; RET ; Done ; ;-------------------------------- 14 Sub_Get_SCB: ; ; In : Number of SCB variables to get. ; Out: SCBPB filled. ; PUSH AX! PUSH BX! PUSH CX! PUSH DX MOV DX, OFFSET SCBnum! MOV CL, 49! INT 224 POP DX! POP CX! POP BX! POP AX RET ; DSEG $ ; ; System Control Block Parameter Block ; SCBnum DB 00h ; Number of SCB variables DB 00h ; 00=Get (FF=Set) SCBvar DB 00h ; Get SCB variable here ; ; Additional values returned, depending upon SCBnum. ; date2 DB 00h ; Date 2nd byte hour DB 00h ; Time: Hours min DB 00h ; Time: Minutes sec DB 00h ; Time: Seconds ; CSEG $ ; ;-------------------------------- 15 Sub_Dis_Width: ; ; In : String$ ; Out: Sends cr, lf, "Console Width: 80" to console. ; CALL Sub_Eol_Txt MOV SCBnum, 0! CALL Sub_Get_SCB MOV AL, SCBvar! INC AL! JMP Sub_Pr_Dec_AL ; ;-------------------------------- 16 Sub_Dis_Length: ; ; In : String$ ; Out: Sends cr, lf, "Console Page Length: 24" to console. ; CALL Sub_Eol_Txt MOV SCBnum, 1! CALL Sub_Get_SCB MOV AL, SCBvar! INC AL! JMP Sub_Pr_Dec_AL ; ;-------------------------------- 18 Sub_Dis_Mode: ; ; In : String$ ; Out: Sends cr, lf, "Console Page Mode: OFF" to console. ; CALL Sub_Eol_Txt MOV SCBnum, 2! CALL Sub_Get_SCB MOV AL, SCBvar ; DSEG $ ; On DB 'ON$' Off DB 'OFF$' ; CSEG $ ; ; Displays ON or OFF if AL = 00h or 0FFh. ; (Note that this is the reverse of binary logic...) ; (Maybe it is a joke: OFF = 0FFh?) ; Sub_On_Off: CMP AL, -1! JE Sub_Off Sub_On: MOV DX, OFFSET On! JMP Sub_Pstring Sub_Off: MOV DX, OFFSET Off Sub_Pstring: MOV CL, 9! INT 224 RET ; ;-------------------------------- 18 Sub_Dis_Ticks: ; ; In : String$ ; Out: Sends cr, lf, "System Ticks per Second: 60" to console. ; CALL Sub_Eol_Txt MOV SCBnum, 3! CALL Sub_Get_SCB MOV AL, SCBvar! JMP Sub_Pr_Dec_AL ; ;-------------------------------- 19 Sub_Dis_Temp: ; ; In : String$ ; Out: Sends cr, lf, "Temporary File Drive: @" to console. ; CALL Sub_Eol_Txt MOV SCBnum, 4! CALL Sub_Get_SCB MOV AL, SCBvar! ADD AL, 40h, MOV DL, AL! JMP Sub_Con_Out_DL ; ;-------------------------------- 20 Sub_Dis_Date: ; ; In : String$ ; Out: Sends cr, lf, "Date: 04DC" to console. ; CALL Sub_Eol_Txt MOV SCBnum, 5! CALL Sub_Get_SCB MOV AX, WORD PTR SCBvar! JMP Sub_Pr_Wor_AX ; ;-------------------------------- 21 Sub_Dis_Time: ; ; In : String$ ; Out: Sends cr, lf, "Time: 12:34:56" to console. ; CALL Sub_Eol_Txt MOV SCBnum, 5! CALL Sub_Get_SCB MOV AL, hour! CALL Sub_Pr_Byt_AL MOV DL, ':'! CALL Sub_Con_Out_DL MOV AL, min! CALL Sub_Pr_Byt_AL MOV DL, ':'! CALL Sub_Con_Out_DL MOV AL, sec! JMP Sub_Pr_Byt_AL ; ;-------------------------------- 22 Sub_Dis_8087: ; ; In : String$ ; Out: Sends cr, lf, "8087 Present: TRUE" to console. ; CALL Sub_Eol_Txt MOV SCBnum, 128! CALL Sub_Get_SCB MOV AL, SCBvar ; DSEG $ ; True DB 'TRUE$' False DB 'FALSE$' ; CSEG $ ; ; Displays TRUE or FALSE if AL = 0FFh or 00h. ; Sub_True_False: CMP AL, -1! JNE Sub_False Sub_True: MOV DX, OFFSET True! JMP Sub_Pstring Sub_False: MOV DX, OFFSET False! JMP Sub_Pstring ; ;-------------------------------- 23 Sub_Dis_ProgID: ; ; In : String$ ; Out: Sends cr, lf, "Program ID: 01" to console. ; CALL Sub_Eol_Txt MOV SCBnum, 129! CALL Sub_Get_SCB MOV AL, SCBvar! JMP Sub_Pr_Dec_AL ; ;-------------------------------- 24 Sub_Dis_DsChain: ; ; In : String$ ; Out: Sends cr, lf, "Drive Search Chain: A, B, C, D" to console. ; CALL Sub_Eol_Txt MOV SCBnum, 135! CALL Sub_Get_SCB MOV AL, SCBvar! ADD AL, 40h! MOV DL, AL! CALL Sub_Con_Out_DL CMP date2, -1! JE DsChain_Abort CALL DsChain_Sep MOV AL, date2! ADD AL, 40h! MOV DL, AL! CALL Sub_Con_Out_DL CMP hour, -1! JE DsChain_Abort CALL DsChain_Sep MOV AL, hour! ADD AL, 40h! MOV DL, AL! CALL Sub_Con_Out_DL CMP min, -1! JE DsChain_Abort CALL DsChain_Sep MOV AL, min! ADD AL, 40h! MOV DL, AL! CALL Sub_Con_Out_DL DsChain_Abort: RET ; DSEG $ ; Comma DB ', $' ; CSEG $ ; DsChain_Sep: MOV DX, OFFSET Comma! JMP Sub_Pstring ; ;-------------------------------- 25 Sub_Dis_Back: ; ; In : String$ ; Out: Sends cr, lf, "In the Background: FALSE" to console. ; CALL Sub_Eol_Txt MOV SCBnum, 136! CALL Sub_Get_SCB MOV AL, SCBvar! JMP Sub_True_False ; ;-------------------------------- 26 Sub_Dis_NRproc: ; ; In : String$ ; Out: Sends cr, lf, "Number of Running Processes: 01" to console. ; CALL Sub_Eol_Txt MOV SCBnum, 137! CALL Sub_Get_SCB MOV AL, SCBvar! JMP Sub_Pr_Dec_AL ; ;-------------------------------- 27 Sub_Dis_Fore: ; ; In : String$ ; Out: Sends cr, lf, "Foreground Ratio: 16" to console. ; CALL Sub_Eol_Txt MOV SCBnum, 138! CALL Sub_Get_SCB MOV AL, SCBvar! JMP Sub_Pr_Dec_AL ; ;-------------------------------- 28 Sub_Dis_Penter: ; ; Sends cr, lf, "Press ENTER to Continue " to console. ; DSEG $ ; Penter DB 13, 10 DB 13, 10, 'Press ENTER to Continue $' ; CSEG $ ; MOV DX, OFFSET Penter! MOV CL, 9! INT 224 MOV CL, 1! INT 224 JMP Sub_Eol ; ;-------------------------------- 29 Sub_Set_Segment_X2: ; MOV AX, X2base! MOV ES, AX JMP Xseg_Loop ; ;-------------------------------- 30 Sub_Set_Segment_X3: ; MOV AX, X3base! MOV ES, AX JMP Xseg_Loop ; ;-------------------------------- 31 Sub_Set_Segment_X4: ; MOV AX, X4base! MOV ES, AX JMP Xseg_Loop ; ;-------------------------------- End of Subroutines ESEG $ ;-------------------------------- ; DB ' CP/M-86 Plus --' DB ' Extra Segment ' DB ' !"#$%&' DB 27h DB '()*+,-./' DB '0123456789:;<=>?' DB '@ABCDEFGHIJKLMNO' DB 'PQRSTUVWXYZ[\]^_' DB '`abcdefghijklmno' DB 'pqrstuvwxyz{|}~' ; ;-------------------------------- SSEG $ ;-------------------------------- ; DB ' CP/M-86 Plus --' DB ' Stack Segment ' DB ' !"#$%&' DB 27h DB '()*+,-./' DB '0123456789:;<=>?' DB '@ABCDEFGHIJKLMNO' DB 'PQRSTUVWXYZ[\]^_' DB '`abcdefghijklmno' DB 'pqrstuvwxyz{|}~' ; ;-------------------------------- ; END Well... As far as I know, this is the first published program using AFTER8 segments (also called "Compact Memory Model" in the DRI CP/M-86 manuals). Of course, this is just a "quick and dirty" little demo. A real RTS would, for example, check if X1base is zero, then print a "Error: RTS alone, without any program to execute..." message, etc, etc. (The (Demo) show places where modifications were needed because of the string "X1 Segment" etc, used to show the beginning of the segments. Else, BP = 0 points to be beginning of each Auxiliary segments (but this is less visible...).) Now that you know how to run a 256K program under CP/M-86 (Plus), "the sky, the limit"! Personally, I wouldn't mind some Internet utilities... A>That's all, folks! Yours Sincerely, "French Luser" EOF