VTEmulator#

General Information

Author: Charles Green
Language: ACTION!
Compiler/Interpreter: ACTION!
Published: usenet

Well, I've gotten enough responses that I'd annoy my UUCP neighbor by mailing this file out that many times, so I guess it'll be better to post it.

A couple of things I never got around to doing:

  • Adding a character count to the data in each line, maybe in the byte just before the 320-byte area where the characters for each line are "stenciled" in for display. This count could be used to speed up the insert-character and clear-to-end-of-line operations; they'd no longer have to go all the way out to column 80 if there was no data displayed out that far.
  • Adding a single graphics-0 display line, either at the top or bottom of the screen, which could hold the current baud rate, parity, etc. (assuming that someone adds the code to change them, in addition to maybe brightness). (P.S. - I've since discovered that luminance values of 0 for brightness, 2 or 4 for cursor, and 6 for the characters works better than the values in this program. You also probably want to put the contrast at a minimum.)
  • Smooth scrolling.

Enjoy, Charles Green char...@c3.COM

;''''''''''''''''''''''''''''''''''''
; "TVI925.ACT" - A display-list based
; terminal emulator by Charles Green.
;
; Derived from the public domain VT52+
; emulator written by Michael Jenkin.
;,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
MODULE
;A: handler, originally by Jenkin.

DEFINE LDY = "$A0",
 RTS = "$60", JMP = "$4C", PLA="$68",
 GR8 = "$0F", SKIP8 = "$70",
 JVP = "$41", LMS = "$40", RTI="$40",
 ESC = "1", CUP = "2", SGR = "3",
 PHA= "$48", TAX= "$AA", TXA= "$8A",
 TAY="$A8",TYA="$98",
 CMO = "4",
 XITVBV = "$E462"

TYPE line = [BYTE mode
 CARD adrs]
BYTE ARRAY display(8831) ;data+dlist
BYTE ARRAY enhance(8), cursor(4)
line POINTER dlist
BYTE POINTER curln ;current line data
BYTE pmbase = $D407, gractl=$D01D,
 lmargin = $52, rmargin = $53,
 rowcrs = $54, colcrs = $55, brkcnt,
 gprior = $26F, pcolr0 = $2C0,
 audc1 =$D201, hposm0 = $D004,
 sdmctl = $22F, soundr = $41, saudc1,
; audf1 = $D200,
; sskctl = $232,
 skctl = $D20F,
 state, need, needx, ins_mode, curcnt
CARD
; savmsc = $58,
 sdlist = $230,cursad,keydef = $79,
 vvblkd = $224, brkky = $236
 BYTE ARRAY xlate = [ ; New kbd map
$6C $6A $3B $80 $80 $6B $2B $2A
$6F $80 $70 $75 $0D $69 $2D $3D
$76 $FF $63 $80 $80 $62 $78 $7A
$34 $80 $33 $36 $1B $35 $32 $31
$2C $20 $2E $6E $80 $6D $2F $7E
$72 $80 $65 $79 $09 $74 $77 $71
$39 $80 $30 $37 $08 $38 $3C $3E
$66 $68 $64 $80 $82 $67 $73 $61 ;lowr
$4C $4A $3A $80 $80 $4B $5C $5E
$4F $80 $50 $55 $0D $49 $5F $7C
$56 $FF $43 $80 $80 $42 $58 $5A
$24 $80 $23 $26 $1B $25 $22 $21
$5B $20 $5D $4E $80 $4D $3F $60
$52 $80 $45 $59 $FE $54 $57 $51
$28 $80 $29 $27 $7F $40 $7B $7D
$46 $48 $44 $80 $82 $47 $53 $41 ;uper
$0C $0A $F0 $80 $80 $0B $1C $1E
$F0 $80 $10 $15 $0A $09 $1F $F0
$16 $FF $03 $80 $80 $02 $18 $1A
$F0 $80 $F0 $F0 $1B $F0 $F0 $F0
$1B $00 $1D $0E $80 $0D $7F $F0
$12 $80 $05 $19 $FD $14 $17 $11
$F0 $80 $F0 $F0 $7F $00 $F0 $F0
$06 $08 $04 $80 $82 $07 $13 $01 ;ctrl
 ]

PROC vbi()
 IF curcnt = 0 THEN
  pcolr0 ==! 5 ;invert cursor
  curcnt = 15
 ELSEIF curcnt < 255 THEN
  curcnt ==- 1
 FI
 IF saudc1 > $E0 THEN
  saudc1 ==- 1
  audc1 = saudc1
; SOUND(2,100,14,bel)
 FI                                
 IF brkcnt THEN
  brkcnt ==- 1
  IF brkcnt = 0 THEN
;  sskctl ==& $7F ;remove force space
;  skctl = sskctl
   skctl = $73
  FI
 FI
 [JMP XITVBV]

PROC brk() ;break interrupt handler
; [TYA PHA TXA PHA]
 brkcnt = 15;
; sskctl ==% $80 ;force serial space
; skctl = sskctl
 skctl = $F3
; [PLA TAX PLA TAY]
 [PLA RTI]

PROC del_ch(BYTE POINTER adr, BYTE col)
 BYTE POINTER badr,wadr
 BYTE x, y, bcount
 bcount = (80-col) RSH 1
 FOR y = 1 TO 7
 DO
  adr ==+ 40
  wadr = adr + (col RSH 1) - 1
  badr = wadr + 1
  IF col & 1 THEN
   badr ==+ 1
   wadr ==+ 1
   wadr^ =(wadr^ & $F0)%(badr^ RSH 4)
  FI
  x = bcount
  WHILE x > 0
  DO
   badr ==+ 1
   wadr ==+ 1
   wadr^ =(wadr^ LSH 4)%(badr^ RSH 4)
   x ==- 1
  OD
  wadr^ ==& $F0
 OD
 RETURN ; null proc for now

PROC ins_ch(BYTE POINTER adr, BYTE col)
 BYTE POINTER badr,wadr
 BYTE x, y, bcount
 bcount = (79-col) RSH 1
 FOR y = 1 TO 7
 DO
  adr ==+ 40
  badr = adr + 39
  wadr = badr - 1
  x = bcount
  WHILE x > 0
  DO
   badr^ =(wadr^ LSH 4)%(badr^ RSH 4)
   x ==- 1
   badr ==- 1
   wadr ==- 1
  OD
  IF col & 1 THEN
   badr^ ==& $F0
  ELSE
   badr^ ==RSH 4
  FI
 OD
 RETURN ; null proc for now

PROC Achr(BYTE cx, cy, cc)
 BYTE POINTER base, offset
 BYTE i, chr, c
 BYTE ARRAY chset = [
  $00 $00 $00 $00 $00 $00 $00 ; SP
  $44 $44 $44 $00 $44 $00 $00
  $AA $AA $00 $00 $00 $00 $00
  $AA $EE $AA $EE $AA $00 $00
  $EE $CC $EE $66 $EE $00 $00
  $AA $22 $44 $88 $AA $00 $00
  $CC $44 $CC $EE $EE $00 $00
  $66 $22 $44 $00 $00 $00 $00
  $22 $44 $44 $44 $22 $00 $00
  $88 $44 $44 $44 $88 $00 $00
  $00 $AA $EE $AA $00 $00 $00
  $00 $44 $EE $44 $00 $00 $00
  $00 $00 $00 $66 $22 $44 $00
  $00 $00 $EE $00 $00 $00 $00
  $00 $00 $00 $66 $66 $00 $00
  $22 $22 $44 $44 $88 $88 $00
  $44 $AA $AA $AA $44 $00 $00 ;'0'
  $44 $CC $44 $44 $EE $00 $00
  $CC $22 $44 $88 $EE $00 $00
  $CC $22 $44 $22 $CC $00 $00
  $AA $AA $EE $22 $22 $00 $00
  $EE $88 $CC $22 $CC $00 $00
  $44 $88 $EE $AA $44 $00 $00
  $EE $22 $44 $88 $88 $00 $00
  $44 $AA $44 $AA $44 $00 $00
  $44 $AA $66 $22 $44 $00 $00
  $66 $66 $00 $66 $66 $00 $00
  $66 $66 $00 $66 $22 $44 $00
  $22 $44 $88 $44 $22 $00 $00
  $00 $EE $00 $EE $00 $00 $00
  $88 $44 $22 $44 $88 $00 $00
  $CC $22 $44 $00 $44 $00 $00
  $44 $AA $AA $88 $66 $00 $00 ;'@'
  $44 $AA $EE $AA $AA $00 $00
  $CC $AA $CC $AA $CC $00 $00
  $66 $88 $88 $88 $66 $00 $00
  $CC $AA $AA $AA $CC $00 $00
  $EE $88 $CC $88 $EE $00 $00
  $EE $88 $CC $88 $88 $00 $00
  $66 $88 $AA $AA $66 $00 $00
  $AA $AA $EE $AA $AA $00 $00
  $EE $44 $44 $44 $EE $00 $00
  $66 $22 $22 $AA $EE $00 $00
  $AA $CC $88 $CC $AA $00 $00
  $88 $88 $88 $88 $EE $00 $00
  $AA $EE $EE $AA $AA $00 $00
  $CC $AA $AA $AA $AA $00 $00
  $66 $AA $AA $AA $CC $00 $00
  $CC $AA $CC $88 $88 $00 $00 ;'P'
  $66 $AA $AA $AA $EE $22 $00
  $CC $AA $CC $CC $AA $00 $00
  $66 $88 $CC $22 $EE $00 $00
  $EE $44 $44 $44 $44 $00 $00
  $AA $AA $AA $AA $66 $00 $00
  $AA $AA $AA $44 $44 $00 $00
  $AA $AA $EE $EE $AA $00 $00
  $AA $AA $44 $AA $AA $00 $00
  $AA $AA $44 $44 $44 $00 $00
  $EE $22 $44 $88 $EE $00 $00
  $66 $44 $44 $44 $66 $00 $00
  $88 $88 $44 $44 $22 $22 $00
  $CC $44 $44 $44 $CC $00 $00
  $44 $AA $00 $00 $00 $00 $00
  $00 $00 $00 $00 $EE $00 $00
  $66 $44 $22 $00 $00 $00 $00
  $00 $CC $66 $AA $66 $00 $00 ;'a'
  $88 $CC $AA $AA $CC $00 $00
  $00 $66 $88 $88 $66 $00 $00
  $22 $66 $AA $AA $66 $00 $00
  $00 $EE $EE $88 $66 $00 $00
  $22 $44 $EE $44 $44 $00 $00
  $00 $66 $AA $AA $66 $22 $CC
  $88 $EE $AA $AA $AA $00 $00
  $44 $00 $CC $44 $EE $00 $00
  $44 $00 $CC $44 $44 $88 $00
  $88 $88 $AA $CC $AA $00 $00
  $CC $44 $44 $44 $EE $00 $00
  $00 $CC $EE $EE $AA $00 $00
  $00 $CC $AA $AA $AA $00 $00
  $00 $66 $AA $AA $CC $00 $00
  $00 $CC $AA $AA $CC $88 $88 ;'p'
  $00 $66 $AA $AA $66 $22 $22
  $00 $EE $88 $88 $88 $00 $00
  $00 $CC $EE $22 $CC $00 $00
  $44 $EE $44 $44 $44 $00 $00
  $00 $AA $AA $AA $66 $00 $00
  $00 $AA $AA $44 $44 $00 $00
  $00 $AA $EE $EE $66 $00 $00
  $00 $AA $44 $AA $AA $00 $00
  $00 $AA $AA $AA $66 $22 $CC
  $00 $EE $44 $88 $EE $00 $00
  $66 $44 $88 $44 $66 $00 $00
  $44 $44 $44 $44 $44 $00 $00
  $CC $44 $22 $44 $CC $00 $00
  $CC $66 $00 $00 $00 $00 $00
  $AA $44 $AA $44 $AA $44 $AA
 ]
;strip high bit (inverse video)
 offset = cc - $20
;display character            
 dlist=sdlist + cy * 10 + 3
 base = dlist.adrs + (cx RSH 1) + 40
 IF ins_mode THEN
  ins_ch(dlist.adrs, cx)
 FI
 offset = chset+(offset LSH 3)-offset
 FOR i = 1 TO 7
 DO
  c = offset^
   c = c XOR enhance(i)
  chr = base^                
  IF (cx & 1) THEN
   c ==& $0F
   chr ==& $F0
  ELSE        
   c ==& $F0
   chr ==& $0F          
  FI                            
   base^ = chr % c
   base ==+ 40
   offset ==+1
 OD
 RETURN

PROC Acurse(); move cursor
 BYTE old_y ;Where we left cursor
 Zero(cursad+(old_y LSH 2), 4)
 hposm0 = (colcrs LSH 1) + 48
 MoveBlock(cursad+(rowcrs LSH 2),cursor,4)
 old_y = rowcrs ;memory for later clr
 pcolr0=5
 IF curcnt < 255 THEN
  curcnt=254
 FI
 RETURN

PROC scroll(BYTE first, last)
 line POINTER tdlist
 CARD tempadrs
 INT delta
 BYTE i  
 dlist=sdlist+3 + first*10
 tempadrs=dlist.adrs
 Zero(tempadrs,320)
 IF first>last THEN
  i=first
  first=last
  last=i
  delta=-1
 ELSE
  delta=1
 FI
 FOR i=first+1 TO last
 DO
  tdlist = dlist+10            *delta
  dlist.adrs = tdlist.adrs
  dlist = tdlist
 OD
 dlist.adrs = tempadrs
 RETURN

PROC Amargin() ; keep between margins
 IF colcrs > rmargin THEN
  colcrs = lmargin
  rowcrs ==+ 1
 FI ;fallthrough to Ascroll()
PROC Ascroll() ; roll screen if req'd
 IF rowcrs > 23 THEN
  scroll(0,23)
  rowcrs = 23
 FI
 RETURN

PROC curpos(BYTE chr)
 IF need = 2 THEN ; 1st ESC Y
  needx = chr - $20
  need ==- 1
 ELSEIF need = 1 THEN ; 2nd ESC Y
  chr ==- $20
  IF (needx <= 23) AND (chr <= rmargin) THEN
   colcrs = chr
   rowcrs = needx
;   Acurse()
  FI
  state = 0
 FI
 RETURN

PROC cursat(BYTE chr) ;cursor type
 IF chr = '2 THEN ;blinking block?
  curcnt = 254
  SetBlock(cursor,4,3)
 ELSEIF chr = '4 THEN ;steady line?
  curcnt = 255
  SetBlock(cursor,3,0)
  cursor(3) = 3
 FI
 pcolr0=5 ;third-intensity cursor
 state=0
 RETURN

PROC attrib(BYTE chr) ; enhance
 IF chr = '0 THEN
  Zero(enhance, 8)
 ELSEIF chr = '4 THEN ; reverse
  SetBlock(enhance, 8, $FF)
 ELSEIF chr = '8 THEN ; underline
  Zero(enhance, 8)
  enhance(6) = $FF
 ELSEIF chr = '> THEN ; both modes
  SetBlock(enhance, 8, $FF)
  enhance(6) = $00
 FI
 state = 0
 RETURN

PROC Aesc(BYTE chr) ; escape sequence
 BYTE ch
 CARD i
 IF chr = 'E THEN ; insert line
  dlist=sdlist+3+rowcrs*10
  scroll(23,rowcrs)
 ELSEIF chr = '. THEN ; cursor attrib
  state = CMO ; cursor rendition
  RETURN
 ELSEIF chr = 'G THEN ; inverse off
  state = SGR ; select rendition
  RETURN
 ELSEIF chr = 'I THEN ; back tab
  colcrs = (colcrs - 1) & $F8
 ELSEIF chr = 'j THEN ; reverse lf
  IF rowcrs > 0 THEN  
   rowcrs ==- 1
  ELSE
   scroll(23,0)
  FI
;  Acurse()
 ELSEIF chr = '* THEN ; home & clear
  rowcrs = 0
  colcrs = 0
  Aesc('Y)
; erase to EOS ( y?? )
 ELSEIF chr = 'Y OR chr = 'y THEN
  FOR ch = colcrs TO 79
  DO
   Achr(ch,rowcrs,' )
  OD                            
  IF rowcrs < 23 THEN
   dlist=sdlist+3+10*rowcrs
   FOR i = rowcrs+1 TO 23
   DO
    dlist==+ 10
    Zero(dlist.adrs,320)
   OD
  FI
;  Acurse()
 ; erase to EOL
 ELSEIF chr = 'T OR chr = 't THEN
  FOR ch = colcrs TO 79
  DO
   Achr(ch,rowcrs,' )
  OD                  
;  Acurse()
 ELSEIF chr = 'R THEN ; delete line
  dlist=sdlist+3+10*rowcrs
  scroll(rowcrs,23)
 ELSEIF chr = '= THEN ; cursor addr
  state = CUP
  need = 2
  RETURN
 ELSEIF chr = 'Q THEN ; insert char
  dlist=sdlist+3+10*rowcrs
  ins_ch(dlist.adrs,colcrs)
 ELSEIF chr = 'W THEN ; delete char
  dlist=sdlist+3+10*rowcrs
  del_ch(dlist.adrs,colcrs)
 ELSEIF chr = 'Z THEN ; multi-insert
  ins_mode = 1;
 ELSEIF chr = 'r THEN ; end multi-ins
  ins_mode = 0;
 ELSEIF chr = 'b THEN ; BlackOnWhite
  SetColor(2,0,15)
  SetColor(1,0,0)
 ELSEIF chr = 'd THEN ; WhiteOnBlack
  SetColor(2,0,0)
  SetColor(1,0,15)
 FI      
 state = 0
 RETURN

PROC Aopen()
 BYTE i
 sdmctl=0 ;No Antic DMA till DL ready
 vvblkd=vbi ;Put our vbi routine in
 brkky=brk ;Put our break routine in
; Examine data array for alignment
 sdlist=(display & $F000) + $1C80
 pmbase=sdlist RSH 8 ;page of plr/mis
 cursad=sdlist+$100
 dlist=sdlist
 FOR i = 0 TO 2 DO
  dlist.mode = SKIP8
  dlist==+1
 OD
 curln=display
 FOR i=0 TO 23 DO
  IF (curln = sdlist) THEN
   curln==+ $180
  FI
  IF (curln & $FFF) > $EC0 THEN
   curln = (curln & $F000) + $1000
  FI
  dlist.mode = GR8 + LMS
  dlist.adrs = curln
  SetBlock(dlist+3,7,GR8)
  curln==+ 320
  dlist ==+ 10
 OD
 dlist.mode = JVP
 dlist.adrs = sdlist
 SetColor(2,0,0) ; was 0,0,0
 SetColor(1,0,15) ; was 1,12,15
 SetColor(4,0,2) ; new - border
 gprior=8 ;cursor behind letters
 gractl=1 ;turn missiles on    
 sdmctl=$26 ;enable missile DMA
 Zero(cursad,128) ;clear missile data
 cursad ==+ 16 ;true top of screen
 state = 0
 lmargin = 0
 rmargin = 79  
 rowcrs = 0
 colcrs = 0
 Acurse()
 attrib('0) ;normal video
 cursat('4) ;normal cursor
 [LDY 1 RTS]

PROC Aclose()
 [LDY 1 RTS]

PROC Aput(BYTE areg)
 IF state = ESC THEN; escape sequence
  Aesc(areg)
 ELSEIF state = CUP THEN ; cursor pos
  curpos(areg)
 ELSEIF state = CMO THEN ; curs enhnc
  cursat(areg)
 ELSEIF state = SGR THEN ; enhance
  attrib(areg)
 ELSEIF areg = $1B THEN ; ESC
  state = ESC
 ELSEIF areg = $1A THEN ; ClearScreen
  colcrs = 0
  rowcrs = 0
  Aesc('Y)
 ELSEIF areg = $1E THEN ; home
  colcrs = 0
  rowcrs = 0
;  Acurse()
 ELSEIF areg = $0B THEN ; cursor up
  IF rowcrs > 0 THEN
   rowcrs ==- 1
;   Acurse()
  FI
 ELSEIF areg = $16 THEN ; cursor down
  IF rowcrs < 23 THEN
   rowcrs ==+ 1
;   Acurse()
  FI
 ELSEIF areg = $0C THEN ; cursor right
  IF colcrs < rmargin THEN
   colcrs ==+ 1
;   Acurse()
  FI
 ELSEIF areg = $0D THEN ; CR
  colcrs = lmargin ; was = 0
 ELSEIF areg = $0A THEN ; lf
  rowcrs ==+ 1
  IF colcrs > rmargin THEN          
   colcrs = 0 ;altos vi kludge
  FI
  Ascroll()
 ELSEIF areg = $08 THEN ; BS
  IF colcrs > lmargin THEN ;was >0
   colcrs ==- 1
  ELSEIF rowcrs > 0 THEN ;bs wraps
   colcrs = rmargin
   rowcrs==- 1
  FI
 ELSEIF areg = $07 THEN ; bell
  saudc1 = $F0
; bel = 16 ; vbi() will pickup
 ELSEIF areg = $09 THEN ; TAB
  colcrs = (colcrs + 8) & $F8
  Amargin()
 ELSEIF areg > $1F THEN ;printable  
  Amargin() ;new here
  Achr(colcrs,rowcrs,areg)
  colcrs ==+ 1
;ELSE unrecognized control-nothing
 FI
 Acurse() ;update cursor
 [LDY 1 RTS]

PROC Anofunc()
 [RTS]        

PROC Adummy()
 [LDY 1 RTS]

PROC Ahandler()
 BYTE ARRAY hatabs = $031A
 BYTE pos, found
 ;do not change the following 3 lines
 CARD ARRAY atab(6)
 BYTE Jump = [JMP]
 CARD init
 ; define device entry points
 atab(0) = Aopen - 1       ;OPEN
 atab(1) = Aclose - 1      ;CLOSE
 atab(2) = Anofunc - 1     ;READ
 atab(3) = Aput - 1        ;WRITE
 atab(4) = Adummy - 1      ;STATUS
 atab(5) = Anofunc - 1     ;SPECIAL
 init = Adummy             ;INIT
 ; find entry in hatabs
 found = 0;
 pos = 0
 WHILE (pos < 34) AND (found = 0)
 DO
  IF hatabs(pos) = 0 THEN
   found = 1
  ELSE
   pos ==+ 3
  FI
 OD
 IF found = 0 THEN
  PrintE("*** A: too many devices")
 ELSE
  hatabs(pos) = 'A
  hatabs(pos + 1) = atab & 255
  hatabs(pos + 2) = atab RSH 8
 FI
 RETURN

;*******************************
;*  MAIN PROGRAM
;*******************************

MODULE

BYTE
 ch = $02FC,
 bcount = $02EB,
 speed = [3], ;CWG 1=300BPS 3=1200
 wsize = [0],
 sbits = [0],
 lf = [0],
 iparity = [0],
 oparity = [0]    

PROC init_R(); set options for R:
 Close(3)
 Open(3,"R:",13,0)
 XIO(3,0,38,lf*64+32+iparity*4+oparity,0,"R1:") ;32=no xlate
 XIO(3,0,36,speed+7+wsize*16+128*sbits,0,"R1:")
 XIO(3,0,34,192,0,"R1:")
 XIO(3,0,40,0,0,"R1:") ;concurrent IO
 bcount = 0
 RETURN            

PROC init_A(); set up A: device
 Ahandler() ; install A: handler
 Close(2)
; Graphics(8+16)
 Open(2,"A:",8,0)
 RETURN

PROC intro()  
 soundr=0
 Close(7)
 Open(7,"K:",4,0)
 keydef = xlate ; load key translate
 init_R()
 init_A()
 soundr=0
 RETURN

PROC do_remote(); process remote
 BYTE chr
 BYTE stop = [0]
 BYTE HIWAT = [128]
 XIO(3,0,13,0,0,"R:")
 IF bcount > HIWAT THEN
  PutD(3,$13)     ;XOFF
  stop=1
 FI
 WHILE bcount > 0
 DO
  chr = GetD(3)
  PutD(2,chr & $7F)
  bcount ==- 1
 OD          
 IF stop THEN
  PutD(3,$11)     ;XON
  stop=0
 FI
 RETURN

PROC do_local(); process local
 BYTE chr
 IF $FF - ch THEN        ;INPUT
  chr = GetD(7)
  PutD(3,chr)
 FI      ; END IF CHAR
 RETURN

PROC main()
 intro()
 DO
  do_remote()
  do_local()
 OD
 RETURN