\ SAVEEXE.SEQ A SAVE EXE routine. Extracted from F83Y by Tom Zimmer only forth also hidden definitions also comment: ****** This code is not needed for now, we are not bothering to ****** ****** do a checksum, since DOS doesn't look at it anyway! ****** \ Some code to allow saving the Forth system to disk in the .EXE format. CODE XCKSUM ( add cnt startv seg - cksum ) POP DS POP AX POP CX POP BX HERE ADD AX, 0 [BX] ADD BX, # 2 DEC CX LOOP PUSH CS POP DS 1PUSH END-CODE comment; HEX CREATE SUVEC 100 , 0 , \ A place to save the entry absolute \ address of Forth. The zero gets plugged \ with the current value of CS. VARIABLE DPSAVED \ A place to save DP for restoral VARIABLE XDPSEGLEN \ holds the relative value for XSEGLEN VARIABLE WITHHEADS \ which is later adjusted in COLD to an WITHHEADS ON \ absolute value. LABEL SEXE MOV SUVEC 2+ DS \ Save DS=CS into setup vector for \ use in running forth after all this \ setup is done. MOV AX, DS \ move DS to AX ADD AX, ' #CODESEGS >BODY \ add code size ADD AX, ' #LISTSEGS >BODY \ and list size to get to heads MOV ES, AX \ move head seg to ES MOV CX, YDP \ get contents of YDP to CX MOV SI, CX \ COPY it into SI DEC SI MOV DI, SI \ decrement SI and move to DI \ We have set up the END addresses for a move \ backwards, to assure no overlap during \ expansion. THIS IS VERY IMPORTANT !! SUB AX, AX \ Clear AX XCHG AX, YSTART \ Exchange AX, and YSTART clearing it ADD AX, SUVEC 2+ \ Add to setup vector, it contains PUSH DS \ save DS MOV DS, AX \ move AX to DS OR CX, CX 0<> \ if YDP was not zero (0) IF STD \ moving BACKWARDS !!! REPZ MOVSB \ move HEADS to head space CLD THEN POP DS \ restore DS MOV YSEG ES \ set YSEG to ES MOV DX, DS \ move DS to AX ADD DX, ' #CODESEGS >BODY \ Add CODE space segments to get \ to the LIST segment start. PUSH DX \ Save for later use bye ES MOV CX, XDPSEGLEN \ get contents of XSEGLEN which is the LENGTH \ of move in SEGMENTS. MOV BX, DPSTART \ mov LIST offset segment to BX \ DPSTART in SAVEEXE holds the segment offset \ from CS to where LIST section starts. ADD BX, SUVEC 2+ \ Add this to the setup vector which holds \ the current value of CS. The result is the \ absolute segment address of start of LIST. ADD BX, CX \ Adjust source to end of move, will be DEC BX \ plugged into DS later. ADD DX, CX \ Bump destination segment to end of move DEC DX \ We are moving BACKWARDS to avoid any \ possibility of an overlay. PUSH DS \ save DS for later restoral STD \ Yes we are moving BACKWARDS. HERE MOV ES, DX \ Destination segment is held in DX MOV DS, BX \ Source segment is held in BX MOV AX, CX \ Save the segment count in AX MOV CX, # 10 \ 10 HEX is 16 decimal, ONE segment MOV SI, CX \ Set source and destination offsets. DEC SI MOV DI, SI REPZ \ move one 16 byte segment of LIST MOVSB \ to LIST space. DEC DX \ Decrement source and destination holders DEC BX MOV CX, AX \ restore CX for LOOP instruction. LOOPNZ CLD POP DS \ restore DS and ES POP ES MOV XSEG ES \ set XSEG to ES MOV AX, DPSAVED \ Move DPSAVED into DPSTART, MOV DPSTART AX \ for use later in setting DP MOV AX, XDPSEGLEN MOV XSEGLEN AX \ Set this for later adjustment to the \ absolute XSEGment value. MOV XMOVED # -1 WORD \ Initialize LIST to already moved. so \ the normal for bootup code wil not move \ the LIST again. JMP FAR [] SUVEC \ Jump to forth cold entry. END-CODE \ An empty .EXE header table. CREATE EHMT ( +0) 5A4D , \ EHADR .EXE File marker ( +2) 0 , \ EHLMRV File mod 512 including header ( +4) 0 , \ EH512Z Number of 512 byte blocks in file ( +6) 0 , \ Number of relocation table items ( +8) 2 , \ Size of header in segments ( +A) 0 , \ Minimum segments needed by program ( +C) 0FFFF , \ Additional segments needed by program, infinity ( +E) 0FFF0 , \ SS Stack segment, 100h below code strt ( +10) 0FFFC , \ Offset for stack pointer. ( +12) 0 , \ Word chekcsum, adds up to -1 ( DOS doesn't care! ) ( +14) SEXE , \ Offset to put in IP when passing control ( +16) 0FFF0 , \ CS Code segment, 100h below code strt ( +18) 1C , \ Displacement in bytes to first relocation item ( +1A) 0 , \ Overlay #, or zero (0) for resident code ( +1C) 0 , \ Null relocation item, and fill to two (2) segments ( +1E) 0 , DECIMAL comment: XCKSUM checksums a block of memory using word addition ( cnt must be even ) SUVEC startup vector, for a long jump to HEX 100 to set up CS correctly. Currently the .EXE header has CS set at 0FFF0h which fakes out the loader to set CS to the same as the Program Sement Prefix. This makes the long jump unnecessary, but we put it in so we could easily make the .EXE header more conventional. SEXE entry point specified by .EXE header. Sets the seg part of SUVEC, moves FORTH headers up to seg after DS (YSEG), does long jump thru SUVEC to start system. EHMT empty .EXE header. Entries 0Eh and 16h are SS and CS, set to -10h, somewhat questionable. If they are changed, 10h and 14h must be changed to compensate. comment; HEX 20 CONSTANT EHZ 100 EHZ - CONSTANT EHADR EHADR 2+ CONSTANT EHLMRV EHADR 4 + CONSTANT EH512Z EHADR 12 + CONSTANT EHCKSM EHADR 10 + CONSTANT EHSP DECIMAL comment: Constants for EXE header. Header is put immediately before 100h for write-out. See DOS 2.0 appendix H for explanation. EHADR header address EHZ header size EHLMRV load module remainder EH512Z # 512 blocks in entire file EHCKSM entire file checksum so file words total to FFFFh EHSP startup SP comment; HEX : WEXE ( handle --- ) >R \ Save the file HANDLE XHERE PARAGRAPH + XDPSEG ! \ Round up to next even paragraph XDP OFF \ boundry, and reset XDP. HERE DPSAVED ! \ Save DP for later restoral EHMT EHADR EHZ CMOVE \ Move empty header to before 100H HERE 100 + EHSP ! \ Startup Stack Pointer to HERE+20H HERE 100 + 05F + U2/ 8 / DPSTART ! \ Save start segment in DPSTART XDPSEG @ XSEG @ - \ Calculate LIST length in segments DUP XDPSEGLEN ! \ LIST dictionary segment offset DPSTART @ + \ add LIST start segment YSTART ! \ Save start segment in YSTART WITHHEADS @ IF YDP @ 1F + U2/ 8 / \ HEAD length in segments ELSE 0 THEN YSTART @ + \ = total length in segments EHADR U2/ 8 / - \ Subtract Header segments DUP 20 MOD \ Remainder of 512 byte pages 10 * EHLMRV ! \ save BYTE remainder in EHLMRV 1F + U2/ 10 / EH512Z ! \ Set # of full pages into EH512Z 0 EHCKSM ! \ dummy fill checksum value with a NULL R> YDP @ >R >R WITHHEADS @ 0= IF YSTART OFF YDP OFF THEN EHADR \ Start address HERE 100 + 05F + 0FFF0 AND \ end address is total length EHADR - \ subtract space below header R@ HWRITE DROP \ Write CODE space R@ XDPSEG @ XSEG @ - 100 /MOD \ Calc # of 4k byte sectors SWAP >R DUP >R 0 \ 100 hex * 16 decimal = 4096 decimal ?DO DUP 0 1000 ROT I 100 * XSEG @ + EXHWRITE DROP LOOP R> 0 R> 10 * ROT >R ROT R> 100 * XSEG @ + EXHWRITE DROP WITHHEADS @ IF 0 \ From segment offseg 0 YDP @ 1F + 0FFF0 AND \ HEAD length in bytes R> YSEG @ EXHWRITE DROP \ Write HEAD space ELSE r>drop THEN R> YDP ! YSTART OFF ; \ Reset YSTART DECIMAL only forth definitions also hidden also handle exehcb : ( | name --- ) DECIMAL \ Save system in DECIMAL number base >in @ span @ 1- > if cr ." File to save? " query then exehcb !hcb " EXE" ">$ exehcb $>ext exehcb hcreate abort" Could not create file" loading dup @ >r off exehcb wexe exehcb hclose drop r> loading ! ; : save-exe ( | name --- ) withheads on ; ' save-exe alias FSAVE ( | name --- ) \ a pseudonym for SAVE-EXE : turnkey ( | name --- ) here 2048 + u16/ =: #codesegs xhere drop xseg @ - 10 + =: #listsegs 0 =: #headsegs withheads off cr cr ." Application Space Usage Information" cr cr #codesegs 16 *d #listsegs 16 *d d+ 1024 um/mod nip 1+ dup 3 .r ." k for YOUR application." cr ." 64k for Editor file buffer." cr ." 13k for Editor delete line buffer." cr ." 8k for Editor line pointer table." cr ." 16k for screen save buffers." cr ." 8k for directory buffers." cr ." 4k for Input disk buffers." >attrib1 cr ." 2k " >norm ." for Macro buffers." cr 115 + 3 .r ." k total, plus user Added and Required data." bl emit bye ; comment: WEXE ( HANDLE ) write .EXE file given HANDLE of opened file. Copies header from EHMT to below 100h, fills out EHSP, EHMLRV, EH512Z, sets DTA, computes YSEG (headers) checksum, plus checksum from 0E0h to YSTART, puts NOT in EHCKSM, writes 0E0h to YSTART, then sets DTA for YSEG, writes out FORTH lists, and headers. "lobz" is the size of the first write, 0E0h to YSTART. Image is written with YSTART containing offset where the header segment data begins. YSTART non- zero indicates the segment hasn't been moved to its correct location for running. SAVE-EXE like SAVE-SYSTEM, but makes a .EXE file. comment; only forth definitions also