$title('Concurrent CP/M System Loader Generation') genldr: do; /* Copyright (C) 1983,1984 Digital Research, Inc. P.O. Box 579 Pacific Grove, CA 93950 */ /* Revised: 03 October 83 by Bruce Skidmore 16 February 84 by GLP */ declare true literally '0FFH'; declare false literally '0'; declare forever literally 'while true'; declare boolean literally 'byte'; declare cr literally '0dh'; declare lf literally '0ah'; declare tab literally '09h'; declare esc literally '1bh'; declare bs literally '08h'; declare bios$data$off literally '0180h'; declare reset label external; declare fcb (1) byte external; declare fcb16 (1) byte external; declare tbuff (1) byte external; declare maxb address external; declare bios$fcb (36) byte public initial ( 0,'LBIOS3 ','SYS',0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0); declare bdos$fcb (36) byte public initial ( 0,'LBDOS3 ','SYS',0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0); declare FCBout (36) byte public initial ( 0,'CPMLDR ','SYS',0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0); /*------------------------------------------------------------------------ External Messages ------------------------------------------------------------------------*/ declare msg9120(16) byte external, msg9125 byte external, msg9135 byte external, msg9140 byte external, msg9145 byte external, msg9155 byte external, msg9160 byte external, msg9190 byte external, msg9195 byte external, msg9250 byte external, msg9255 byte external, msg9485 byte external, msg9490 byte external, msg9495 byte external, msg9500 byte external, msg9505 byte external; declare sctbfr (1) structure ( record (128) byte) public at (.memory); declare fcb$msg (13) byte public initial (' . $'); declare display boolean public; declare dma address public; declare osbase address public; declare buff$base address public; mon1: procedure (func,info) external; declare func byte; declare info address; end mon1; mon2: procedure (func,info) byte external; declare func byte; declare info address; end mon2; /* B D O S P r o c e d u r e & F u n c t i o n C a l l s */ system$reset: procedure public; call mon1 (0,0); end system$reset; write$console: procedure (char) public; declare char byte; if display then call mon1 (2,char); end write$console; print$buf: procedure (buffer$address) public; declare buffer$address address; if display then call mon1 (9,buffer$address); end print$buf; crlf: procedure public; call write$console (cr); call write$console (lf); end crlf; error: procedure(term$code,err$type,err$msg$adr) public; declare (term$code,err$type) byte; declare err$msg$adr address; declare (i,temp) byte; temp = display; display = true; call crlf; call print$buf (.msg9125); call print$buf (err$msg$adr); if err$type = 1 then call print$buf(.fcb$msg); if term$code then call system$reset; call crlf; display = temp; end error; open$file: procedure (fcb$address) byte public; declare fcb$address address; declare fcb based fcb$address (1) byte; fcb(12), /* ex = 0 */ fcb(32) = 0; /* cr = 0 */ return mon2 (15,fcb$address); end open$file; close$file: procedure (fcb$address) public; declare fcb$address address; call mon1 (16,fcb$address); end close$file; delete$file: procedure (fcb$address) public; declare fcb$address address; call mon1 (19,fcb$address); end delete$file; read$record: procedure (fcb$address) public; declare fcb$address address; if mon2 (20,fcb$address) <> 0 then do; call error(true,1,.msg9135); end; end read$record; write$record: procedure (fcb$address) public; declare fcb$address address; if mon2 (21,fcb$address) <> 0 then do; call error(true,1,.msg9140); end; end write$record; create$file: procedure (fcb$address) public; declare fcb$address address; declare fcb based fcb$address (1) byte; if mon2 (22,fcb$address) = 255 then do; call error(true,0,.msg9145); end; fcb(32) = 0; /* set cr = 0 */ end create$file; set$DMA$address: procedure (DMA$address) public; declare DMA$address address; call mon1 (26,DMA$address); end set$DMA$address; read$random$record: procedure (fcb$address) public; declare fcb$address address; if mon2 (33,fcb$address) <> 0 then do; call error(true,1,.msg9135); end; end read$random$record; write$random$record: procedure (fcb$address) public; declare fcb$address address; if mon2 (34,fcb$address) <> 0 then do; call error(true,1,.msg9140); end; end write$random$record; set$random$record: procedure (fcb$address) public; declare fcb$address address; call mon1 (36,fcb$address); end set$random$record; setbuf: procedure external; end setbuf; /* D a t a S t r u c t u r e s */ declare data$base address public; declare data$end address public; declare act$data$end address public; declare act$buf$end address public; declare bdos$atts(4) address public; declare bios$atts(4) address public; declare (sys$clen,sys$cbase,sys$dlen,sys$dbase) address public; declare (dblk$last,dblk$next) address public; declare add$buf$adr address public; declare add$buf based add$buf$adr structure ( base address, len address); declare drvtbl$adr address public; declare drvtbl based drvtbl$adr (16) address; declare dph$adr address public; declare dph based dph$adr structure ( xlt address, scratch1 address, scratch2 byte, mf byte, scratch3 address, dpb address, csv address, alv address, dirbcb address, dtabcb address, hash address, init address, login address, read address, write address, unit byte, chnnl byte, fcnt byte); declare dpb$adr address public; declare dpb based dpb$adr structure ( spt address, bsh byte, blm byte, exm byte, dsm address, drm address, al0 byte, al1 byte, cks address, off address, psh byte, phm byte); declare header$adr address; declare header$rec based header$adr structure ( gtype byte, len address, base address, min address, max address); declare base$pg$adr address public; declare base$pg based base$pg$adr structure( clenw address, clenb byte, cbase address, m80 byte, dlenw address, dlenb byte, dbase address, res1 byte, elenw address, elenb byte, ebase address, res2 byte, slenw address, slenb byte, sbase address, res3 byte); declare temp$fcb$adr address public; declare temp$fcb based temp$fcb$adr structure( drv byte, name(8) byte, type(3) byte, ex byte, s1 byte, s2 byte, rc byte, dm(16) byte, cur$rec byte, rr address, r2 byte); /* L o c a l P r o c e d u r e s */ movef: procedure (count,source$adr,dest$adr) public; declare count address; declare (source$adr,dest$adr) address; if count = 0 then return; else call move (count,source$adr,dest$adr); end movef; upper: procedure(b) byte public; declare b byte; if b < ' ' then return cr; /* all non-graphics */ /* translate alpha to upper case */ if b >= msg9155 and b <= msg9160 then b = b and 101$1111b; /* upper case */ return b; end upper; dsply$hex: procedure (val) public; declare val byte; call write$console (msg9120(shr (val,4))); call write$console (msg9120(val and 0fh)); end dsply$hex; dsply$hex$adr: procedure (val) public; declare val address; call dsply$hex (high (val)); call dsply$hex (low (val)); call write$console (msg9195); end dsply$hex$adr; get$param: procedure (val$adr) public; declare (val$adr) address; declare word$val based val$adr address; declare base byte; declare (char) byte; declare (lbindx) byte; lbindx = 0; word$val = 0; base = 16; do while (char := upper(tbuff(lbindx:=lbindx+1))) <> cr; if char = msg9190 then do; base = 10; end; else do; char = char - '0'; if (base = 16) and (char > 9) then do; if char > 16 then char = char - 7; else char = 255; end; if char < base then do; word$val = word$val*base + char; end; else do; call error (true,0,.msg9250); end; end; end; end get$param; get$atts: procedure (fcb$adr,atts$adr); declare fcb$adr address; declare atts$adr address; declare atts based atts$adr (4) address; declare i byte; call movef(8,fcb$adr+1,.fcb$msg); call movef(3,fcb$adr+9,.fcb$msg+9); if openfile(fcb$adr) = 0ffh then call error(true,1,.msg9255); header$adr = .sctbfr(0); call set$DMA$address(header$adr); call read$record(fcb$adr); do i = 0 to 3; atts(i) = 0; end; do i = 0 to 3; if (header$rec.gtype <> 0) and (header$rec.gtype < 5) then atts(header$rec.gtype-1) = header$rec.len; header$adr = header$adr + 9; end; end get$atts; buf$seg$blk: procedure(space,fcb$adr) public; declare space address; declare fcb$adr address; declare i byte; if (dma+space) > (buff$base+1000H) then do; call movef(8,.FCBout+1,.fcb$msg); call movef(3,.FCBout+9,.fcb$msg+9); do i = 0 to 30; call set$DMA$address(buff$base + (128 * i)); call write$record(.FCBout); end; call movef(double(128),buff$base+0f80h,buff$base); dma = dma - 0f80H; call set$DMA$address(dma); call movef(8,fcb$adr+1,.fcb$msg); call movef(3,fcb$adr+9,.fcb$msg+9); end; end buf$seg$blk; read$write$seg: procedure (fcb$adr,seg$len) public; declare fcb$adr address; declare seg$len address; declare seg$rec$len address; declare i address; call movef(8,fcb$adr+1,.fcb$msg); call movef(3,fcb$adr+9,.fcb$msg+9); if seg$len = 0 then return; seg$rec$len = (seg$len-1) / 8; /* convert para length to records */ call set$DMA$address(dma); do i = 0 to seg$rec$len; call buf$seg$blk(double(128),fcb$adr); call read$record(fcb$adr); call set$DMA$address(dma := dma + 128); end; end read$write$seg; read$seg: procedure (fcb$adr,seg$len) public; declare fcb$adr address; declare seg$len address; declare seg$rec$len address; declare i address; call movef(8,fcb$adr+1,.fcb$msg); call movef(3,fcb$adr+9,.fcb$msg+9); if seg$len = 0 then return; seg$rec$len = (seg$len-1) / 8; /* convert para length to records */ call set$DMA$address(dma); do i = 0 to seg$rec$len; call read$record(fcb$adr); call set$DMA$address(dma := dma + 128); end; end read$seg; write$seg: procedure (fcb$adr,seg$len) public; declare fcb$adr address; declare seg$len address; declare seg$rec$len address; declare i address; call movef(8,fcb$adr+1,.fcb$msg); call movef(3,fcb$adr+9,.fcb$msg+9); if seg$len = 0 then return; seg$rec$len = (seg$len-1) / 8; /* convert para length to records */ call set$DMA$address(dma); do i = 0 to seg$rec$len; call write$record(fcb$adr); call set$DMA$address(dma := dma + 128); end; end write$seg; setup$sysdat: procedure public; declare sysdat$adr address; declare sysdat based sysdat$adr structure( iosentry$off address, iosentry$seg address, iosinit$off address, iosinit$seg address, ldrentry$off address, ldrentry$seg address); sysdat$adr = data$base; /* Point to SYSTEM Data Area */ /* Setup the pointer to the BIOS entry point */ sysdat.iosentry$off = 3; /* Should be 3 */ sysdat.iosentry$seg = sys$cbase + bdos$atts(0); /* BIOS code segment */ /* Setup the pointer to the BIOS init point */ sysdat.iosinit$off = 0; /* Should be 0 but the Linker adds 5 */ sysdat.iosinit$seg = sys$cbase + bdos$atts(0); /* BIOS code segment */ /* Setup the Loader entry point */ sysdat.ldrentry$off = 6; sysdat.ldrentry$seg = sys$cbase + bdos$atts(0); return; end setup$sysdat; set$value: procedure (seg,byteoff,value) public; declare (seg,value) address; declare byteoff byte; declare setword$adr address; declare setword based setword$adr address; if temp$fcb.rr <> seg/8 then do; temp$fcb.rr = seg/8; temp$fcb.r2 = 0; call read$random$record(temp$fcb$adr); end; setword$adr = buff$base + ((seg and 7)*16) + byteoff; setword = value; call write$random$record(temp$fcb$adr); end set$value; fixup$segs: procedure public; call set$DMA$address(buff$base); temp$fcb$adr = .FCBout; temp$fcb.rr = 0FFFFh; temp$fcb.r2 = 0FFh; call set$value(8,6,sys$dbase); call set$value(bdos$atts(0)+8,9,sys$dbase); return; end fixup$segs; initialization: procedure public; declare (first,next,bracket) byte; first = 1; next = 1; bracket = false; if tbuff(0) <> 0 then do; do while(next <= tbuff(0)+1); if (tbuff(next) = ' ') or (tbuff(next) = tab) or (tbuff(next) = msg9485) or (tbuff(next) = msg9490) then do; if tbuff(next) = msg9485 then bracket = true; tbuff(next) = 0; end; else do; tbuff(first) = tbuff(next); first = first + 1; end; next = next + 1; end; tbuff(0) = first - 2; if bracket = true then do; call get$param(.osbase); end; end; else do; call error(true,0,.msg9495); end; end initialization; setup$sys$file: procedure public; declare i byte; call movef(8,.FCBout+1,.fcb$msg); call movef(3,.FCBout+9,.fcb$msg+9); call delete$file (.FCBout); call create$file (.FCBout); call movef(8,.FCBout+1,.fcb$msg); call movef(3,.FCBout+9,.fcb$msg+9); dma = .sctbfr(0); buff$base = dma; do i = 0 to 127; sctbfr(0).record(i) = 0; end; call set$DMA$address (dma); call write$record (.FCBout); end setup$sys$file; read$write$code: procedure public; declare i byte; declare flush$cnt address; dma = buff$base; call read$write$seg(.bdos$fcb,bdos$atts(0)); dblk$last = (7 - ((bdos$atts(0)-1) and 7)) * 16; dma = dma - dblk$last; call read$write$seg(.bios$fcb,bios$atts(0)); dblk$last = (7 - ((bios$atts(0)-1) and 7)) * 16; call movef(8,.FCBout+1,.fcb$msg); call movef(3,.FCBout+9,.fcb$msg+9); dma = dma - dblk$last; flush$cnt = (dma-buff$base+127)/128 - 1; dma = buff$base; call set$DMA$address(dma); do i = 0 to flush$cnt; call write$record(.FCBout); call set$DMA$address(dma:=dma + 128); end; call set$random$record(.bdos$fcb); call set$random$record(.bios$fcb); call set$random$record(.FCBout); temp$fcb$adr = .FCBout; temp$fcb.rr = temp$fcb.rr - 1; temp$fcb$adr = .bdos$fcb; temp$fcb.rr = temp$fcb.rr - 1; temp$fcb$adr = .bios$fcb; temp$fcb.rr = temp$fcb.rr - 1; /* the following adjustments are to take care */ /* of BIOS data ORG'ed at BIOS$DATA$OFF. */ temp$fcb.rr = temp$fcb.rr + bios$data$off/128; bios$atts(0) = bios$atts(0) + bios$data$off/16; bios$atts(1) = bios$atts(1) - bios$data$off/16; end read$write$code; read$data: procedure public; dma = buff$base + 128; call set$DMA$address(dma); call read$random$record(.FCBout); call set$DMA$address(buff$base); call read$random$record(.bdos$fcb); call read$record(.bdos$fcb); dblk$next = (7 - ((bdos$atts(0)-1) and 7)) * 16; dblk$last = (7 - ((sys$clen-1) and 7)) * 16; call movef(dblk$next,(buff$base+128-dblk$next),(dma+128-dblk$last)); dma = dma + (128 - dblk$last) + dblk$next; call read$seg(.bdos$fcb,bdos$atts(1) - (dblk$next/16)); dma = dma - (7 - ((bdos$atts(1)-(dblk$next/16)-1) and 7)) * 16; call set$DMA$address(buff$base); call read$random$record(.bios$fcb); call read$record(.bios$fcb); dblk$next = (7 - ((bios$atts(0) - 1) and 7)) * 16; dma = dma + bios$data$off - (bdos$atts(1)*16); call movef(dblk$next,(buff$base+128-dblk$next),dma); dma = dma + dblk$next; call read$seg(.bios$fcb,bios$atts(1) - (dblk$next/16)); end read$data; write$bdos$bios$data: procedure public; dma = buff$base; call write$seg(.FCBout,(bios$data$off/16)+bios$atts(1)+dblk$last/16); end write$bdos$bios$data; clean$up: procedure public; header$adr = .sctbfr(0); call set$DMA$address(header$adr); FCBout(33), FCBout(34), FCBout(35) = 0; call read$random$record(.FCBout); header$rec.gtype = 1; header$rec.len = sys$clen + sys$dlen; header$rec.base = sys$cbase; header$rec.min = sys$clen + sys$dlen; header$adr = header$adr + 9; header$rec.base = sys$dbase; call write$random$record(.FCBout); call close$file(.FCBout); call close$file(.bios$fcb); call close$file(.bdos$fcb); end clean$up; print$epilog: procedure public; display = true; call print$buf (.msg9500); end print$epilog; plm: procedure public; /* G E N C P M M a i n P r o g r a m */ call initialization; call get$atts(.bdos$fcb,.bdos$atts); call get$atts(.bios$fcb,.bios$atts); sys$clen = bdos$atts(0) + bios$atts(0); sys$cbase = osbase; sys$dlen = bios$atts(1); sys$dbase = sys$clen + osbase; call setup$sys$file; /* Take care of creating the */ /* system file. */ call read$write$code; /* Read the system code segments */ /* and concatenate them. */ call read$data; /* Read the system data segments */ /* and concat. them. */ dblk$last = 128 - dblk$last; dma = buff$base + 128; buff$base = .sctbfr(0); call movef(dblk$last+(bios$atts(1)*16)+bios$data$off,dma,buff$base); data$base = buff$base + dblk$last; data$end = data$base + (bios$atts(1)*16)+bios$data$off; drvtbl$adr = data$base + bios$data$off; /* position the DRVTBL array */ if drvtbl(0) = 0 then call error(true,0,.msg9505); sys$dbase = sys$clen + osbase; act$data$end = data$end - data$base; call setbuf; /* Set up all buffers */ bios$atts(1) = shr((data$end-data$base-bios$data$off+15),4); sys$dlen = act$buf$end - sys$dbase; call setup$sysdat; /* Setup the system data. */ call write$bdos$bios$data; /* Write the combined and updated */ /* data to the SYS file. */ call fixup$segs; /* Fixup the segment values */ /* that were not known on the 1st */ /* pass. */ call clean$up; /* Fix up the SYS file header and */ /* close the file. */ call print$epilog; end plm; end genldr; EOF