$title('VCMODE.CMD - Set Virtual Console Background Mode')
$compact
vcmode:
do;

$include (:f2:copyrt.lit)
$include (:f2:vaxcmd.lit)
$include (:f2:comlit.lit)
$include (:f2:mfunc.lit)
$include (:f2:fcb.lit)

dcl fcb (1) byte external;
dcl buff (1) byte external;

$include (:f2:sd.lit)

dcl ccb$pointer pointer;
dcl ccb$ptr structure ( offset address, segment address) at
  (@ccb$pointer);
$include (:f2:vccb.lit)
dcl ccb based ccb$pointer ccb$structure;

dcl ccpmproduct lit '14h';
dcl bdosversion lit '31h';

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;

mon3: procedure (func,info) address external;
  dcl func byte, info address;
end mon3;

mon4: procedure (func,info) pointer external;
  dcl func byte, info address;
end mon4;



  /**************************************
   *                                    *
   *       B D O S   Externals          *
   *                                    *
   **************************************/

print$char: procedure(char);
  declare char byte;
  call mon1(2,char);
end print$char;

print$console$buffer: procedure (buffer$address);
  declare buffer$address address;
  call mon1 (9,buffer$address);
end print$console$buffer;

version: procedure address;
  return mon3(12,0);
end version;

  /**************************************
   *                                    *
   *       X D O S   Externals          *
   *                                    *
   **************************************/

terminate: procedure;
  call mon1 (143,0);
end terminate;

get$console$number: procedure byte;
  return mon2 (153,0);
end get$console$number;

printb: procedure public;
  call print$char(' ');
end printb;

pdecimal: procedure(v,prec,zerosup) public;
                         /* print value v, field size = (log10 prec) + 1  */
                         /* with leading zero suppression if zerosup = true */
  declare v address,     /* value to print           */
       prec address,     /* precision                */
    zerosup boolean,     /* zero suppression flag    */
          d byte;        /* current decimal digit    */

  do while prec <> 0;
    d = v / prec;                           /* get next digit           */
    v = v mod prec;                         /* get remainder back to v  */
    prec = prec / 10;                       /* ready for next digit     */
    if prec <> 0 and zerosup and d = 0 then
      call printb;
    else
    do;
      zerosup = false;
      call printchar('0'+d);
    end;
  end;
end pdecimal;

/*lbracket: procedure byte;  /* find left bracket in command tail return */
/*  dcl i byte;              /* its index. if not found ret 0            */
/*  i = 1;
  do while i <= buff(0) and (buff(i) = ' ' or buff(i) = tab);
    i = i + 1;
  end;
  if buff(i) = '[' then
    return(i);
  return(0);
end lbracket;
*/

help: procedure;
    call mon1(m$prt$buf, .(cr, lf, tab, tab, tab ,'VCMODE EXAMPLES$'));
    call mon1(m$prt$buf, .(cr, lf, lf, 'vcmode', tab, tab, tab, tab, tab,
      '(show background mode)$'));
    call mon1(m$prt$buf, .(cr, lf, 'vcmode dynamic', tab, tab, tab, tab,
      '(sets background mode)$'));
    call mon1(m$prt$buf, .(cr, lf, 'vcmode buffered', tab, tab, tab, tab,
      tab, '"$'));
/*    call mon1(m$prt$buf, .(cr, lf, 'vcmode suspend', tab, tab, tab, tab,
      tab, '"$'));*/
    call mon1(m$prt$buf, .(cr, lf, 'vcmode size = 5', tab, tab, tab, tab,
      '(sets buffered mode max file size in)$'));
    call mon1(m$prt$buf, .(cr, lf, tab, tab, tab, tab, tab,
      '(kilobytes, legal range is 1 to 8191)$'));
    call mon1(m$prt$buf, .(cr, lf, tab, tab, tab, tab, tab,
      '(also sets background mode to buffered)$'));
/*    call mon1(m$prt$buf, .(cr, lf, 'vcmode size = 100H', tab, tab, tab,
      '(legal range in HEX is 1H to 1FFFFH)$'));*/
    call mon1(m$prt$buf, .(cr, lf, 'vcmode help', tab, tab, tab, tab,
      '(prints this message)$'));
    call mon1(m$prt$buf, .(cr, lf, '$'));
    call terminate;
end help;

showstate: procedure (verb);
  dcl (verb,state) address;
    call mon1(m$prt$buf, .(cr,lf,'Background Mode For Virtual Console$'));
    call pdecimal (console, 100, true);
    call printb;
    call mon1(m$prt$buf, verb);
    state = ccb.state and csm$buffered;
    if state = 0 then
      call mon1(m$prt$buf, .(' Dynamic$'));
    else
    do;
      call mon1(m$prt$buf, .(' Buffered', cr, lf, 'Maximum file size = $'));
      call pdecimal(ccb.maxbufsiz, 10000, true);
      call mon1(m$prt$buf, .('K$'));
    end;
    call mon1(m$prt$buf, .(cr, lf, '$'));
    
end show$state;

$include (:f2:qd.lit)

dcl qpb qpb$structure;

read$change$mxq: procedure;
  qpb.qaddr = ccb.vcmxq;
  call mon1 (m$readq, .qpb);
end read$change$mxq;

write$change$mxq: procedure;
  qpb.qaddr = ccb.vcmxq;
  call mon1 (m$writeq, .qpb);
end write$change$mxq;

atohb: procedure (char) byte public;    /* convert ascii hex to nibble value */
  declare char byte;
  if char >= '0' and char <= '9' then
    char = char - '0';
  else if char >= 'A' and char <= 'F' then
   char = char - 'A' + 10;
  else
    char = 255;	
  return(char);
end atohb;

atodb: procedure (char) byte public;/* convert ascii decimal to nibble value */
  declare char byte;
  if char >= '0' and char <= '9' then
    char = char - '0';
  else
    char = 255;	
  return(char);
end atodb;

atoi: procedure(str) word;    /* convert ascii to 16 bit unsigned value */
  dcl str pointer;
  dcl (accum, temp) word;
  dcl (val, i, len) byte;
  dcl string based str (1) byte;
  i, accum = 0;
  if (len := findb(str, 'H', 5)) <> 0ffffh then        /* hex conversion */
  do while (val := atohb(string(i))) <> 0ffh and i < len;
    accum = shl(accum, 4) + val;
    i = i + 1;
  end;
  else                       /* decimal is default base */
  do while (val := atodb(string(i))) <> 0ffh and i < 5;
    accum = 10 * accum + val;
    if i = 4 then
      temp = accum;
    i = i + 1;
  end;
  if temp > accum then                 /* overflow */
    accum = 0ffffh;
  return(accum);
end atoi;

compare: procedure(ustr, ostr, minlen, maxlen) boolean;
  dcl (ustr, ostr) pointer;            /* user string, option string */
  dcl user$string based ustr (1) byte;
  dcl (minlen, maxlen) byte;
  dcl cmplen word;
  cmplen = cmpb(ustr, ostr, maxlen);
  if cmplen = 0ffffh or (user$string(cmplen) = ' ' and cmplen >= minlen) then
    return(true);
  if user$string(cmplen) = ' ' then
  do;
     call mon1(m$prt$buf, .(cr,lf,'Invalid Command Option.', cr ,lf, '$'));
     call help;
  end;
  return(false);
end compare;

dcl vers address initial (0);
dcl no$state lit '0ffh';
dcl console byte;

  /*
    Main Program
  */

plmstart: procedure public;
  dcl option$ptr byte;
  dcl num word;
  vers = version;
  if (high(vers) <> ccpmproduct) then
  do;
    call print$console$buffer(.(cr,lf,'Requires Concurrent CP/M-86', cr, lf,
      '$'));
    call mon1(0,0);
  end;

  sysdat$pointer, ccb$pointer = mon4(m$sysdat, 0);  /* system data segment */
  ccb$ptr.offset = sd.ccb + (console := mon2(m$getcns, 0)) * size(ccb);

  call read$change$mxq;         /* MXQ is written in kernel terminate code */
  if (ccb.state and csm$background) <> 0 then
    call mon1(m$prt$buf, .(cr,lf,'Virtual Console not in foreground', cr, lf,
      '$'));
  else if buff(0) = 0 then
    call show$state(.('is$'));  /* show current state */
  else                          /* try to set state or show help message */
  do;
    fcb(f$type) = ' ';
    if      compare(@fcb(f$name), @('BUFFERED'), 1, 8) then
      ccb.state = ccb.state or csm$buffered;
    else if compare(@fcb(f$name), @('DYNAMIC'), 1, 7) then
      ccb.state = ccb.state and not double(csm$buffered);
    else if compare(@fcb(f$name), @('HELP'), 1, 4) then
      call help;
    else if compare(@fcb(f$name), @('SIZE'), 1, 4) then  /* change to 2,4    */
    do;                                    /* when suspend is put back in    */
      num = atoi(@fcb(f$name2));
      if num > 0 and num < 2000H then
        ccb.maxbufsiz = num;     /* limit size to 16 bit record count */
      else
      do;
        call mon1(m$prt$buf, .(cr,lf,'File size out of range', cr ,lf, '$'));
        call help;
      end;
      ccb.state = ccb.state or csm$buffered;
                                /* automatically sets to buffered */
    end;
    else
    do;
      call mon1(m$prt$buf, .(cr,lf,'Invalid Command Option.', cr ,lf, '$'));
      call help;
    end;
    call show$state(.('set to$'));
  end;
  call terminate;
    
end plmstart;
end vcmode;
