'ASM Bezier first source
'Ver. 2.1 (C) 2025 Thomas ™llinger
EQU START &C0C5
EQU MaxLevel 8
EQU XX &FA00 'BCD register X
EQU SRXL &F9E0 'current pen pos. in 0.1mm
EQU SRYL &F9EC

EQU EXPRESS &0274
EQU EXPREX &0277
EQU BCBINS &0241
EQU EOCHK3 &0265

EQU PRTIOCS &4008
EQU PINIT &00
EQU PGETMOD &2E
EQU PGRAPH &02
EQU PCOLOR &04
EQU PPENUP &0D
EQU PLTYPE &0F
EQU PAMVDN &14

EQU HTOA &0283
EQU PRTANK &0100

ORG START
  POP AF
  POP AF
  POP AF
  LD (&FEBE),A
  AND &70
  OR &06
  OUT (&31),A
  POP AF
  POP AF

  XOR A
  LD (&F07D),A
  OUT (&3D),A

  CALL PCHEK 'Is plotter there?
  RET C 'no plotter
  LD A,(HL)
  CP 59 ';
  JR NZ,syntax
  INC HL
  CALL RdArgs
  RET C 'ret on error
  JP DrwBezier
RdArgs:
  LD B,4 '# of points to read from args
  LD IY,X1 'dest.addr. for points.
  LD A,(HL)
  CP 45 '- use current pos. as start point
  JR NZ,rdCrd
  INC HL
  'copy current pen pos. to X1:
  PUSH HL
  LD HL,(SRXL)
  'only shift once because pen pos is already doubled.
  ADD HL,HL 'integer to fix 13.2
  LD (X1),HL
  LD HL,(SRYL)
  ADD HL,HL
  LD (Y1),HL
  POP HL
  LD B,2 '# of points to read from args.
  LD IY,X2 'dest. addr. for points.
rdCrd:
  CALL RdCoord 'X1,Y1,X2,Y2
  RET C
  LD A,(HL)
  CP 45 '-
  JR NZ,syntax
  INC HL
  LD B,4 '# of points to read
  LD IY,X3 'dest.addr. for points
  CALL RdCoord 'X3,Y3,X4,Y4
  RET C
  CALL RdOptArg
  RET C
  LD A,B
  CP 1
  JR NZ,endOfCmd
  LD A,E
  LD C,PLTYPE
  CALL PRT
  RET C  
  CALL RdOptArg
  RET C
  LD A,B
  CP 1
  JR NZ,endOfCmd
  LD A,E
  LD C,PCOLOR
  CALL PRT
  RET C  
endOfCmd:
  CALL EOCHK3 
  RET
RdOptArg:
'Reads an optional argument to DE.
'REG: AF,DE,HL
'B=0 if no argument read
'B=1 if argument read
' DE=argument value
'CF=1 on error, A=ErrCode.
  LD A,(HL)
  CP 44 ',
  LD B,0
  JR NZ,noArg
  INC HL
  CALL RdArg 'arg->DE
  LD B,1
noArg:
  OR A 'CF=0
  XOR A
  RET
syntax:
  LD A,1 'ErrCode
  SCF
  RET
RdCoord:
'Evaluate signed int. args in the format:
'(x1,y1,x2,y2) or (x1,y1)
'and store as Q13.2 in (IY),(IY+2),(IY-4),(IY-2).
'B=Number of vals to read (4 or 2)
'HL=Text read addr. 
'IY=Addr.of X1 in DEFW X2,Y2,X1,Y1
'RES: AF,BC,IX,XX
'HL=Addr. after read text
'IY=Addr. of X2.
'On error: CF=1, A=ErrCode.
  LD A,(HL)
  CP 40 '(
  JR NZ,syntax
  INC HL
  DEC B  
rdArg:
  PUSH BC
  PUSH IY
  CALL RdArg 'arg->DE
  POP IY
  POP BC
  RET C
  CALL DEtoIY
  BIT 0,B
  JR Z,decIY
  INC IY
  INC IY
  JP nxtArg
decIY:
  DEC IY
  DEC IY
  DEC IY
  DEC IY
  DEC IY
  DEC IY
nxtArg:
  LD A,(HL)
  CP 44 ',
  JR NZ,syntax
  INC HL
  DJNZ rdArg
  'Read the last arg that
  'is terminated by a ')':
  PUSH IY
  CALL RdArgX 'arg->DE
  POP IY
  RET C
  CALL DEtoIY
  LD A,(HL)
  CP 41 ')
  JR NZ,syntax
  INC HL
  RET
RdArgX:
'Same as RdArg but argument
'can be terminated with ')'.
'REG: AF,BE,DE,HL,IX,IY,XX
  CALL EXPREX
  JR toBin
RdArg:
'Read signed int. argument and
'and convert it to Q13.2
'fixed point number in DE.
'HL=Text read addr. 
'RES: AF,BE,IX,IY,XX
'DE=Q13.2 fixed point num.
'HL=Addr. after read text
'On error CF=1, A=ErrCode
  CALL EXPRESS
toBin:
  RET C 'RET on error
  LD A,(XX+4)
  CP &B3
  LD A,17 'error code
  CCF
  RET C 'error
  PUSH HL
  LD HL,XX
  CALL BCBINS 'XX->DE
  POP HL
  LD A,19 'Error code
  RET
DEtoIY:
'Convert the integer value
'in DE to 13.2 fix point
'format and write it to (IY).
'DE=Value
'IY=Dest. addr.
  EX DE,HL
  ADD HL,HL 'conv. to 13.2 fix point
  ADD HL,HL
  OR A 'CF=0 
  EX DE,HL
  LD (IY+0),E
  LD (IY+1),D
  RET
DrwBezier:
  CALL PltInit
  RET C
Subdivide:
  LD A,0
  LD (Level),A
  LD (FlatFlag),A
  LD HL,0
  LD (DirFlags),HL
  LD HL,x4
  LD (Idx),HL
repeat:
  LD IX,(Idx)
  CALL IsFlat '(FlatFlag=1 if flat)
  LD HL,(Idx)
  CALL SplitBezier
  LD HL,(DirFlags)
  ADD HL,HL 'DirFlags®1
  LD (DirFlags),HL

  LD A,(Level)
  INC A
  LD (Level),A
  CP MaxLevel
  JR Z,pltLines 'max.level reached

  LD A,(FlatFlag)
  CP 1
  JR NZ,moveDown 'not flat
pltLines:
  'Flat or MaxLevel reached:
  LD HL,(Idx)
  CALL PltSeg
  JR C,fine 'BREAK pressed or error
moveUp:
  LD A,(Level) 'Level-=1
  DEC A
  LD (Level),A
  LD HL,(DirFlags)
  SRL H 'HL¯=1
  RR  L
  LD (DirFlags),HL
  BIT 0,L 'are we left?
  JR NZ,moveUp
  'we are left: Idx-=3*4
  LD HL,(Idx)
  LD BC,12
  OR A
  SBC HL,BC
  LD (Idx),HL
  'switch to right:
  LD HL,(DirFlags)
  LD A,L
  OR 1
  LD L,A
  LD (DirFlags),HL

  JR chkLevel
moveDown:
  LD HL,(DirFlags)
  BIT 0,L 'are we left?
  JR NZ,chkLevel
  'we are left: Idx-=3*4
  LD HL,(Idx)
  LD BC,12
  ADD HL,BC
  LD (Idx),HL
chkLevel:
  LD A,(Level)  
  OR A
  JR NZ,repeat  
fine:
  CALL ENDPRT
  OR A
  RET
IsFlat:
'Check if the Bezier
'segment is flat (enough).
'P2 should be halfway btw. 
'P1 and P2; P3 halfway btw.
'P2 and P4.
'|x1+x3-x2-x2|+|x2+x4-x3-x3|
'+|y1+y3-y2-y2|+|y2+y4-y3-y3|
'<=distance_tol_manhatten
'IX: Address of P4 in the
' Bezier subdivision memoy block.
  LD A,0
  LD (FlatFlag),A
  LD H,(IX+13) 'x1
  LD L,(IX+12)
  SRL H 'Q13.2 to integer
  RR L
  SRL H
  RR L

  LD B,(IX+5) 'x3
  LD C,(IX+4)
  SRL B
  RR C
  SRL B
  RR C

  ADD HL,BC 'x1+x3

  LD B,(IX+9) 'x2
  LD C,(IX+8)
  SRL B
  RR C
  SRL B
  RR C

  OR A 'clear carry
  SBC HL,BC 'x1+x3-x2
  OR A
  SBC HL,BC 'x1+x3-x2-x2
  LD A,H
  BIT 7,H
  JR Z,pos1 'already positive
  CALL PosHL 'make HL positive
pos1:
  PUSH HL '|x1+x3-x2-x2|
  'BC=x2
  LD H,(IX+1) 'x4
  LD L,(IX+0)
  SRL H
  RR L
  SRL H
  RR L
  ADD HL,BC 'x4+x2
  LD B,(IX+5) 'x3
  LD C,(IX+4)
  SRL B
  RR C
  SRL B
  RR C
  OR A
  SBC HL,BC
  OR A
  SBC HL,BC 'x4+x2-x3-x3
  LD A,H
  BIT 7,H
  JR Z,pos2
  CALL PosHL
pos2:
  'HL=|x4+x2-x3-x3|
  POP BC 'BC=|x1+x3-x2-x2|
  ADD HL,BC
  PUSH HL

  LD H,(IX+15) 'y1
  LD L,(IX+14)
  SRL H
  RR L
  SRL H
  RR L
  LD B,(IX+7) 'y3
  LD C,(IX+6)
  SRL B
  RR C
  SRL B
  RR C
  ADD HL,BC
  LD B,(IX+11) 'y2
  LD C,(IX+10)
  SRL B
  RR C
  SRL B
  RR C
  OR A
  SBC HL,BC
  OR A
  SBC HL,BC 'y1+y3-y2-y2

  LD A,H
  BIT 7,H
  JR Z,pos3
  CALL PosHL
pos3:
  PUSH HL '|y1+y3-y2-y2|
  'BC=y2
  LD H,(IX+3) 'y4
  LD L,(IX+2)
  SRL H
  RR L
  SRL H
  RR L
  ADD HL,BC 'y4+y2
  LD B,(IX+7) 'y3
  LD C,(IX+6)
  SRL B
  RR C
  SRL B
  RR C
  OR A
  SBC HL,BC
  OR A
  SBC HL,BC 'y4+y2-y3-y3
  LD A,H
  BIT 7,H
  JR Z,pos4
  CALL PosHL
pos4:
  POP BC
  ADD HL,BC
  POP BC
  ADD HL,BC  

  LD A,H
  CP 0
  JR NZ,notFlat
  LD A,L
  CP 4
  JR C,flat
  JR Z,flat 
notFlat:
  RET
flat:
  LD A,1
  LD (FlatFlag),A
  RET
      
negFl:
  DEFB 0
t1:
  DEFB "1234"
t2:
  DEFB "5678"
res:
  DEFB "ABCD"
SplitBezier:
'HL: address of 4 points
'each point has two signed
'16bit values as x and y
'coordinates. Following the 4 points
'there has to be space for another 3 points.
'Order of points in mem: P4,P3,P2,P1
'REG: BC,DE,IX,IY
  PUSH HL 'save start of points mem.
  'Copy P1 to pos 7:
  LD BC,12
  ADD HL,BC 'src of P1
  PUSH HL
  ADD HL,BC 'dest of P1
  LD D,H
  LD E,L  
  POP HL 'src of P1
  LD BC,4
  LDIR
  
  POP HL
  PUSH HL
  'Create P12 in pos 6:
  LD BC,8
  ADD HL,BC 'src of P2
  PUSH HL
  POP IX
  LD BC,12
  ADD HL,BC 'dest of P12
  PUSH HL
  POP IY
  CALL MidPoint

  POP HL 'addr of P4
  PUSH HL 
  'Create P23 in pos 4:
  INC HL
  INC HL
  INC HL
  INC HL
  PUSH HL 'src of P3
  POP IX
  LD BC,8
  ADD HL,BC
  PUSH HL 'dest of P23
  POP IY
  CALL MidPoint

  'Create P123 in pos 5:
  PUSH IY
  POP IX 'src of P23
  INC IY
  INC IY
  INC IY
  INC IY 'dest of P123
  CALL Mid2Point

  'Create P34 in pos 2:
  POP HL 'addr of P4
  PUSH HL
  PUSH HL 
  POP IX 'src of P4
  INC HL
  INC HL
  INC HL
  INC HL
  PUSH HL
  POP IY 'dest of P34
  CALL MidPoint

  'Create P234 in pos 3:
  PUSH IY
  POP IX 'src of P34
  INC IY
  INC IY
  INC IY
  INC IY 'dest of P234
  CALL Mid2Point

  'Create P1234 in pos 4:
  PUSH IY
  POP IX 'src of P234
  INC IY
  INC IY
  INC IY
  INC IY 'dest of P1234
  CALL Mid2Point

  POP DE
  RET
MidPoint:
'IX: address of line, that
'are four signed 16bit
'values (x1,y1,x2,y2)
'IY: address where calc.
'mid point should be stored.
'REG: HL,BC
  LD H,(IX+1)
  LD L,(IX+0)
  LD B,(IX+5)
  LD C,(IX+4)
  ADD HL,BC
  SRA H 'SRA HL
  RR  L
  LD (IY+0),L
  LD (IY+1),H
  LD H,(IX+3)
  LD L,(IX+2)
  LD B,(IX+7)
  LD C,(IX+6)
  ADD HL,BC
  SRA H 'SRA HL
  RR  L
  LD (IY+2),L
  LD (IY+3),H
  RET
Mid2Point:
'Same as MidPoint, but the
'two source points have a
'distance of one point.
  LD H,(IX+1)
  LD L,(IX+0)
  LD B,(IX+9)
  LD C,(IX+8)
  ADD HL,BC
  SRA H 'SRA HL
  RR  L
  LD (IY+0),L
  LD (IY+1),H
  LD H,(IX+3)
  LD L,(IX+2)
  LD B,(IX+11)
  LD C,(IX+10)
  ADD HL,BC
  SRA H 'SRA HL
  RR  L
  LD (IY+2),L
  LD (IY+3),H
  RET
'END OF BEZIER1.ASM
