Browse Source

qlunits: updated QDOS unit, added a QLfloat unit to convert longints and doubles to 48bit QLfloats, added a rotating cube example

git-svn-id: trunk@47456 -
Károly Balogh 4 years ago
parent
commit
84e1be805c

+ 2 - 0
.gitattributes

@@ -8721,8 +8721,10 @@ packages/pxlib/src/pxlib.pp svneol=native#text/plain
 packages/qlunits/Makefile svneol=native#text/plain
 packages/qlunits/Makefile svneol=native#text/plain
 packages/qlunits/Makefile.fpc svneol=native#text/plain
 packages/qlunits/Makefile.fpc svneol=native#text/plain
 packages/qlunits/README.txt svneol=native#text/plain
 packages/qlunits/README.txt svneol=native#text/plain
+packages/qlunits/examples/qlcube.pas svneol=native#text/plain
 packages/qlunits/fpmake.pp svneol=native#text/plain
 packages/qlunits/fpmake.pp svneol=native#text/plain
 packages/qlunits/src/qdos.pas svneol=native#text/plain
 packages/qlunits/src/qdos.pas svneol=native#text/plain
+packages/qlunits/src/qlfloat.pas svneol=native#text/plain
 packages/regexpr/Makefile svneol=native#text/plain
 packages/regexpr/Makefile svneol=native#text/plain
 packages/regexpr/Makefile.fpc svneol=native#text/plain
 packages/regexpr/Makefile.fpc svneol=native#text/plain
 packages/regexpr/Makefile.fpc.fpcmake svneol=native#text/plain
 packages/regexpr/Makefile.fpc.fpcmake svneol=native#text/plain

+ 209 - 0
packages/qlunits/examples/qlcube.pas

@@ -0,0 +1,209 @@
+{
+    Copyright (c) 2017-2020 Karoly Balogh
+
+    Rotating 3D cube on a Sinclair QL
+    Example program for Free Pascal's Sinclair QL support
+
+    This example program is in the Public Domain under the terms of
+    Unlicense: http://unlicense.org/
+
+ **********************************************************************}
+
+program qlcube;
+
+uses
+  qdos, qlfloat;
+
+type
+  tvertex = record
+    x: longint;
+    y: longint;
+    z: longint;
+  end;
+
+const
+  cube: array[0..7] of tvertex = (
+     ( x: -1; y: -1; z: -1; ), // 0
+     ( x:  1; y: -1; z: -1; ), // 1
+     ( x:  1; y:  1; z: -1; ), // 2
+     ( x: -1; y:  1; z: -1; ), // 3
+
+     ( x: -1; y: -1; z:  1; ), // 4
+     ( x:  1; y: -1; z:  1; ), // 5
+     ( x:  1; y:  1; z:  1; ), // 6
+     ( x: -1; y:  1; z:  1; )  // 7
+  );
+
+type
+  tface = record
+    v1, v2, v3: longint;
+    edge: longint;
+  end;
+
+const
+  sincos_table: array[0..255] of longint = (
+         0,  1608,  3216,  4821,  6424,  8022,  9616, 11204,
+     12785, 14359, 15924, 17479, 19024, 20557, 22078, 23586,
+     25079, 26557, 28020, 29465, 30893, 32302, 33692, 35061,
+     36409, 37736, 39039, 40319, 41575, 42806, 44011, 45189,
+     46340, 47464, 48558, 49624, 50659, 51664, 52638, 53580,
+     54490, 55367, 56211, 57021, 57797, 58537, 59243, 59913,
+     60546, 61144, 61704, 62227, 62713, 63161, 63571, 63943,
+     64276, 64570, 64826, 65042, 65219, 65357, 65456, 65515,
+     65535, 65515, 65456, 65357, 65219, 65042, 64826, 64570,
+     64276, 63943, 63571, 63161, 62713, 62227, 61704, 61144,
+     60546, 59913, 59243, 58537, 57797, 57021, 56211, 55367,
+     54490, 53580, 52638, 51664, 50659, 49624, 48558, 47464,
+     46340, 45189, 44011, 42806, 41575, 40319, 39039, 37736,
+     36409, 35061, 33692, 32302, 30893, 29465, 28020, 26557,
+     25079, 23586, 22078, 20557, 19024, 17479, 15924, 14359,
+     12785, 11204,  9616,  8022,  6424,  4821,  3216,  1608,
+         0, -1608, -3216, -4821, -6424, -8022, -9616,-11204,
+    -12785,-14359,-15924,-17479,-19024,-20557,-22078,-23586,
+    -25079,-26557,-28020,-29465,-30893,-32302,-33692,-35061,
+    -36409,-37736,-39039,-40319,-41575,-42806,-44011,-45189,
+    -46340,-47464,-48558,-49624,-50659,-51664,-52638,-53580,
+    -54490,-55367,-56211,-57021,-57797,-58537,-59243,-59913,
+    -60546,-61144,-61704,-62227,-62713,-63161,-63571,-63943,
+    -64276,-64570,-64826,-65042,-65219,-65357,-65456,-65515,
+    -65535,-65515,-65456,-65357,-65219,-65042,-64826,-64570,
+    -64276,-63943,-63571,-63161,-62713,-62227,-61704,-61144,
+    -60546,-59913,-59243,-58537,-57797,-57021,-56211,-55367,
+    -54490,-53580,-52638,-51664,-50659,-49624,-48558,-47464,
+    -46340,-45189,-44011,-42806,-41575,-40319,-39039,-37736,
+    -36409,-35061,-33692,-32302,-30893,-29465,-28020,-26557,
+    -25079,-23586,-22078,-20557,-19024,-17479,-15924,-14359,
+    -12785,-11204, -9616, -8022, -6424, -4821, -3216, -1608
+  );
+
+function sin(x: longint): longint; inline;
+begin
+  sin:=sincos_table[x and 255];
+end;
+
+function cos(x: longint): longint; inline;
+begin
+  cos:=sincos_table[(x + 64) and 255];
+end;
+
+function mulfp(a, b: longint): longint; inline;
+begin
+  mulfp:=sarint64((int64(a) * b),16);
+end;
+
+function divfp(a, b: longint): longint;
+begin
+  divfp:=(int64(a) shl 16) div b;
+end;
+
+procedure rotate_vertex(const v: tvertex; var vr: tvertex; xa, ya, za: longint);
+var
+  x,y,z: longint;
+  s,c: longint;
+begin
+  s   :=sin(ya);
+  c   :=cos(ya);
+  x   :=mulfp(c,v.x) - mulfp(s,v.z);
+  z   :=mulfp(s,v.x) + mulfp(c,v.z);
+  if za <> 0 then
+    begin
+      vr.x:=mulfp(cos(za),x)   + mulfp(sin(za),v.y);
+      y   :=mulfp(cos(za),v.y) - mulfp(sin(za),x);
+    end
+  else
+    begin
+      vr.x:=x;
+      y:=v.y;
+    end;
+  vr.z:=mulfp(cos(xa),z)   - mulfp(sin(xa),y);
+  vr.y:=mulfp(sin(xa),z)   + mulfp(cos(xa),y);
+end;
+
+procedure perspective_vertex(const v: tvertex; zc: longint; var xr,yr: longint);
+var
+  rzc: longint;
+begin
+  rzc:=divfp(1 shl 16,(v.z - zc));
+  xr:=mulfp(mulfp(v.x,zc),rzc);
+  yr:=mulfp(mulfp(v.y,zc),rzc);
+end;
+
+procedure init_cube;
+var
+  i: longint;
+begin
+  for i:=low(cube) to high(cube) do
+    begin
+      cube[i].x:=cube[i].x shl 16;
+      cube[i].y:=cube[i].y shl 16;
+      cube[i].z:=cube[i].z shl 16;
+    end;
+end;
+
+
+var
+  mx, my: smallint;
+
+function min(a, b: smallint): smallint;
+begin
+  if a < b then
+    min:=a
+  else
+    min:=b;
+end;
+
+procedure draw_line(x1,y1,x2,y2: smallint);
+begin
+  sd_line(QCON,-1,x1,y1,x2,y2);
+end;
+
+procedure cube_redraw;
+var
+  i,s,e,cx,cy,vx,vy: longint;
+  vr: tvertex;
+  scale: longint;
+  rect:TQLRect;
+  fcubex: array[low(cube)..high(cube)] of Tqlfloat;
+  fcubey: array[low(cube)..high(cube)] of Tqlfloat;
+begin
+  rect.q_x:=0;
+  rect.q_y:=0;
+  rect.q_width:=140;
+  rect.q_height:=100;
+
+  scale:=(min(rect.q_width,rect.q_height) div 6) shl 16;
+  cx:=rect.q_x + rect.q_width div 2;
+  cy:=rect.q_y + rect.q_height div 2;
+  for i:=low(cube) to high(cube) do
+    begin
+      rotate_vertex(cube[i],vr,-my,-mx,0);
+      perspective_vertex(vr,3 shl 16,vx,vy);
+      longint_to_qlfp(@fcubex[i],cx + sarlongint(mulfp(vx,scale),16));
+      longint_to_qlfp(@fcubey[i],cy + sarlongint(mulfp(vy,scale),16));
+    end;
+
+  sd_clear(QCON,-1);
+  for i:=0 to 3 do 
+    begin
+      e:=(i+1) and 3;
+      sd_line(QCON,-1,@fcubex[i],@fcubey[i],@fcubex[e],@fcubey[e]);
+      s:=i+4; e:=e+4;
+      sd_line(QCON,-1,@fcubex[s],@fcubey[s],@fcubex[e],@fcubey[e]);
+      sd_line(QCON,-1,@fcubex[i],@fcubey[i],@fcubex[s],@fcubey[s]);
+    end;
+end;
+
+procedure main_loop;
+begin
+  repeat
+    inc(mx,5);
+    inc(my,7);
+    cube_redraw;
+  until false;
+end;
+
+begin
+  init_cube;
+
+  main_loop;
+end.

+ 3 - 2
packages/qlunits/fpmake.pp

@@ -29,9 +29,10 @@ begin
     P.OSes:=[sinclairql];
     P.OSes:=[sinclairql];
 
 
     T:=P.Targets.AddUnit('qdos.pas');
     T:=P.Targets.AddUnit('qdos.pas');
+    T:=P.Targets.AddUnit('qlfloat.pas');
 
 
-//    P.ExamplePath.Add('examples');
-//    T:=P.Targets.AddExampleProgram('.pas');
+    P.ExamplePath.Add('examples');
+    T:=P.Targets.AddExampleProgram('qlcube.pas');
 
 
 {$ifndef ALLPACKAGES}
 {$ifndef ALLPACKAGES}
     Run;
     Run;

+ 136 - 1
packages/qlunits/src/qdos.pas

@@ -44,20 +44,155 @@ const
   ERR_EX = -17;  { Expression error. }
   ERR_EX = -17;  { Expression error. }
   ERR_OV = -18;  { Arithmetic overflow. }
   ERR_OV = -18;  { Arithmetic overflow. }
   ERR_NI = -19;  { Not implemented. }
   ERR_NI = -19;  { Not implemented. }
-  ERR_RO = -20;	 { Read only. }
+  ERR_RO = -20;  { Read only. }
   ERR_BL = -21;  { Bad line of Basic. }
   ERR_BL = -21;  { Bad line of Basic. }
 
 
+const
+  Q_OPEN = 0;
+  Q_OPEN_IN = 1;
+  Q_OPEN_NEW = 2;
+  Q_OPEN_OVER = 3;  { Not available on microdrives. }
+  Q_OPEN_DIR = 4;
+
+type
+  Tqlfloat = array[0..5] of byte;
+  Pqlfloat = ^Tqlfloat;
+
+type
+  TQLRect = record
+    q_width : word;
+    q_height : word;
+    q_x : word;
+    q_y : word;
+  end;
+  PQLRect = ^TQLRect;
+
+type
+  TWindowDef = record
+    border_colour : byte;
+    border_width : byte;
+    paper : byte;
+    ink : byte;
+    width : word;
+    height : word;
+    x_origin: word;
+    y_origin: word;
+  end;
+  PWindowDef = ^TWindowDef;
+
 
 
 { the functions declared as external here are implemented in the system unit. They're included
 { the functions declared as external here are implemented in the system unit. They're included
   here via externals, do avoid double implementation of assembler wrappers (KB) }
   here via externals, do avoid double implementation of assembler wrappers (KB) }
 
 
+function mt_inf(sys_vars: ppchar; ver_ascii: plongint): Tjobid; external name '_mt_inf';
+
+procedure mt_dmode(s_mode: pword; d_type: pword); external name '_mt_dmode';
+
 function mt_alchp(size: dword; sizegot: pdword; jobid: Tjobid): pointer; external name '_mt_alchp';
 function mt_alchp(size: dword; sizegot: pdword; jobid: Tjobid): pointer; external name '_mt_alchp';
 procedure mt_rechp(area: pointer); external name '_mt_rechp';
 procedure mt_rechp(area: pointer); external name '_mt_rechp';
 
 
+function io_open_qlstr(name_qlstr: pointer; mode: longint): Tchanid; external name '_io_open_qlstr';
+function io_open(name: pchar; mode: longint): Tchanid; external name '_io_open';
+function io_close(chan: Tchanid): longint; external name '_io_close';
+
 function io_sbyte(chan: Tchanid; timeout: Ttimeout; c: char): longint; external name '_io_sbyte';
 function io_sbyte(chan: Tchanid; timeout: Ttimeout; c: char): longint; external name '_io_sbyte';
 function io_sstrg(chan: Tchanid; timeout: Ttimeout; buf: pointer; len: smallint): smallint; external name '_io_sstrg';
 function io_sstrg(chan: Tchanid; timeout: Ttimeout; buf: pointer; len: smallint): smallint; external name '_io_sstrg';
 
 
+function sd_wdef(chan: Tchanid; timeout: Ttimeout; border_colour: byte; border_width: word; window: PQLRect): longint; external name '_sd_wdef'; 
+function sd_clear(chan: Tchanid; timeout: Ttimeout): longint; external name '_sd_clear';
+
+function ut_con(params: PWindowDef): Tchanid; external name '_ut_con';
+function ut_scr(params: PWindowDef): Tchanid; external name '_ut_scr';
+
+
+procedure sd_point(chan: Tchanid; timeout: Ttimeout; x: Pqlfloat; y: Pqlfloat);
+procedure sd_point(chan: Tchanid; timeout: Ttimeout; x: double; y: double);
+
+procedure sd_line(chan: Tchanid; timeout: Ttimeout; x_start: Pqlfloat; y_start: Pqlfloat; x_end: Pqlfloat; y_end: Pqlfloat);
+procedure sd_line(chan: Tchanid; timeout: Ttimeout; x_start: double; y_start: double; x_end: double; y_end: double);
+
 
 
 implementation
 implementation
 
 
+uses
+  qlfloat;
+
+const
+  _SD_POINT = $30;
+  _SD_LINE = $31;
+
+procedure sd_point(chan: Tchanid; timeout: Ttimeout; x: Pqlfloat; y: Pqlfloat);
+var
+  stack: array[0..1] of TQLFloat;
+begin
+  stack[1]:=x^;
+  stack[0]:=y^;
+  asm
+    move.l d3,-(sp)
+    move.w timeout,d3
+    move.l chan,a0
+    lea.l stack,a1
+    moveq.l #_SD_POINT,d0
+    trap #3
+    move.l (sp)+,d3
+  end;
+end;
+
+procedure sd_point(chan: Tchanid; timeout: Ttimeout; x: double; y: double);
+var
+  stack: array[0..1] of TQLFloat;
+begin
+  double_to_qlfp(@stack[1],@x);
+  double_to_qlfp(@stack[0],@y);
+  asm
+    move.l d3,-(sp)
+    move.w timeout,d3
+    move.l chan,a0
+    lea.l stack,a1
+    moveq.l #_SD_POINT,d0
+    trap #3
+    move.l (sp)+,d3
+  end;
+end;
+
+
+procedure sd_line(chan: Tchanid; timeout: Ttimeout; x_start: Pqlfloat; y_start: Pqlfloat; x_end: Pqlfloat; y_end: Pqlfloat);
+var
+  stack: array[0..3] of TQLFloat;
+begin
+  stack[3]:=x_start^;
+  stack[2]:=y_start^;
+  stack[1]:=x_end^;
+  stack[0]:=y_end^;
+  asm
+    move.l d3,-(sp)
+    move.w timeout,d3
+    move.l chan,a0
+    lea.l stack,a1
+    moveq.l #_SD_LINE,d0
+    trap #3
+    move.l (sp)+,d3
+  end;
+end;
+
+procedure sd_line(chan: Tchanid; timeout: Ttimeout; x_start: double; y_start: double; x_end: double; y_end: double);
+var
+  stack: array[0..3] of TQLFloat;
+begin
+  double_to_qlfp(@stack[3],@x_start);
+  double_to_qlfp(@stack[2],@y_start);
+  double_to_qlfp(@stack[1],@x_end);
+  double_to_qlfp(@stack[0],@y_end);
+  asm
+    move.l d3,-(sp)
+    move.w timeout,d3
+    move.l chan,a0
+    lea.l stack,a1
+    moveq.l #_SD_LINE,d0
+    trap #3
+    move.l (sp)+,d3
+  end;
+end;
+
+
 end.
 end.

+ 182 - 0
packages/qlunits/src/qlfloat.pas

@@ -0,0 +1,182 @@
+{
+    Conversion code from various number formats to QL Float format.
+
+    Code ported from the C68/QL-GCC libc implementation available at:
+    http://morloch.hd.free.fr/qdos/qdosgcc.html
+
+    The QL wiki claims the original of these sources are by
+    Dave Walker, and they are in the Public Domain.
+    https://qlwiki.qlforum.co.uk/doku.php?id=qlwiki:c68
+
+ **********************************************************************}
+unit qlfloat;
+
+interface
+
+uses
+  qdos;
+
+function longint_to_qlfp(qlf: Pqlfloat; val: longint): Pqlfloat;
+function double_to_qlfp(qlf: Pqlfloat; val: Pdouble): Pqlfloat;
+
+
+implementation
+
+function longint_to_qlfp(qlf: Pqlfloat; val: longint): Pqlfloat; assembler; nostackframe;
+asm
+  { pointer to qlfloat is in a0 }
+  { val is in d0 }
+
+  movem.l d2-d4/a0,-(sp)  { save register variables and a0 }
+  moveq.l #0,d2           { sign value }
+  move.l  d2,d3           { shift value }
+  tst.l   d0              { zero or -ve ? }
+  beq     @zeroval        { zero }
+  bpl     @plusval        { +ve }
+
+{ i is negative here. set the sign value then make i positive }
+
+  moveq   #1,d2           { boolean to say -ve }
+  not.l   d0              { i has all bits reversed }
+  bne     @plusval        { i was not -1, so can continue }
+
+{ i was -1, so cannot go into following loop, as it now is zero }
+
+  moveq   #0,d2           { pretend i was positive }
+  move.l  #$80000000,d1   { set d1 correctly }
+  move.w  #31,d3          { shift value }
+  bra     @outloop        { continue }
+
+@plusval:
+  move.l  d0,d1           { save a copy of the original i }
+
+{ check for shortcuts with shifts }
+
+  and.l   #$ffffff00,d0   { shift by 23 ? }
+  bne     @bigger23       { no cheat available }
+  move.w  #23,d3          { shift value is 23 }
+  lsl.l   d3,d1           { shift copy of i }
+  bra     @nbigger        { continue }
+
+{ check for 15 bit shortcut shift }
+
+@bigger23:
+  move.l  d1,d0           { restore i }
+  and.l   #$ffff0000,d0   { shift by 15 ? }
+  bne     @nbigger        { no cheat available }
+  move.w  #15,d3          { shift value is 15 }
+  lsl.l   d3,d1           { shift copy of i }
+
+{ no shortcuts available }
+
+@nbigger:
+  move.l  d1,d0           { restore i }
+  and.l   #$40000000,d0   { if(!(i & 0x40000000)) }
+  bne     @outloop        { bit is set, no more shifts }
+  lsl.l   #1,d1           { shift copy of i }
+  addq.l  #1,d3           { increment shift count }
+  bra     @nbigger        { ensures i is restored }
+
+{ finished shifts - copy into qlfloat }
+{ correct shifted i is in d1, d0 contains i & 0x40000000 }
+
+@outloop:
+  move.w  #$81f,d4
+  sub.w   d3,d4           { set exponent correctly }
+  move.w  d4,(a0)+        { copy into exponent }
+
+{ difference here between positive and negative numbers
+; negative should just be shifted until first zero, so as we
+; have 2s complemented and shifted until first one, we must now
+; re-complement what is left }
+
+  tst.b   d2
+  beq     @setmant        { positive value here - just copy it }
+
+{ negative value, xor it with -1 shifted by same amount as in shift (d3)
+; to convert it back to -ve representation }
+
+  moveq.l #-1,d2          { set d2 to all $FFs }
+  lsl.l   d3,d2           { shift it by shift (d3 ) }
+  eor.l   d2,d1           { not the value by xoring }
+
+{ negative value restored by above }
+
+@setmant:
+  move.l  d1,(a0)         { copy into mantissa }
+@fin:
+  movem.l (sp)+,d2-d4/a0  { reset register variables and return value }
+  rts
+
+{ quick exit if zero }
+
+@zeroval:
+  move.w  d2,(a0)+        { zero exponent }
+  move.l  d2,(a0)         { zero mantissa }
+  bra     @fin
+end;
+
+
+function double_to_qlfp(qlf: Pqlfloat; val: Pdouble): Pqlfloat; assembler; nostackframe;
+asm
+{----------------------------- IEEE -----------------------------------
+; routine to convert IEEE double precision (8 byte) floating point
+; to a QLFLOAT_t.
+}
+  { pointer to qlfloat is in a0 }
+  move.l  (a1),d0        { high long of IEEE double }
+
+{ SNG - avoid loading low part for now so we can treat D1 as temporary }
+
+  add.l   d0,d0          { Put sign bit in carry }
+  lsr.l   #1,d0          { put zero where sign was }
+  bne     @notzero       { not zero }
+  move.l  4(a1),d1       { Test low bits too (probably zero!) }
+  bne     @notzero
+
+{ here the double was a signed zero - set the QLFLOAT_t and return }
+
+  move.w  d1,(a0)+       { We know that D1 is 0 at this point }
+  bra     @positive
+
+{ was not zero - do manipulations }
+
+@notzero:
+  move.l  d0,d1          { set non-signed high part copy }
+{                          We are going to lose least significant byte so we
+;                          can afford to over-write it.  We can thus take
+;                          advantage that the shift size when specified in
+;                          a register is modulo 64 }
+  move.b  #20,d0         { shift amount for exponent }
+  lsr.l   d0,d0          { get exponent - tricky but it works! }
+  add.w   #$402,d0       { adjust to QLFLOAT_t exponent }
+  move.w  d0,(a0)+       { set QLFLOAT_t exponent }
+
+{ now deal with mantissa }
+
+  and.l   #$fffff,d1     { get top 20 mantissa bits }
+  or.l    #$100000,d1    { add implied bit }
+  moveq   #10,d0         { shift amount ;; save another 2 code bytes }
+  lsl.l   d0,d1          { shift top 21 bits into place }
+
+  move.l  4(a1),d0       { get less significant bits }
+
+{                          We are going to lose least significant byte so we
+;                          can afford to over-write it.  We can thus take
+;                          advantage that the shift size when specified in
+;                          a register is modulo 64 }
+  move.b  #22,d0         { amount to shift down low long: not MOVEQ! }
+  lsr.l   d0,d0          { position low 10 bits of mantissa }
+  or.l    d0,d1          { D1 now positive mantissa }
+
+@lowzer:
+  tst.b   (a1)           { Top byte of IEEE argument }
+  bpl     @positive      { No need to negate if positive }
+  neg.l   d1             { Mantissa in D1 now }
+@positive:
+  move.l  d1,(a0)        { put mantissa in QLFLOAT_t }
+  subq.l  #2,a0          { correct for return address }
+  move.l  a0,d0          { set return value as original QLFLOAT_t address }
+end;
+
+end.