// DspStreamsB.bcpl -- Companion file is DspStreamsA.asm
// Copyright Xerox Corporation 1979, 1980
// Last modified November 26, 1980  3:29 PM by Taft

// Definition of DS in Streams.d publishes only the generic stream
// stuff + fdcb, ldcb, scroll, compact, cdcb

get "Streams.d"
get "AltoDefs.d"

external
[
// outgoing procedures
CreateDisplayStream	// (nlines, pBlock, lBlock, Font [sysFont]
			// wWidth [38], options [left+right}, zone
			// [sysZone]) -> ds
ShowDisplayStream	// (ds, how [, otherS])
GetFont			// (ds) -> font
SetFont			// (ds, font)
GetBitPos		// (ds) -> pos
SetBitPos		// (ds, pos)
GetLinePos		// (ds) -> lpos
SetLinePos		// (ds, lpos) -> true/false
InvertLine		// (ds, lpos) -> 0/1
EraseBits		// (ds, nbits[, flag])
CharWidth		// (fontorstream, charorstring) -> width
GetLmarg		// (ds) -> pos
SetLmarg		// (ds, pos)
GetRmarg		// (ds) -> pos
SetRmarg		// (ds, pos)
Scroll			// (ds[, char])
ResetLine		// (ds)
FontHeight		// (font) -> height

// incoming procedures
MoveBlock; SetBlock; Zero; BitBlt; Max; Min; Usc
DefaultArgs; CallSwat; SysErr
Allocate; Free
DisplayPut		// (ds, char), in DspStreamsA.asm
Puts

// incoming statics
dsp; sysFont; sysZone
]

// error codes
manifest
[
ecNotEnoughRoom = 1700
ecBadHowCommand = 1701
]

// Display stream (published part is in Streams.D)
// numbered entries used by assembly code
// starred entries may be modified after initialization

structure DSS:
[
@DS		// 12 + 2 words ( scroll=12)
lmarg word	// * left margin
rmarg word	// * right margin
options word	// option flags
blksz word	// block size for text line
nwrds word	// words per full scan line
pfont word	// *19 pointer to font
		// Following 2 are in order for CONVRT instruction
bwrds word	// *20 words per scan line
dba word	// *21, destination bit address
bstop word	// *22, bit where to stop writing
bsofar word	// *23, bits so far in this line
wad word	// *24, dest. word address
savac2 word	// *25, temp for AC2
fmp word	// pointer to full text line of bitmap
bda word	// beginning of bitmap data area
tdcb word	// * top DCB with data
mwp word	// * bitmap writer pointer
nl word		// number of lines user has
zone word	// zone allocated from
]
manifest lDSS = (size DSS)/16


manifest
[
displayheight = 808
displaywidth = 606
leftmargin = 8
]

structure STRING [ length byte; char ↑1,255 byte ]

//----------------------------------------------------------------------------
let CreateDisplayStream(nl, ssa, esa, font, wWidth, options,
 zone; numargs na) = valof
//----------------------------------------------------------------------------
[
DefaultArgs(lv na, -3, sysFont, ((displaywidth+31)/16)&(-2),
 DScompactleft+DScompactright, sysZone)
compileif lDSS gr lDS then [ foo = nil ]
esa = ssa + esa
ssa = (ssa+1) & (not 1)
esa = (esa) & (not 1)
let ds = Allocate(zone, lDSS)
ds>>DSS.zone = zone
ds>>DSS.nl = nl
ds>>DSS.pfont = font
ds>>DSS.type = stTypeDisplay
ds>>DSS.nwrds = wWidth
let ht = (xfont(font)!(-2)+1) rshift 1
let bsz = wWidth*ht*2
if Usc(esa-ssa, nl*lDCB+bsz) ls 0 then SysErr(ds, ecNotEnoughRoom)
ds>>DSS.puts = DisplayPut
ds>>DSS.close = ReleaseDs
ds>>DSS.reset = ClearDs
ds>>DSS.scroll = Scroll
ds>>DSS.compact = Compact
let edcb = ssa + nl*lDCB
let ldcb = edcb - lDCB
ds>>DSS.fdcb, ds>>DSS.ldcb = ssa, ldcb
ds>>DSS.blksz = bsz
let bda = edcb
ds>>DSS.fmp = esa - bsz
ds>>DSS.bda = bda
let p = ssa
for i = 1 to nl do
   [
   p>>DCB.next, p>>DCB.height = p+lDCB, ht
   p = p +lDCB
   ]
ldcb>>DCB.next = 0
let rightmargin = wWidth*16
if rightmargin gr displaywidth then rightmargin = displaywidth
ds>>DSS.lmarg, ds>>DSS.rmarg = leftmargin, rightmargin
ds>>DSS.options = options
SetFont(ds, font)
ClearDs(ds)
resultis ds
]

//----------------------------------------------------------------------------
and CharWidth(font, char) = valof
//----------------------------------------------------------------------------
[
let w, cw = 0, nil
if (char & 177400b) ne 0 then
   [
   for i = 1 to char>>STRING.length do
      w = w + CharWidth(font, char>>STRING.char↑i)
   resultis w
   ]
if font>>DSS.type eq stTypeDisplay then font = font>>DSS.pfont
if font!-2 ls 0 then font, w = font!-1, 1
   [
   cw = font!(font!char+char)
   if (cw & 1) ne 0 then break
   w, char = w+16, cw rshift 1
   ] repeat
resultis w + cw rshift 1
]

//----------------------------------------------------------------------------
and ResetLine(ds) be
//----------------------------------------------------------------------------
[
SetBitPos(ds, ds>>DSS.rmarg)
EraseBits(ds, ds>>DSS.lmarg-ds>>DSS.rmarg)
]

//----------------------------------------------------------------------------
and FontHeight(font) = xfont(font)!-2
//----------------------------------------------------------------------------

//----------------------------------------------------------------------------
and xfont(font) = (font!-2 ls 0? font!-1, font)
//----------------------------------------------------------------------------

//----------------------------------------------------------------------------
and ClearDs(ds) be
//----------------------------------------------------------------------------
[
let fdcb, fmp = ds>>DSS.fdcb, ds>>DSS.fmp
let dcb = fdcb
for i = 1 to ds>>DSS.nl do
   [
   dcb>>DCB.parwd = 0
   dcb>>DCB.bitmap = fmp
   dcb = dcb +lDCB
   ]
ds>>DSS.cdcb = fdcb
ds>>DSS.tdcb = fdcb
fdcb>>DCB.width = ds>>DSS.nwrds
ds>>DSS.mwp = ds>>DSS.bda
ClearMap(ds)
]

//----------------------------------------------------------------------------
and ClearMap(ds) be
//----------------------------------------------------------------------------
[
ds>>DSS.cdcb>>DCB.indwidth = ds>>DSS.nwrds
Zero(ds>>DSS.fmp, ds>>DSS.blksz)
SetBitPos(ds, ds>>DSS.lmarg)
]

//----------------------------------------------------------------------------
and ReleaseDs(ds) = valof
//----------------------------------------------------------------------------
[
ShowDisplayStream(ds, DSdelete)
Free(ds>>DSS.zone, ds)
resultis 0
]

//----------------------------------------------------------------------------
and ShowDisplayStream(ds, how, otherDs; numargs na) be
//----------------------------------------------------------------------------
// fdcb and ldcb must be first two elements of DSS structure -- this
// is so that other people can simulate "streams" easily for the
// purposes of using this routine
[
compileif offset DSS.fdcb ne 0 % offset DSS.ldcb ne 16 then [ foo = nil ]
DefaultArgs(lv na, -1, DSbelow, dsp)
let p = nil
switchon how into
   [
   case DSdelete:
      [
      p = PrevDCB(ds!0)
      if p then p>>DCB.next = (ds!1)>>DCB.next
      endcase
      ]
   case DSbelow:
      [
      (ds!1)>>DCB.next = (otherDs!1)>>DCB.next
      (otherDs!1)>>DCB.next = ds!0
      endcase
      ]
   case DSabove:
      [
      p = PrevDCB(otherDs!0)
      if p then 
         [
         (ds!1)>>DCB.next = otherDs!0
         p>>DCB.next = ds!0
         ]
      endcase
      ]
   case DSalone:
      [
      (ds!1)>>DCB.next = 0
      @displayListHead = ds!0
      endcase
      ]
   default: SysErr(ds, ecBadHowCommand)
   ]
]

//----------------------------------------------------------------------------
and PrevDCB(dcb) = valof
//----------------------------------------------------------------------------
[
let org = displayListHead-(offset DCB.next/16)
while org>>DCB.next ne dcb do
   [
   if org eq 0 then resultis 0
   org = org>>DCB.next
   ]
resultis org
]

//----------------------------------------------------------------------------
and GetFont(ds) = ds>>DSS.pfont
//----------------------------------------------------------------------------

//----------------------------------------------------------------------------
and SetFont(ds, pfont) = valof
//----------------------------------------------------------------------------
[
let ht = (xfont(pfont)!(-2)+1) rshift 1
ds>>DSS.pfont = pfont
SetRmarg(ds, ds>>DSS.rmarg)
resultis ht le ds>>DSS.cdcb>>DCB.height
]

//----------------------------------------------------------------------------
and GetBitPos(ds) = ds>>DSS.bsofar
//----------------------------------------------------------------------------

//----------------------------------------------------------------------------
and SetBitPos(ds, pos) = valof
//----------------------------------------------------------------------------
[
ds>>DSS.bsofar = pos
ds>>DSS.dba = (not pos) & 17b
let cdcb = ds>>DSS.cdcb
ds>>DSS.bwrds = cdcb>>DCB.width
ds>>DSS.wad = cdcb>>DCB.bitmap-cdcb>>DCB.width+pos rshift 4
resultis pos le ds>>DSS.bstop
]

//----------------------------------------------------------------------------
and GetLmarg(ds) = ds>>DSS.lmarg
//----------------------------------------------------------------------------

//----------------------------------------------------------------------------
and SetLmarg(ds, pos) be
//----------------------------------------------------------------------------
[
ds>>DSS.lmarg = pos
SetBitPos(ds, pos)
]

//----------------------------------------------------------------------------
and GetRmarg(ds) = ds>>DSS.rmarg
//----------------------------------------------------------------------------

//----------------------------------------------------------------------------
and SetRmarg(ds, pos) be
//----------------------------------------------------------------------------
[
ds>>DSS.rmarg = pos
let d, font = 0, ds>>DSS.pfont
if font!-2 ls 0 then d, font = 1, font!-1
ds>>DSS.bstop = pos-(font!(-1) & 377b)-d
]

//----------------------------------------------------------------------------
and GetLinePos(ds) = (ds>>DSS.cdcb-ds>>DSS.fdcb)/lDCB
//----------------------------------------------------------------------------

//----------------------------------------------------------------------------
and SetLinePos(ds, lpos) = valof
//----------------------------------------------------------------------------
[
let dcb = ds>>DSS.fdcb+lpos*lDCB
if lpos ge ds>>DSS.nl resultis false
if dcb>>DCB.indentation ne 0 resultis false
if dcb>>DCB.width eq 0 resultis false
ds>>DSS.cdcb = dcb
ds>>DSS.bwrds = dcb>>DCB.width
SetBitPos(ds, ds>>DSS.bsofar)
resultis true
]

//----------------------------------------------------------------------------
and InvertLine(ds, lpos) = valof
//----------------------------------------------------------------------------
[
let dcb = ds>>DSS.fdcb+lpos*lDCB
let b = dcb>>DCB.background
dcb>>DCB.background = b+1
resultis b
]

//----------------------------------------------------------------------------
and Scroll(ds, char; numargs na) = valof
//----------------------------------------------------------------------------
[
if na ge 2 switchon char into
   [
   case $*N: endcase  // cr
   case $*T:	// tab
      [
      let sp8 = CharWidth(ds, $*S)*8
      let lm=ds>>DSS.lmarg
      unless SetBitPos(ds, ((ds>>DSS.bsofar-lm)/sp8+1)*sp8+lm) then
         Puts(ds, $*S)
      resultis char
      ]
   case $*L: case 0:	// null, lf
      resultis char
   case -1:	// about to burp lines up one
   case -2:	// about to lose data off top of screen
      resultis true
   default:
      [
      test char ls 40b
         ifso [ Puts(ds, $↑); Puts(ds, char+100b) ]
         ifnot
            [
            // If a character with width=0 causes Scroll to be called,
            // it can only be because the character doesn't exist in the font.
            let w = CharWidth(ds, char)
            let rpos = w+ds>>DSS.bsofar
            // Check inside machine code is only for whether
            // currentpos+max width will exceed bstop.
            // This check is more careful.
            if w ne 0 then test rpos gr ds>>DSS.rmarg
               ifso if (ds>>DSS.options&DSstopright) eq 0 endcase
               ifnot
                  [ 
                  let ostop = ds>>DSS.bstop
                  ds>>DSS.bstop = rpos+20
                  Puts(ds, char)	//always succeeds
                  ds>>DSS.bstop = ostop
                  ]
             ]
      resultis char
      ]
   ]
let scrolled = false
let cdcb, ldcb = ds>>DSS.cdcb, ds>>DSS.ldcb
test cdcb eq ldcb
   ifnot
      [
      unless Compact(ds) resultis char
      cdcb = cdcb>>DCB.next
      ds>>DSS.cdcb = cdcb
      ]
   ifso
      [
      let dcb = ds>>DSS.fdcb
      if dcb eq ds>>DSS.tdcb then
         [
         unless ScrollOK(ds) resultis char
         unless FreeBitMap(ds) goto one
         ]
      unless Compact(ds) resultis char
      // We are about to scroll. Do NOT call user scroll routine here to
      // tell him of it, because if he does anything with the stream,
      // the world will die, as the stream is temporarily in a bad state.
      scrolled = true
      while dcb ne ldcb do
         [
         MoveBlock(dcb+1, dcb+(lDCB+1), lDCB-1)  // assumes next in word 0
         dcb = dcb+lDCB
         ]
      ds>>DSS.tdcb = ds>>DSS.tdcb-lDCB
one:  cdcb>>DCB.indwidth = ds>>DSS.nwrds
      cdcb>>DCB.bitmap = ds>>DSS.fmp
      ]
test cdcb>>DCB.bitmap eq ds>>DSS.fmp
   ifso ClearMap(ds)
   ifnot ResetLine(ds)
if scrolled then (ds>>DSS.scroll)(ds, -1)
if char ne $*N then Puts(ds, char)
resultis char
]

//----------------------------------------------------------------------------
and Compact(ds) = valof
//----------------------------------------------------------------------------
[
let dcb = ds>>DSS.cdcb
let ht = dcb>>DCB.height*2
let onw = dcb>>DCB.width
let nw = ((ds>>DSS.options&DScompactright) ne 0? (ds>>DSS.bsofar+15) rshift 4, onw)
let old = dcb>>DCB.bitmap	// = ds>>DSS.fmp
let d = 0
if (ds>>DSS.options&DScompactleft) ne 0 then
   [
   while d ne nw do
      [
      let p = old+ds>>DSS.blksz+d
      for i = 1 to ht do
         [
         p = p-onw
         if @p ne 0 then goto used
         ]
      d = d+1
      ]
   used: ]
// unless (nw eq onw) & (d eq 1) do
   nw, old = (nw-d+1)&(-2), old+d
let p = GetMapSpace(ds, nw*ht)
test p eq 0
   ifso	dcb>>DCB.indwidth = 0 // not enough room
   ifnot test p eq -1
      ifso resultis false // don't scroll
      ifnot
         [
         let new = p
         if nw ne 0 then for i = 1 to ht do
            [
            MoveBlock(new, old, nw)
            old, new = old+onw, new+nw
            ]
         dcb>>DCB.width = nw
         dcb>>DCB.indentation = d
         ]
dcb>>DCB.bitmap = p
resultis true
]

//----------------------------------------------------------------------------
and GetMapSpace(ds, nw) = valof
//----------------------------------------------------------------------------
[
let wp = nil
let bda = ds>>DSS.bda
   [
   wp = ds>>DSS.mwp
   let rp = ds>>DSS.tdcb>>DCB.bitmap
   let fmp = ds>>DSS.fmp
   if rp eq fmp then wp = bda
   test wp-bda gr rp-bda  //Subtractions try to keep < 15 bit nums
      ifso
         [
         if fmp-wp ge nw break  //u/b gr
         ds>>DSS.mwp = ds>>DSS.bda
         if rp eq ds>>DSS.bda then
            [
            unless ScrollOK(ds) resultis -1
            FreeBitMap(ds)
            ]
         ]
      ifnot
         [
         if rp-wp ge nw break  //u/b gr
         unless ScrollOK(ds) resultis -1
         unless FreeBitMap(ds) resultis 0	// not enough room
         ]
   ] repeat
ds>>DSS.mwp = wp+nw
resultis wp
]

//----------------------------------------------------------------------------
and ScrollOK(ds) = valof
//----------------------------------------------------------------------------
[
if (ds>>DSS.options & DSstopbottom) ne 0 resultis false
resultis (ds>>DSS.scroll)(ds, -2)
]

//----------------------------------------------------------------------------
and FreeBitMap(ds) = valof
//----------------------------------------------------------------------------
[
let dcb = ds>>DSS.tdcb
if dcb>>DCB.bitmap eq ds>>DSS.fmp resultis false
ds>>DSS.tdcb = dcb +lDCB
dcb>>DCB.indwidth = 0
resultis true
]

//----------------------------------------------------------------------------
and EraseBits(ds, nBits, flag; numargs na) = valof
//----------------------------------------------------------------------------
[
if na ls 3 then flag = 0
let newPos = Max(ds>>DSS.bsofar+nBits, 0)
let bbt = vec lBBT; bbt = (bbt+1)&(-2); Zero(bbt, lBBT)
bbt>>BBT.function = flag ls 0? 16B, 14B  // invert, replace
bbt>>BBT.dbca = ds>>DSS.cdcb>>DCB.bitmap
bbt>>BBT.dbmr = ds>>DSS.cdcb>>DCB.width
bbt>>BBT.dlx = Max(0, ds>>DSS.bsofar + Min(nBits, 0))
bbt>>BBT.dw = newPos eq 0? ds>>DSS.bsofar,
 newPos gr ds>>DSS.cdcb>>DCB.width*16?
 ds>>DSS.cdcb>>DCB.width*16 - ds>>DSS.bsofar, Max(nBits, -nBits)
bbt>>BBT.dh = ds>>DSS.cdcb>>DCB.height*2
SetBlock(lv bbt>>BBT.gray, flag ne 0, 4)
BitBlt(bbt)
SetBitPos(ds, newPos)
resultis newPos
]