$title ('MP/M 1.1 Loader') mpmldr: do; /* Copyright (C) 1979,1980 Digital Research P.O. Box 579 Pacific Grove, CA 93950 Revised: 18 Jan 80 by Thomas Rolander */ declare true literally '0FFFFH'; declare false literally '0'; declare forever literally 'while true'; declare boolean literally 'byte'; declare start label; declare jmp$to$start structure ( jmp$instr byte, jmp$location address ) data ( 0C3H,.start-3); declare copyright (*) byte data ( 'COPYRIGHT (C) 1980,'); declare company$name (*) byte data ( ' DIGITAL RESEARCH '); declare serial$number (6) byte data ( '654321'); declare err$msgadr address initial (.default$err$msg); declare default$err$msg (*) byte data ( 'Disk read error','$'); declare mon1 literally 'ldmon1'; declare mon2 literally 'ldmon2'; 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 Externals * * * **************************************/ system$reset: procedure; declare dummy address; dummy = 0; stackptr = .dummy; end system$reset; write$console: procedure (char); declare char byte; call mon1 (2,char); end write$console; print$buffer: procedure (buffer$address); declare buffer$address address; call mon1 (9,buffer$address); end print$buffer; reset$disk$system: procedure; call mon1 (13,0); end reset$disk$system; open$file: procedure (fcb$address) byte; declare fcb$address address; return mon2 (15,fcb$address); end open$file; read$record: procedure (fcb$address) byte; declare fcb$address address; return mon2 (20,fcb$address); end read$record; set$DMA$address: procedure (DMA$address); declare DMA$address address; call mon1 (26,DMA$address); end set$DMA$address; /************************************** * * * Misc. BDOS procs * * * **************************************/ crlf: procedure; call write$console (0dh); call write$console (0ah); end crlf; printnib: procedure (n); declare n byte; if n > 9 then call write$console (n+'A'-10); else call write$console (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 write$console (' '); call write$console (' '); call printhex (high(a)); call printhex (low(a)); call write$console ('H'); end printaddr; printname: procedure (nadr); declare nadr address; declare n based nadr (1) byte; declare i byte; do i = 0 to 10; call write$console (n(i)); end; end printname; printitems: procedure (nadr,base,size); declare (nadr,base,size) address; call print$name (nadr); call printaddr (base); call printaddr (size); call crlf; end printitems; match$serial: procedure (cpyrtadr,memadr); declare (cpyrtadr,memadr) address; declare (i,j) byte; declare cpyrt based cpyrtadr (1) byte; declare mem based memadr (1) byte; do forever; i,j = -1; do while cpyrt(i:=i+1) = mem(j:=j+1); ; end; if i > 23 then return; if (memadr = 0) or (i > 17) then do; err$msgadr = .('Synchronization','$'); go to error; end; memadr = memadr + 1; end; end match$serial; declare (base,cur$top,prev$top) address; declare sysdatadr address; declare MPMbase based cur$top structure ( jmpinstr (3) byte, sysdat address ); declare (nrec,mask,ret,offset) byte; declare notdone boolean; declare (i,j) address; declare fcb (33) byte initial ( 0,'MPM ','SYS',0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0); declare banked$bdos$fcb (33) byte initial ( 0,'BNKBDOS ','SPR',0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0); declare entry$point (3) address; declare header (256) byte at (2000h); declare code$size address at (.header(1)); declare data$size address at (.header(4)); declare system$data (256) byte at (2100h); /* System Data: byte assignments ----------------------------- 000-000 Mem$top, top page of memory 001-001 Nmb$cns, number of consoles 002-002 Brkpt$RST, breakpoint RST # 003-003 Add system call user stacks, boolean 004-004 bank switched memory, boolean 005-005 Z80 version, boolean 006-006 banked bdos, boolean 007-008 base address of banked bdos 009-014 Unassigned 015-015 Max$mem$seg, max memory segment number 016-047 Memory segment table, filled in by GENSYS if memory bank switched, otherwise by MPMLDR 048-079 Breakpoint vector table, filled in by DDTs 080-111 System call user stacks 112-121 Scratch area used for memory segmentation 122-127 Unassigned 128-143 Subflg, submit flag array 144-251 Unassigned 252-253 Sysdatadr, MP/M data page address 254-255 Rspl, resident system process link, the address of the next Rsp, list terminates with a zero. */ declare mem$top byte at (.system$data(000)); declare nmb$cns byte at (.system$data(001)); declare brkpt$RST byte at (.system$data(002)); declare sys$call$stks boolean at (.system$data(003)); declare bank$switched boolean at (.system$data(004)); declare z80$cpu boolean at (.system$data(005)); declare banked$bdos boolean at (.system$data(006)); declare base$banked$bdos address at (.system$data(007)); declare nmb$mem$seg byte at (.system$data(015)); declare mem$seg$tbl (8) structure ( base byte, size byte, attrib byte, bank byte ) at (.system$data(016)); declare user$stacks (16) address at (.system$data(080)); declare tmp$mem$segs (9) byte at (.system$data(112)); declare rspl address at (.system$data(254)); declare option$temp byte at (005dh); declare option byte; declare buffer (1) byte at (2200h); declare sector$size literally '128'; declare destination based cur$top (1) byte; declare link based cur$top address; declare test$byte based cur$top byte; declare bitmap$adr address; declare bitmap based bitmap$adr (1) byte; load$PRL: procedure (get$name,fcb$adr) byte; declare get$name boolean; declare fcb$adr address; call set$DMA$address (.header); if (ret := read$record (fcb$adr)) <> 0 then do; if ret = 1 then return false; err$msgadr = .('Bad first record of SPR/RSP header','$'); go to error; end; call set$DMA$address (.header(128)); if (ret := read$record (fcb$adr) <> 0) then do; err$msgadr = .('Bad second record of SPR/RSP header','$'); go to error; end; nrec = shr(code$size+ shr(code$size,3)+ 7FH, 7); base = .buffer; /* read PRL+bitmap file into memory */ do i = 1 to nrec; call set$DMA$address (base); base = base + sector$size; if (ret := read$record (fcb$adr)) <> 0 then do; err$msgadr = .('Unexpected end of file, or disk read error','$'); go to error; end; end; /* offset by destination */ prev$top = cur$top; cur$top = prev$top-((code$size+data$size+0FFH) and 0FF00H); if not get$name then do; call printaddr (cur$top); call printaddr (prev$top - cur$top); end; offset = high(cur$top); /* bitmap directly follows last byte of code */ bitmap$adr = .buffer + code$size; j = 0; mask = 80H; /* loop through entire bit map */ do i = 0 to code$size-1; if (bitmap(j) and mask) <> 0 then /* copy & offset the byte where a bitmap bit is on */ do; destination(i) = buffer(i) + offset; end; else /* simply copy it to destination */ do; destination(i) = buffer(i); end; /* move mask bit one position to the right */ if (mask := shr(mask,1)) = 0 then /* re-initialize mask and get next bitmap byte */ do; mask = 80H; j = j + 1; end; end; if get$name then do; call move (3,.('RSP'),.buffer(16)); call printitems (.buffer(8), cur$top,prev$top - cur$top); return true; end; call crlf; return true; end load$PRL; load$banked$bdos: procedure; call set$DMA$address (.header); if open$file (.banked$bdos$fcb) = 0ffh then do; go to error; end; if not load$PRL (false,.banked$bdos$fcb) then do; go to error; end; base$banked$bdos = cur$top; end load$banked$bdos; load$system$data: procedure; declare cntr byte; call set$DMA$address (.system$data); if open$file (.fcb) = 0ffh then do; err$msgadr = .('MPM.SYS does not exist','$'); go to error; end; if (ret := read$record (.fcb)) <> 0 then do; go to error; end; call set$DMA$address (.system$data(128)); if (ret := read$record (.fcb)) <> 0 then do; go to error; end; if mem$top = 0 then /* determine size of memory */ do; cur$top = 0ff00h; not$done = true; do while not$done; test$byte = 5ah; if test$byte = 5ah then not$done = false; else cur$top = cur$top - 0100h; end; mem$top = high(cur$top); end; sysdatadr, cur$top = shl(double(mem$top),8); call print$buffer (.( 'Number of consoles = ','$')); call printnib (nmb$cns); call print$buffer (.(0dh,0ah, 'Breakpoint RST # = ','$')); call printnib (brkpt$RST); if z80$cpu then call print$buffer (.(0dh,0ah, 'Z80 CPU','$')); if banked$bdos then call print$buffer (.(0dh,0ah, 'Banked BDOS file manager','$')); call print$buffer (.(0dh,0ah, 'Top of memory =','$')); call printaddr (cur$top + 255); call print$buffer (.(0dh,0ah,0ah, 'Memory Segment Table:',0dh,0ah,'$')); call printitems (.('SYSTEM DAT'),cur$top,256); cur$top = cur$top - (prev$top := nmb$cns*256); call printitems (.('CONSOLE DAT'),cur$top,prev$top); if sys$call$stks then do; do cntr = 0 to nmb$mem$seg-1; user$stacks(cntr) = cur$top - cntr*64; end; cur$top = cur$top - (prev$top := (shr(nmb$mem$seg-1,2)+1)*256); call printitems (.('USERSYS STK'),cur$top,prev$top); end; end load$system$data; display$mem$map: procedure; if bank$switched then do; nrec = nmb$mem$seg; end; else do; nrec = 1; notdone = true; do while notdone; if tmp$mem$segs(nrec) >= high(cur$top) then do; tmp$mem$segs(nrec) = high(cur$top); nrec = nrec - 1; nmb$mem$seg = nrec; notdone = false; end; else do; nrec = nrec + 1; end; end; end; call print$buffer (.( '-------------------------',0dh,0ah,'$')); do while nrec <> 0; nrec = nrec - 1; call print$buffer (.('Memseg Usr','$')); if not bank$switched then do; mem$seg$tbl(nrec).base = tmp$mem$segs(nrec+1); mem$seg$tbl(nrec).size = tmp$mem$segs(nrec+2) - tmp$mem$segs(nrec+1); mem$seg$tbl(nrec).attrib = 0; mem$seg$tbl(nrec).bank = 0; end; call printaddr (shl(double(mem$seg$tbl(nrec).base),8)); call printaddr (shl(double(mem$seg$tbl(nrec).size),8)); if bank$switched then do; call print$buffer (.(' Bank ','$')); call printhex (mem$seg$tbl(nrec).bank); call write$console ('H'); end; call crlf; end; end display$mem$map; Restart$instr: procedure; disable; /* this disable is overlayed with RST x */ end Restart$instr; xeq$mpm: procedure; declare brkpt$adr address data (.Restart$instr); declare brkpt based brkpt$adr byte; stack$ptr = .entry$point(2); /* if command tail starts with 'B' then break */ if option = 'B' then do; brkpt = 1100$0111b or shl(brkpt$RST,3); call Restart$instr; end; end xeq$mpm; /* Main Program */ start: disable; /* allows mpmldr to run under MP/M */ option = option$temp; call reset$disk$system; call print$buffer (.(0dh,0ah,0ah, 'MP/M 1.1 Loader',0dh,0ah, '===============',0dh,0ah,0ah,'$')); call load$system$data; call print$buffer (err$msgadr:=.('XIOS SPR','$')); if not load$PRL (false,.fcb) then do; go to error; end; call print$buffer (err$msgadr:=.('BDOS SPR','$')); if not load$PRL (false,.fcb) then do; go to error; end; call print$buffer (err$msgadr:=.('XDOS SPR','$')); if not load$PRL (false,.fcb) then do; go to error; end; MPMbase.sysdat = sysdatadr; entry$point(2) = cur$top; call match$serial (.company$name,.system$data); /* Load Optional RSP Files */ rspl = 0; do while load$PRL (true,.fcb); link = rspl; rspl = cur$top; end; if banked$bdos then do; call print$buffer (err$msgadr:=.('BNKBDOS SPR','$')); call load$banked$bdos; end; call display$mem$map; call move (256,.system$data,sysdatadr); call xeq$mpm; error: call print$buffer (.(0dh,0ah, 'MP/M Loader error: ','$')); call print$buffer (err$msgadr); do forever; stackptr = 0; disable; halt; end; end mpmldr;