! EDWIN driver for the BBC Macro (native mode)

from Edwin include Device
from Edwin include Icodes

record format Point fm (integer x, y)

const integer MAX IN BUF = 35

own integer BBC MODE = 2
! Mode 0 = 640 x 256  @  2 cols
! Mode 1 = 320 x 256  @  4 cols
! Mode 2 = 160 x 256  @ 16 cols

! Screen information
own integer SX = 0
own integer SY = 0
own integer XL = 0
own integer XR = 1279;     !Right hand side of device window
own integer YB = 0
own integer YT = 1023
own integer VIS = 0;       !0 if CVP inside VW
own integer OMODE = 0    { Overwrite mode 
own integer CCOL = 7     { White as Current Colour

routine SWAP (integer name  A, B)
   integer C
   C = A;   A = B;   B = C
end

routine COORDS (integer X, Y)
   TTPUT (X&255)
   TTPUT ((X>>8)&255)
   TTPUT (Y&255)
   TTPUT ((Y>>8)&255)
end

routine PLOT (integer COM, X, Y)
   TTPUT (25)
   TTPUT (COM)
   COORDS (X, Y)
end

routine DO COLOUR
   TTPUT (18)
   TTPUT (OMODE)
   CCol = 1 if BBC Mode = 0 and CCol > 1
   TTPUT (CCOL)
end

external routine Set Mode alias "EDWIN_BBC_MODE" (integer MODE)
   BBC MODE = MODE & 7
end

routine spec initialise cursor    ;  ! the thing that starts up the trackerball
routine spec reset interrupt vector

constant integer dev call = 1, cursor call = 2
own integer last call = dev call

routine set xor
   ttput(18) ; ttput(3) ; ttput(7)
end

routine XOR Cursor(integer X,Y)
   const short array X Arm ( 1: 4 ) = -30, 30,   0,   0
   const short array Y Arm ( 1: 4 ) =   0,  0, -30,  30
   integer I

   for I = 1, 1, 4 cycle
      Plot(4,X,Y)
      Plot(1,X Arm(I),Y Arm(I))
   repeat
   Flush Output
end

own integer last x = -1, last y = -1

routine line (integer x1, y1, x2, y2)
   plot (4, x1, y1);   plot (5, x2, y2)
end

routine triangle (integer x1, y1, x2, y2, x3, y3)
   PLOT (84, x1, y1)
   PLOT (84, x2, y2)
   PLOT (85, x3, y3)
end

routine FILL (integer xl, yb, xr, yt)
   SWAP (xl, xr) if xl > xr
   SWAP (yb, yt) if yb > yt
   xr = xr + 1 if xr = xl
   yt = yt + 1 if yt = yb
   TTPUT (24);   COORDS (xl, yb);      COORDS (xr, yt)
   TTPUT (18);   TTPUT (OMODE);   TTPUT (128 + CCOL)
   TTPUT (16)    { Clear }
   DO COLOUR
   TTPUT (26)
end

external routine BBC alias "EDWIN___B" (integer COM, X, Y)
   const integer array COLMAP (0:7) = 0   { black }   ,
                                         7   { white }   ,
                                         4   { blue }    ,
                                         2   { green }   ,
                                         1   { red }     ,
                                         5   { magenta } ,
                                         3   { yellow }  ,
                                         6   { cyan }
   own integer WX, WY, Nlines = 0, Lmode=0
   record format Data Fm (record (point fm) p, record (Data Fm) name Next)
   own record (Data fm) name Point List == 0
   own record (Data fm) name Next point == 0
   switch SW (0:MAX COM)

   include "Polyfill.abc"

   routine draw lines
      record (Data fm) name PP
      record (point fm) array pts (1:nlines+1)
      integer i
      return if Point List == Nil
      if nlines <= 1 start
          Line (point list_p_x, point list_p_y,
                point list_p_x, point list_p_y)
      else if nlines = 2
          Line (point list_p_x, point list_p_y,
                point list_next_p_x, point list_next_p_y)
      else
          pp == point list
          for I = 1, 1, Nlines cycle
             pts(i) = pp_p
             pp == pp_next
          repeat
          nlines = nlines + 1
          pts(nlines) = pts(1)
          POLY fill (nlines, Pts)
      finish
      nlines = 0
   end

   routine circle(integer x, y, r)
      integer d, e, s, da, db, dda, ddb, odb, odda, oddb
      e = 1
      s = 0
      while e<r cycle
         e = e<<1
         s = s+1
      repeat
      d = e>>1
      da = r<<s-d
      dda = r
      db = d
      ddb = 0
      cycle
         odda = dda
         oddb = ddb
         cycle
            odb = db
            db = db+da>>s
            da = da-odb>>s
            dda = da>>s
         repeat until odda#dda
         ddb = db>>s
         line(x-ddb,y+odda,x+ddb,y+odda)
         fill(x-odda,y+oddb,x+odda,y+ddb)
         fill(x-odda,y-oddb,x+odda,y-ddb)
         line(x-ddb,y-odda,x+ddb,y-odda)
      repeat until db>=da
   end


   constant integer char com = 6
   own integer last com = 0

   if last call = cursor call start
      xor cursor(last x,last y)
      do colour
      last call = dev call
   finish
   draw lines if nlines # 0 and (com = 10 or COM < 5)
   if last com = char com and com # char com start
      last com = com
      plot(4, SX, SY)
      ttput(4)
      ! restore cursor to correct position
   else if com # char com
      last com = com
   finish

   -> SW(COM)

SW(0): ! Initialise
       ! Valid numbers are 16_BBC, 16_BBC0, 16_BBC1, 16_BBC2
       BBC MODE = X & 15
       BBC MODE = 0 unless 0<=BBC MODE<=2
       Point List == New (point list)
       point List_next == Nil
       DEV DATA_NAME = "an Acorn workstation"
       DEV DATA_DVX = 1279
       DEV DATA_DVY = 1023
       DEV DATA_MVX = 1279
       DEV DATA_MVY = 1023
       DEV DATA_MAX COLOUR = 7
       TTPUT (22);    TTPUT (BBC MODE)
       TTPUT (23); TTPUT(1); TTPUT(0); TTPUT(0); TTPUT(0); TTPUT(0); TTPUT(0);
       TTPUT (0); TTPUT(0); TTPUT(0)  ; !turn cursor off  (IGF)
       CCOL = 7
       OMODE = 0
       LMODE = 0
       DO COLOUR
       initialise cursor
       return

SW(1): ! Terminate
       TTPUT (22)
       TTPUT (0)
       TTPUT (23); TTPUT(1); TTPUT(1); TTPUT(0); TTPUT(0); TTPUT(0); TTPUT(0);
       TTPUT (0); TTPUT(0); TTPUT(0)  ; !turn cursor back on again  (IGF)
       FLUSH OUTPUT
       reset interrupt vector
       return

SW(2): ! Update
       FLUSH OUTPUT
       return

SW(3): ! Newframe
       TTPUT (22);   TTPUT (BBC MODE)
       return

SW(4): ! Move
       PLOT (COM, X, Y)
       SX = X;   SY = Y
       return

SW(5): ! Line
       if Lmode >= 0 start
           PLOT (COM+LMODE, X, Y)
       else
           if Nlines = 0 start
               point list_p_x = sx;   point list_p_y = sy
               Next point == point list
               Nlines = 1
           finish
           return if x=sx and y=sy
           Nlines = Nlines + 1
           if Next point_next == Nil start
               Next point_next == New (Next point)
               Next point      == Next point_next
               Next point_next == Nil
           else
               Next point == next point_next
           finish
           next point_p_x = x;   next point_p_y = y
       finish
       SX = X;   SY = Y
       return

SW(6): ! Char
       ! Move to the bottom of the character by adding 32 to Y
       plot (4, sx, sy+32)
       ttput (5);   ttput (x);   ttput(4)
       sx = sx + 16
       plot (4, sx, sy)
       return

SW(7): ! Attribute
       if x=att colour start
           if 0<=Y<=7 start
               CCOL = COL MAP (Y)
           else
               CCOL = 7
           finish
           DO COLOUR
       finish else if x=att line style start
           if y=1 start
               LMODE = 16
           else
               LMODE = 0
           finish
       finish else if x=att colour mode start
           ! y=0 => Replace
           ! y=1 => Or    { EDWIN Uses 2 }
           ! y=2 => And   { EDWIN Uses 1 }
           ! y=3 => Xor
           ! y=4 => Invert
           if 0<=y<=4 start
               if y=1 or y=2 start
                   y = y !! 3  { swap 1 & 2 }
               finish
               OMODE = y
           else
               OMODE = 0
           finish
           DO COLOUR
       finish
       return

SW(8): ! Lower window bounds
       XL = X;   YB = Y
       return

SW(9): ! Upper window bounds
       XR = X;   YT = Y
       return

SW(10): ! Line modes
        if X=2 start { Point plot only }
            LMODE = 64
        else if X=3
            LMODE = -1
        else
            LMODE = 0
        finish
        return

SW(11): ! Overwrite mode (old entry point)
        Y = X;        X = att colour mode;        -> sw(7)

SW(12): ! Remember lower box bounds
        WX = X;   WY = Y
        return

SW(13): ! Upper box bounds & do the box
        swap (wx, x) if wx > x
        swap (wy, y) if wy > y
        return if WX > XR or X < XL or WY > YT or Y < YB
        WX = XL if WX < XL
        WY = YB if WY < YB
        X = XR if X > XR
        Y = YT if Y > YT
        ! Box now clipped into the screen.
        Fill (wx, wy, x, y)
        return

SW(14): ! Circle
        circle (sx, sy, x)
        return

SW(15): ! Area fill
         triangle (integer (y),    integer (y+4),
                   integer (y+8),  integer (y+12),
                   integer (y+16), integer (y+20))
         if x=4 start
             triangle (integer (y+16), integer (y+20),
                       integer (y+24), integer (y+28),
                       integer (y),    integer (y+4))
         finish
!          %for com = 1, 1, x*2 %cycle
!               write (integer(y), 1)
!               y = y + 4
!          %repeat
!          newline

SW(*):
end

!**************************************************************************
! The above bit is host independent, the following is for networked hosts,
! using the BBC as a terminal, and then follows the code for using the BBC
! when a 32016 processor is connected.
!
!**************************************************************************
!
! REMOTE HOST SPECS:
!%external %routine Sample %alias "EDWIN___B_SAM" (%integer %name i,x,y)
!   %signal 14, 8
!%end
!
!%external %routine Cursor %alias "EDWIN___B_REQ" ( %integername Char, X, Y )
!   %signal 14, 8
!%end
!%end %of %file
!**************************************************************************
! 
! 32016 SPECS:

external routine draw dots alias "EDWIN_DRAW_DOTS" (integer lx,ly,hx,hy,gap)
   integer dummy omode = omode, dummy ccol = ccol
   integer i,j,k,lx1,ly1,hx1,hy1
   from edwin include specs

   routine draw vertical lines
      lx1 = lx ; ly1 = ly
      map to device coords(lx1,ly1)
      hx1 = hx ; hy1 = hy
      map to device coords(hx1,hy1)
      i = lx
      while i <= hx cycle
         j = i ; ly1 = ly
         map to device coords(j,ly1)
         line(j,ly1,j,hy1)
         i = i + gap
      repeat
   end

   ! do vertical lines
   ccol = 1
   omode = 0
   do colour
   draw vertical lines
   !
   ! now do horizontal lines in black
   !
   i = ly
   ccol = 0
   do colour
   while i <= hy cycle
      j = i ; lx1 = lx
      map to device coords(lx1,j)
      line(lx1,j,hx1,j)
      i = i + gap
!      j = i + 1 ; lx1 = lx
!      map to device coords(lx1,j)
!      i = i + gap
!      k = i - 1 ; hx1 = hx
!      map to device coords(hx1,k)
!      k = k - 1; j = j + 1
!      fill(lx1,j,hx1,k)
   repeat
   !
   ! now invert the vertical lines
   !
   omode = 4
   do colour
   draw vertical lines
   omode = dummy omode
   ccol = dummy ccol
   do colour
end

constant integer tkbyte = 6
constant integer TK word = 7
record format iop fm (integer ad,byte v)
constant integer assemble address = 16_2F00
constant integer reset address = 16_2FA1
constant integer max 6502 = 179
constant integer array code 6502 (0 : 179) = -
{2F00} 16_4C,  16_C,  16_2F,  16_0,  16_0,  16_0,  16_0,  16_7,  16_0,  
{2F09} 16_0,  16_0,  16_0,  16_78,  16_AD,  16_5,  16_2,  16_C9,  
{2F11} 16_C0,  16_90,  16_1D,  16_8D,  16_9,  16_2F,  16_AD,  16_4,  
{2F19} 16_2,  16_8D,  16_8,  16_2F,  16_A9,  16_33,  16_8D,  16_4,  
{2F21} 16_2,  16_A9,  16_2F,  16_8D,  16_5,  16_2,  16_A9,  16_98,  
{2F29} 16_8D,  16_6E,  16_FE,  16_A9,  16_0,  16_8D,  16_62,  16_FE,  
{2F31} 16_58,  16_60,  16_8A,  16_48,  16_AD,  16_6D,  16_FE,  16_29,  
{2F39} 16_18,  16_D0,  16_6,  16_68,  16_A5,  16_FC,  16_6C,  16_8,  
{2F41} 16_2F,  16_AE,  16_60,  16_FE,  16_48,  16_29,  16_10,  16_F0,  
{2F49} 16_1D,  16_8A,  16_29,  16_8,  16_F0,  16_B,  16_EE,  16_3,  
{2F51} 16_2F,  16_D0,  16_13,  16_EE,  16_4,  16_2F,  16_4C,  16_67,  
{2F59} 16_2F,  16_CE,  16_3,  16_2F,  16_AD,  16_3,  16_2F,  16_C9,  
{2F61} 16_FF,  16_D0,  16_3,  16_CE,  16_4,  16_2F,  16_68,  16_29,  
{2F69} 16_8,  16_F0,  16_1D,  16_8A,  16_29,  16_10,  16_F0,  16_B,  
{2F71} 16_EE,  16_5,  16_2F,  16_D0,  16_13,  16_EE,  16_6,  16_2F,  
{2F79} 16_4C,  16_89,  16_2F,  16_CE,  16_5,  16_2F,  16_AD,  16_5,  
{2F81} 16_2F,  16_C9,  16_FF,  16_D0,  16_3,  16_CE,  16_6,  16_2F,  
{2F89} 16_68,  16_AA,  16_A5,  16_FC,  16_40,  16_48,  16_8,  16_C9,  
{2F91} 16_4,  16_D0,  16_8,  16_AD,  16_60,  16_FE,  16_29,  16_7,  
{2F99} 16_8D,  16_7,  16_2F,  16_28,  16_68,  16_6C,  16_A,  16_2F,  
{2FA1} 16_A9,  16_0,  16_8D,  16_6E,  16_FE,  16_AD,  16_8,  16_2F,  
{2FA9} 16_8D,  16_4,  16_2,  16_AD,  16_9,  16_2F,  16_8D,  16_5,  
{2FB1} 16_2,  16_60,  16_0


byte fn read io(integer ad)
   record(iop fm) io

   io_ad = ad
   * addr   _ io,2
   * movqd  _ #5,1
   * svc    _ #TK word
   result = io_v
end

routine write io(integer ad,byte v)
   record(iop fm) io

   io_ad = ad
   io_v = v
   * addr   _ io,2
   * movqd  _ #6,1
   * svc    _ #TK word
end

routine osbyte  {Call OB2 With R} ( integer Which, integer name X, Y )
   * MovD _ Which, 1
   * MovD _ 0(X),  2
   * MovD _ 0(Y),  3

   * Svc  _ #TK Byte

   * MovZBD _ 2, 0(X)
   * MovZBD _ 3, 0(Y)
end

own integer char = 0
!
! >> Try <<
!
! Try and extract a character from the given buffer
!
predicate Try ( integer Buff )
   integer z
   ! zero for the keyboard
   * MovD   _ # 16_91, 1
   * MovD   _ Buff,    2
{H}* MovQD  _ #0,      3
   * SVC    _ # TK BYTE
!H!* BCS    _ No
   * MOVZBD _ 3, z
   char = z
{H}false if Char = 0
   Char = Char&127 if Buff # 0
   true
end

routine dump 6502 code
   integer i

   for i = 0,1,max 6502 cycle
      write io(assemble address + i,code 6502(i))
   repeat
end

routine initialise cursor
   integer i = 0,j = 0

   dump 6502 code
   write io(16_0200,assemble address & 16_FF)
   write io(16_0201,(assemble address >> 8) & 16_FF)  {set up user vector to
                                                      {point to init code
   osbyte(16_88,i,j)       {execute initialise code in BBC micro}
end

routine reset interrupt vector
   integer i = 0, j = 0

   write io(16_0200,reset address & 16_FF)
   write io(16_0201,(reset address >> 8) & 16_FF)
   osbyte (16_88,i,j)      {execute reset code in BBC micro}
end

own integer joy stick = 1

external routine joystick present alias "BBC_JOYSTICK" ( integer joy)
   joy stick = joy if 0 <= joy <= 1
end

constant integer x addr = 16_2F03,
                   y addr = 16_2F05,
                   button addr = 16_FE60   {user VIA Port B}

routine cursor keys(integer name i,x,y)
   const byte array X Inc {offset by 1} ( 16_88: 16_8B ) = 0, 2, 1, 1
   const byte array Y Inc {offset by 1} ( 16_88: 16_8B ) = 1, 1, 0, 2
   integer Scaler

   scaler = 2                            ;! slow movement unless the cursor is down
   if try(0) and char <= 127 then i = char
   if 16_98+4 <= Char <= 16_9B+4 then Char = Char-4
   if 16_98 <= Char <= 16_9B start
      Scaler = 15 ; Char = Char-16
   finish
   if 16_88+4 <= Char <= 16_8B+4 then Char = Char-4
   if 16_88 <= Char <= 16_8B start
      X = X + (X Inc(Char) - 1)*Scaler
      Y = Y + (Y Inc(Char) - 1)*Scaler
      x = rem(x + 1280,1280) ; y = rem(y + 1032,1032)
      if joystick = 1 start
         write io(x addr,x & 255)
         write io(x addr + 1,(x >> 8) & 255)
         write io(y addr,y & 255)
         write io(y addr + 1,(y >> 8) & 255)
      finish
   finish
   !
   ! draw the cross
   !
   if last call = dev call start
      set xor
      last call = cursor call
      xor cursor(last x,last y)          ;! draw in the original position
   finish
   if last x # x or last y # y start
      ! refresh only if necessary
      xor cursor(last x,last y)          ;! draw off the old position
      xor cursor(x,y)                    ;! draw on the new position
      last x = x ; last y = y
   finish
end

external routine cursor alias "EDWIN___B_SAM" (integer name i,x,y)
   byte b,b1
   constant integer array convert buttons(1:4) = 4,1,3,2
   short a
   
   if joystick = 1 start
      !
      ! get the cursor position
      !
      select output(0)
      b = read io(x addr)
      b1 = read io(x addr + 1)
      a = (b1 << 8) ! b
      x = a    ;! convert from short to long integer
      !
      ! Do wrap round stuff
      !
      if x > 1280 start
         write io (x addr,0)
         write io (x addr+1,0)
         x = 0
      finish
      if x < 0 start
         write io (x addr,16_FF)
         write io (x addr+1,16_04)
         x=1279
      finish
      b = read io(y addr)
      b1 = read io(y addr + 1)
      a = (b1 << 8) ! b
      y = a
      if y > 1024 start
         write io (y addr,0)
         write io (y addr+1,0)
         y = 0
      finish
      if y < 0 start
         write io (y addr,16_FF)
         write io (y addr+1,16_03)
         y=1024
      finish
      !
      ! get buttons
      !
      b = ¬(read io(button addr)) & 7
      i = b
      i = convert buttons(i) if 1 <= i <= 4   ; ! change value from acorn to wc no
   finish

   cursor keys(i,x,y)
end

external routine BC alias "EDWIN___B_REQ" ( integername Char, X, Y )
   X = SX;   Y = SY
   BBC (4, SX, SY)
   Flush OUTPUT
   char = 0
   cycle
      if joystick = 1 start
         cursor(char,x,y)
      else
         cursor keys(char,x,y)
      finish
      exit if 0 < Char <= 127
   repeat
   SX = X ; SY = Y ; last x = x ; last y = y
end

end of file