GENCMD: DO; /* CP/M 8086 CMD file generator COPYRIGHT (C) 1983 DIGITAL RESEARCH BOX 579 PACIFIC GROVE CALIFORNIA 93950 */ /**** The following commands were used on the VAX to compile GENCMD: $ util := GENCMD $ ccpmsetup ! set up environment $ plm86 'util'.plm xref 'p1' optimize(3) debug $ link86 f1:scd.obj, 'util'.obj to 'util'.lnk $ loc86 'util'.lnk od(sm(code,dats,data,stack,const)) - ad(sm(code(0),dats(10000h))) ss(stack(+32)) to 'util'. $ h86 'util' **** Followed on the micro by: A>vax gencmd.h86 $fans A>gencmd gencmd data[b1000 m86 xfff] ****/ DECLARE digital$code literally '0081h', /*DR code record */ digital$data literally '0082h', /* DR data record */ digital02 literally '0085h', /* DR 02 records */ paragraph literally '16', ex literally '12', /* extent */ nr literally '32', /* current record */ maxb address external, fcba(33) byte external, /* DEFAULT FILE CONTROL BLOCK */ buffa(128) byte external; /* DEFAULT BUFFER ADDRESS */ DECLARE COPYRIGHT(*) BYTE DATA (' COPYRIGHT (C) 1983, DIGITAL RESEARCH '); MON1: PROCEDURE(F,A) EXTERNAL; DECLARE F BYTE, A ADDRESS; END MON1; MON2: PROCEDURE(F,A) BYTE EXTERNAL; DECLARE F BYTE, A ADDRESS; END MON2; DECLARE SP ADDRESS; BOOT: PROCEDURE; call mon1 (0,0); END BOOT; declare segmts(11) structure (name(5) byte,begin$add address) initial ('CODE ',00h,'DATA ',0ffffh,'EXTRA',0ffffh,'STACK',0, 'X1 ',0,'X2 ',0,'X3 ',0,'X4 ',0,'8080 ',0,'NZERO',0, 'NHEAD',0); declare header (15) structure (typseg byte,file$length address,absolute$add address, minimum$mem address, maximum$mem address) initial (0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,00,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0); plmstart: PROCEDURE public; DECLARE FCB (33) BYTE AT (.FCBA), DFCBA LITERALLY 'FCBA'; DECLARE BUFFER (128) BYTE AT (.BUFFA), DBUFF LITERALLY 'BUFFA'; DECLARE SFCB(33) BYTE, /* SOURCE FILE CONTROL BLOCK */ BSIZE LITERALLY '1024', EOFILE LITERALLY '1AH', SBUFF(BSIZE) BYTE, /* SOURCE FILE BUFFER */ RFLAG BYTE, /* READER FLAG */ SBP ADDRESS; /* SOURCE FILE BUFFER POINTER */ declare tbp address; /* pointer to command tail */ declare count$command$tail byte at (.buffa); declare (t8080,nozero) byte; DECLARE TRUE LITERALLY '1', FALSE LITERALLY '0', FOREVER LITERALLY 'WHILE TRUE', CR LITERALLY '13', LF LITERALLY '10', WHAT LITERALLY '63'; PRINTCHAR: PROCEDURE(CHAR); DECLARE CHAR BYTE; CALL MON1(2,CHAR); END PRINTCHAR; CRLF: PROCEDURE; CALL PRINTCHAR(CR); CALL PRINTCHAR(LF); END CRLF; PRINTNIB: PROCEDURE(N); DECLARE N BYTE; IF N > 9 THEN CALL PRINTCHAR(N+'A'-10); ELSE CALL PRINTCHAR(N+'0'); END PRINTNIB; PRINTHEX: PROCEDURE(B); DECLARE B BYTE; CALL PRINTNIB(SHR(B,4)); CALL PRINTNIB(B AND 0FH); END PRINTHEX; PRINTADDR: PROCEDURE(A); DECLARE A ADDRESS; CALL PRINTHEX(HIGH(A)); CALL PRINTHEX(LOW(A)); END PRINTADDR; PRINTM: PROCEDURE(A); DECLARE A ADDRESS; CALL MON1(9,A); END PRINTM; PRINT: PROCEDURE(A); DECLARE A ADDRESS; /* PRINT THE STRING STARTING AT ADDRESS A UNTIL THE NEXT DOLLAR SIGN IS ENCOUNTERED WITH PRECEDING CRLF */ CALL CRLF; CALL PRINTM(A); END PRINT; declare mbuffadr address, LA ADDRESS; /* CURRENT LOAD ADDRESS */ declare head byte; PERROR: PROCEDURE(A); /* PRINT ERROR MESSAGE */ DECLARE A ADDRESS; CALL PRINT(.('ERROR: $')); CALL PRINTM(A); CALL PRINTM(.(', LOAD ADDRESS $')); CALL PRINTADDR(LA); CALL BOOT; END PERROR; diskerror: procedure; call perror(.('DISK WRITE$')); end diskerror; DECLARE DCNT BYTE; setdma: procedure(a); declare a address; call mon1 (26,a); end setdma; OPEN: PROCEDURE(FCB); DECLARE FCB ADDRESS; DCNT = MON2(15,FCB); END OPEN; CLOSE: PROCEDURE(FCB); DECLARE FCB ADDRESS; DCNT = MON2(16,FCB); END CLOSE; SEARCH: PROCEDURE(FCB); DECLARE FCB ADDRESS; DCNT = MON2(17,FCB); END SEARCH; SEARCHN: PROCEDURE; DCNT = MON2(18,0); END SEARCHN; DELETE: PROCEDURE(FCB); DECLARE FCB ADDRESS; CALL MON1(19,FCB); END DELETE; DISKREAD: PROCEDURE(FCB) BYTE; DECLARE FCB ADDRESS; RETURN MON2(20,FCB); END DISKREAD; DISKWRITE: PROCEDURE(FCB) BYTE; DECLARE FCB ADDRESS; RETURN MON2(21,FCB); END DISKWRITE; MAKE: PROCEDURE(FCB); DECLARE FCB ADDRESS; DCNT = MON2(22,FCB); END MAKE; RENAME: PROCEDURE(FCB); DECLARE FCB ADDRESS; CALL MON1(23,FCB); END RENAME; MOVE: PROCEDURE(S,D,N); DECLARE (S,D) ADDRESS, N BYTE, A BASED S BYTE, B BASED D BYTE; DO WHILE (N:=N-1) <> 255; B = A; S=S+1; D=D+1; END; END MOVE; declare char byte; comline$error: procedure; declare i byte; call crlf; do i = 1 to tbp; call printchar (buffer(i)); end; call printchar ('?'); call crlf; call boot; end comline$error; retchar: procedure byte; /* get another character from command tail */ if (tbp :=tbp+1) <= count$command$tail then return buffer(tbp); else return (0dh); end retchar; tran: procedure(b) byte; declare b byte; if b < ' ' then return 0dh; /* non-graphic */ if b - 'a' < ('z' - 'a') then b = b and 101$1111b; /* upper case */ return b; end tran; next$non$blank: procedure; char=tran(retchar); do while char= ' '; char= tran(retchar); end; end next$non$blank; CHECK$ONE$HEX: PROCEDURE (h) BYTE; /* READ ONE HEX CHARACTER FROM THE INPUT */ DECLARE H BYTE; IF H - '0' <= 9 THEN RETURN H - '0'; IF H - 'A' > 5 THEN return (0ffh); RETURN H - 'A' + 10; END CHECK$ONE$HEX; MAKE$DOUBLE: PROCEDURE(H,L) ADDRESS; /* CREATE A BOUBLE BYTE VALUE FROM TWO SINGLE BYTES */ DECLARE (H,L) BYTE; RETURN SHL(DOUBLE(H),8) OR L; END MAKE$DOUBLE; delimiter: procedure byte; /* logical */ declare i byte; declare del (*) byte data (0dh,'[], '); do i = 0 to last(del); if char = del(i) then return true; end; return false; end delimiter; get$num: procedure address; declare paradd address; paradd = 0; char = retchar; do while not delimiter ; if (char:=check$one$hex(char)) = 0ffh then call comline$error; else paradd = paradd * 16 + char; char = retchar; end; return paradd; end get$num; GETCHAR: PROCEDURE BYTE; /* GET NEXT CHARACTER FROM DISK BUFFER */ DECLARE I BYTE; IF (SBP := SBP+1) <= LAST(SBUFF) THEN RETURN SBUFF(SBP); /* OTHERWISE READ ANOTHER BUFFER FULL */ DO SBP = 0 TO LAST(SBUFF) BY 128; IF (I:=DISKREAD(.SFCB)) = 0 THEN CALL MOVE(.buffer,.SBUFF(SBP),80H); ELSE DO; IF I<>1 THEN CALL PERROR(.('DISK READ$')); SBUFF(SBP) = EOFILE; SBP = LAST(SBUFF); END; END; SBP = 0; RETURN SBUFF(0); END GETCHAR; DECLARE STACKPOINTER LITERALLY 'STACKPTR'; /* INTEL HEX FORMAT LOADER */ RELOC: PROCEDURE; DECLARE (RL, CS, RT,K) BYTE; declare multi$segments byte; DECLARE tabs address, /* temporary value */ TA ADDRESS, /* TEMP ADDRESS */ SA ADDRESS, /* PARAGRAPH LOAD ADDRESS */ FA ADDRESS, /* FINAL ADDRESS */ NB ADDRESS, /* NUMBER OF BYTES LOADED */ nxb byte, /* next byte in stream */ segadjst address, /* segment adjust */ seg$length (8) address, /* length of each segment */ write$add address, MBUFF based mbuffadr (256) BYTE, P BYTE; declare high$add address; SETMEM: PROCEDURE(B); /* set mbuff to b at location la */ DECLARE (B) BYTE; if ((.memory+la) < 0) or ((.memory+la) > maxb) then do; call print (.('INSUFFICIENT MEMORY TO CREATE CMD FILE $')); call boot; end; MBUFF(LA) = B; END SETMEM; zero$mem: procedure; do while (.memory +la) 0 then call diskerror; p = p+1; end write$record; empty$buffers: procedure; write$add = .memory; do while write$add+127 <= (.memory+fa); call write$record; write$add = write$add+128; end; if not multi$segments then do; call write$record; return; end; call move (write$add,.memory,(la:=.memory+fa+1-write$add)); end empty$buffers; READHEX: PROCEDURE BYTE; /* READ ONE HEX CHARACTER FROM THE INPUT */ declare khex byte; if (khex := check$one$hex(getchar)) <> 0ffh then return khex; else DO; CALL PRINT(.('INVALID HEX DIGIT$')); CALL DIAGNOSE; end; end readhex; READBYTE: PROCEDURE BYTE; /* READ TWO HEX DIGITS */ RETURN SHL(READHEX,4) OR READHEX; END READBYTE; READCS: PROCEDURE BYTE; /* READ BYTE WHILE COMPUTING CHECKSUM */ DECLARE B BYTE; CS = CS + (B := READBYTE); RETURN B; END READCS; hex$input: procedure; if rt = 2 or rt > 84h then segadjst = make$double(readcs,readcs); else do; /* PROCESS EACH BYTE */ DO WHILE (RL := RL - 1) <> 255; CALL SETMEM(READCS); LA = LA+1; END; IF LA > FA THEN FA = LA - 1; end; /* NOW READ CHECKSUM AND COMPARE */ IF CS + READBYTE <> 0 THEN DO; CALL PRINT(.('CHECK SUM ERROR $')); CALL DIAGNOSE; END; end hex$input; get$buffer$len: procedure; multi$segments = true; if rt = 84h then rt = 83h; else if rt = 83h then rt = 84h; if seg$length (rt-81h) <= (high$add:=la+rl-1) then do; if high$add=0 then high$add = 1; seg$length (rt-81h) = high$add; header(rt-81h).typseg = rt-80h; end; end get$buffer$len; compute$la: procedure (j) address; declare j byte; return (la and 000Fh)+shl((sa-segmts(j).begin$add),4); end compute$la; /* INITIALIZE */ SA, FA, NB = 0; P = 0; /* PARAGRAPH COUNT */ SBUFF(0) = EOFILE; fcb(nr) = 0; if head then fcb(nr) = 1; multi$segments = false; segadjst = 0; do k= 0 to 7; seglength(k) = 0; end; call zero$mem; ta=0; la=1; /* READ RECORDS UNTIL :00XXXX IS ENCOUNTERED */ DO FOREVER; /* SCAN THE : */ DO WHILE (nxb:=getchar) <> ':'; if nxb = eofile then go to second; /* MAY BE THE END OF TAPE */ END; /* SET CHECK SUM TO ZERO, AND SAVE THE RECORD LENGTH */ CS = 0; nb = nb +(rl:=readcs); TA, LA = MAKE$DOUBLE(READCS,READCS) ; sa = segadjst + shr(la,4); /* READ THE RECORD TYPE */ /* skip all records except type 0 2 81 */ if (rt:=readcs) > digital$code and rt < digital02 then do; if not t8080 then call get$buffer$len; else call hex$input; end; else do; if (rt = digital$code) then do; call hex$input; header(0).typseg = 1; end; else do; if (rt = 0 and sa < segmts(1).begin$add and sa >= segmts(0).begin$add) or rt = 2 then do; la = compute$la(0); call hex$input; header(0).typseg = 1; end; if (rt = 0 and sa >= segmts(1).begin$add) then do; multi$segments = true; if seg$length(1) < (high$add:=compute$la(1) +rl-1) then do; seg$length(1) = high$add; header(1).typseg=2; end; end; end; end; end; second: call empty$buffers; ta = (la+paragraph-1) and 0fff0h; header(0).file$length=fa/16+1; if header(0).minimum$mem = 0 then header(0).minimum$mem = fa/16+1; fa=ta; if not multi$segments then go to fin; call zero$mem; multi$segments = false; sfcb(ex),sfcb(nr) = 0; call open(.sfcb); call setdma(.buffer); do k = 1 to 7; if seg$length(k) <> 0 then do; seg$length(k) = seg$length(k)+paragraph and 0fff0h; header(k).file$length = seg$length(k)/16; if header(k).minimum$mem=0 then header(k).minimum$mem=seg$length(k)/16; end; end; segadjst = 0; seg$length(0) = ta; sbp=length(sbuff); DO FOREVER; /* SCAN THE : */ DO WHILE (nxb:=getchar) <> ':'; if nxb = eofile then go to afin; END; cs = 0; rl = readcs; la = make$double(readcs,readcs); sa = segadjst + shr(la,4); if (rt := readcs) = eofile then go to afin; if rt = 84h then rt = 83h; else if rt = 83h then rt = 84h; if rt > digital$code and rt < digital02 then do; do k = 0 to (rt-82h); la = la + seg$length(k); end; call hex$input; end; if (rt = 0 and sa >= segmts(1).begin$add) or rt = 2 then do; la = compute$la(1) + seg$length(0); call hex$input; end; END; afin: call empty$buffers; FIN: /* PRINT FINAL STATISTICS */ CALL PRINT(.('BYTES READ $')); CALL PRINTADDR(NB); CALL PRINT(.('RECORDS WRITTEN $')); CALL PRINTHEX(P+1); CALL CRLF; /* write the header record */ call close(.fcba); if head then do; fcb(ex),fcb(nr) = 0; call open(.fcba); call move (.header,.buffer,128); call setdma(.buffer); if diskwrite(.fcba) <> 0 then call diskerror; end; END RELOC; declare seg$number byte; ignore$filename: procedure; tbp = 0; char = buffer(tbp); call next$non$blank; do while (char:=buffer(tbp)) <> ' '; tbp = tbp +1; end; end ignore$filename; parse$tail: procedure; declare seg$index byte; get$segmt: procedure byte; /* get the segment name */ declare ( kentry, match$flag,j, no$match) byte; declare user$segmt(5) byte; do j = 0 to last (user$segmt); if delimiter then user$segmt(j) = ' '; else do; user$segmt(j) = char; char = tran(retchar); end; end; seg$index = 0; no$match, matchflag = true; do while no$match and seg$index < 11; match$flag=true; kentry = 0; do while match$flag and kentry <= last (segmts.name); if usersegmt(kentry) <> segmts(seg$index).name(kentry) then matchflag = false; else kentry = kentry +1; end; if matchflag then no$match = false; else seg$index = seg$index +1; end; if no$match then seg$index = 0ffh; return seg$index; end get$segmt; get$switches: procedure; do while char <> ']' and char <> cr; call next$non$blank; if char= 'A' then header(seg$index).absolute$add = (get$num); else if char= 'M' then do; header(seg$index).minimum$mem = (get$num); header(seg$index).typseg = seg$index+1; end; else if char= 'X' then header(seg$index).maximum$mem = (get$num); else if char= 'B' then segmts(seg$index).begin$add = (get$num); else do; call comline$error; call boot; end ; end; end get$switches; do forever; call next$non$blank; if char = cr then return; if get$segmt = 0ffh then do; call comline$error; end; if seg$index < 8 then do; if char = ']' or char = cr then call comline$error; call get$switches; end; else do; if seg$index = 8 then t8080 = true; else do; if seg$index = 9 then nozero = true; else head = false; end; end; end; end parse$tail; /* ARRIVE HERE FROM THE SYSTEM MONITOR, READY TO READ THE HEX TAPE */ /* SET UP STACKPOINTER IN THE LOCAL AREA */ DECLARE STACK(64) ADDRESS; SP = STACKPOINTER; STACKPOINTER = .STACK(LENGTH(STACK)); LA = 0h; mbuffadr = .memory; t8080 = false; nozero = false; head = true; SBP = LENGTH(SBUFF); /* SET UP THE SOURCE FILE */ CALL MOVE(.FCBA,.SFCB,33); CALL MOVE(.('H86',0),.SFCB(9),4); CALL OPEN(.SFCB); IF DCNT = 255 THEN CALL PERROR(.('CANNOT OPEN SOURCE$')); CALL MOVE(.('CMD'),.FCBA+9,3); /* REMOVE ANY EXISTING FILE BY THIS NAME */ CALL DELETE(.FCBA); /* THEN OPEN A NEW FILE */ CALL MAKE(.FCBA); CALL OPEN(.FCBA); IF DCNT = 255 THEN CALL PERROR(.('NO MORE DIRECTORY SPACE$')); ELSE DO; call ignore$filename; call parse$tail; CALL RELOC; CALL CLOSE(.FCBA); IF DCNT = 255 THEN CALL PERROR(.('CANNOT CLOSE FILE$')); END; CALL CRLF; CALL BOOT; END plmstart; END;