Page 2 of 3
Posted: Thu Feb 17, 2011 11:56 pm
by CurtisP
darkatx wrote:This looks just perfect for my needs!
Thanks for the effort put into this!
I just uploaded version 0.21 and updated the link in the OP accordingly. So download it and give it a try.
BTW, what is your favorite assembler?
Posted: Fri Feb 18, 2011 11:17 am
by darkatx
Right now, I'm using CC65 for what I'm trying to tackle in ML here.
I'm pretty excited about trying it out after I download it tonight!
Posted: Fri Feb 18, 2011 11:50 am
by Mike
Mike wrote:Presumably it is the hires toolkit written by Thomas Magnusson you are using. That one was featured in the OP of the thread '
Hires Graphics' (
download) and it includes a line routine and an interface that fits your description, but it is not related to MINIGRAFIK.
CurtisP wrote:Yes, that is the one I am using. Sorry for the confusion.
O.K., that being cleared up, here's one question: How would you implement an ellipse drawing routine in TCB? A version which only uses MINIGRAFIK commands could be written as follows (all variables and expressions contain integer values):
Code: Select all
10 @ON:@CLR
11 CX=80:CY=96:A=57:B=95:GOSUB15
12 GETA$:IFA$=""THEN12
13 @RETURN:END
14 :
15 X=0:Y=B:S=B*B:T=A*A*(2*Y-1):U=2*B*B:V=2*A*A:E=0
16 X1=CX+X:Y1=CY+Y:P1=X1>=0ANDX1<160:P2=Y1>=0ANDY1<192
17 X2=CX-X:Y2=CY-Y:P3=X2>=0ANDX2<160:P4=Y2>=0ANDY2<192
18 IFP1ANDP2THEN:@1,X1,Y1
19 IFP1ANDP4THEN:@1,X1,Y2
20 IFP3ANDP4THEN:@1,X2,Y2
21 IFP3ANDP2THEN:@1,X2,Y1
22 IFX=AANDY=0THENRETURN
23 F=E+S:D=0
24 G=F-T:IFABS(F)>ABS(G)THENF=G:D=1
25 G=E-T:IFABS(F)>ABS(G)THENF=G:D=2
26 E=F
27 IFD<2THENX=X+1:S=S+U
28 IFD>0THENY=Y-1:T=T-V
29 GOTO16
CX and CY are the centre co-ordinates, and A and B are horizontal and vertical radii of the ellipse. This way, the non-square aspect ratio of the pixels can be taken account for. The example is assumed to plot a centered circle on a PAL VIC-20 (on an NTSC VIC-20 it will appear vertically lengthened, use A=71 instead in line 11).
Posted: Fri Feb 18, 2011 7:08 pm
by CurtisP
Mike wrote:O.K., that being cleared up, here's one question: How would you implement an ellipse drawing routine in TCB? A version which only uses MINIGRAFIK commands could be written as follows (all variables and expressions contain integer values):
I will need to implement 16-bit (word) variables. My main stumbling block was that the 6502 only has an 8 bit accumulator, so I would need to use memory locations as an accumulator, and that would require making the code machine specific. But I recently realized I can temporarily store the high and low byte in Y and X.
I should have an update later tonight.
Posted: Sat Feb 19, 2011 12:27 am
by CurtisP
16 bit variables weren't quite as hard as I thought they would be.
I've got them working in expressions and functions and in the INCR, DECR, SHIFTL, and SHIFTR commands.
I just need to add some extra parameter checking to function calls and upgrade conditionals to 16 bits.
Hopefully I will have something new to post tomorrow.
Posted: Sat Feb 19, 2011 10:19 pm
by CurtisP
I just finished coding 16 bit comparisons. I still have to test them, but here is the code that I am currently generating:
Code: Select all
;IF FOO! = BAR! THEN GOSUB YES
LDY wrdFOO+1
LDA wrdFOO
CPY wrdBAR+1
BNE clb00001
CMP wrdBAR
BNE clb00001
JSR lblYES
clb00001
;IF FOO! <> BAR! THEN GOSUB YES
LDY wrdFOO+1
LDA wrdFOO
CPY wrdBAR+1
BEQ clb00002
CMP wrdBAR
BEQ clb00002
JSR lblYES
clb00002
;IF FOO! > BAR! THEN GOSUB YES
LDY wrdFOO+1
LDA wrdFOO
CPY wrdBAR+1
BMI clb00003
BNE clb00004
CMP wrdBAR
BMI clb00003
BEQ clb00003
clb00004
JSR lblYES
clb00003
;IF FOO! >= BAR! THEN GOSUB YES
LDY wrdFOO+1
LDA wrdFOO
CPY wrdBAR+1
BMI clb00005
BNE clb00005
CMP wrdBAR
BMI clb00005
JSR lblYES
clb00005
;IF FOO! < BAR! THEN GOSUB YES
LDY wrdFOO+1
LDA wrdFOO
CPY wrdBAR+1
BPL clb00006
BNE clb00007
CMP wrdBAR
BPL clb00006
clb00007
JSR lblYES
clb00006
;IF FOO! <= BAR! THEN GOSUB YES
LDY wrdFOO+1
LDA wrdFOO
CPY wrdBAR+1
BEQ clb00009
BCS clb00008
BNE clb00010
clb00009
CMP wrdBAR
BEQ clb00011
BCS clb00008
clb00011
clb00010
JSR lblYES
clb00008
Posted: Sun Feb 20, 2011 5:20 am
by Mike
Mike wrote:[...](all variables and expressions contain integer values):
CurtisP wrote:I will need to implement 16-bit (word) variables. [...]
Actually, I meant values without fractional digits. I did not make a statement about the value range, though: CX and CY should be 16-bit signed integer (so the centre of the ellipse may be placed outside the screen in all directions), A and B may sensibly be restricted to unsigned byte, i.e. 0 .. 255. Even with that last restriction, the values in E, F, G, S, T, U, and V require 32 bits to be represented.
My main stumbling block was that the 6502 only has an 8 bit accumulator, so I would need to use memory locations as an accumulator, and that would require making the code machine specific.
What is the problem setting aside a data area to handle 2- or 4-byte values? Then you add two 32-bit values like this:
Code: Select all
CLC
LDA A
ADC B
STA C
LDA A+1
ADC B+1
STA C+1
LDA A+2
ADC B+2
STA C+2
LDA A+3
ADC B+3
STA C+3 ; C = A + B: A, B, C either all signed or all unsigned, 32-bit values
I just finished coding 16 bit comparisons. I still have to test them, but here is the code that I am currently generating:
You should try this one:
Code: Select all
LDA A
CMP B
LDA A+1
SBC B+1
BCC skip
JSR A_gteq_B ; A >= B: A, B unsigned 16-bit values
.skip
[...]
Actually, these two example code snippets above only cover basic knowledge about 6502 machine language. The carry flag is there to support multi-byte operations.
Posted: Sun Feb 20, 2011 6:39 pm
by CurtisP
What my example code didn't show was the ability to include mathmatical expressions within the comparison.
Code: Select all
;IF A!+B!-C! = D! THEN LET E! = G! & H!
LDY wrdA+1
LDA wrdA
CLC
ADC wrdB
PHA
TYA
ADC wrdB+1
TAY
PLA
SEC
SBC wrdC
PHA
TYA
SBC wrdC+1
TAY
PLA
CPY wrdD+1
BNE clb00012
CMP wrdD
BNE clb00012
LDY wrdG+1
LDA wrdG
AND wrdH
PHA
TYA
AND wrdH+1
TAY
PLA
STA wrdE
STY wrdE+1
clb00012
Posted: Mon Feb 21, 2011 4:14 pm
by Mike
I see you insist on not storing intermediate results in memory.
That gets rather complicated though if you move beyond what could be hold in A, X, Y at once - 24 bits - even more difficult if you (need to) use the stack.
Yet, your example can easily be shortened to:
Code: Select all
CLC
LDA wrdA
ADC wrdB
TAX
LDA wrdA+1
ADC wrdB+1
TAY
SEC
TXA
SBC wrdC
TAX
TYA
SBC wrdC+1
CMP wrdD+1 ; instead of TAY:CPY wrdD+1
BNE skip
CPX wrdD
BNE skip
LDA wrdG
AND wrdH
STA wrdE
LDA wrdG+1
AND wrdH+1
STA wrdE+1
.skip
... omitting any stack usage as well.
IMO, it is rather wasteful to commit the X and Y registers as "accumulators" over any extended period of time. They are more useful as counters or index registers.
Posted: Mon Feb 21, 2011 8:58 pm
by CurtisP
Hand optimized code is almost always going to beat compiler generated code.
I tested all the sixteen-bit compare code I had writtem and surprisingly, it worked perfectly.
Posted: Tue Feb 22, 2011 10:28 pm
by CurtisP
Mike wrote:O.K., that being cleared up, here's one question: How would you implement an ellipse drawing routine in TCB? A version which only uses MINIGRAFIK commands could be written as follows (all variables and expressions contain integer values):
Well dang. All variations of the mid-point algorithm require not only multiplication, but signed arithmetic as well. I wasn't planning on implementing the latter at all.
Posted: Thu Feb 24, 2011 11:16 pm
by CurtisP
OK, an elipse drawing program would look something like this (i say something like this, because this code is untested).
Code: Select all
REM Draw Ellipse using Bresenham Algorithm
IMPORT "LIB"
INCLUDE "MACROS"
IMPORT "GFX"
INCLUDE "MACROGFX"
IMPORT "MTH"
INCLUDE "MACROMTH"
DIM rx, ry, cx, cy, z
LET cx=64
LET cy=64
LET rx=20
LET ry=30
GRAPHICS
CLEAR
GOSUB BELLIPSE
INP z
EXIT
LABEL BELLIPSE
REM Draw Ellipse
REM rx = x radius, ry = y radius, cx = center x, cy = center y
DIM d1!,rxsq!,rysq!,tworxsq!,tworysq!,dx!,dy!,z!
DIM i,gd,gm,x,y
LET rxsq!=MULT(rx,rx)
LET rysq!=MULT(ry,ry)
REM tworxsq=2*rxsqy
LET tworxsq!=rxsq!
SHIFTL tworxsq!
REM tworysq=2*rysq
LET tworysq!=rysq!
SHIFTL tworysq!
LET x=0
LET y=ry
REM d1=rysq-rxsq*ry+(0.25*rxsq)
LET d1!=MULTW(rxsq!,ry)
SHIFTR rxsq!
SHIFTR rxsq!
LET d1!=rysq!-d1!+rxsq!
LET dx!=MULTW(tworysq,x)
LET dy!=MULTW(tworxsq,y)
WHILE Y>0
LET z=cy+y
PLOT cx+x,z
PLOT cx-x,z
LET z=cy-y
PLOT cx-x,z
PLOT cx+x,z
LET x=x+1
LET dx!=dx!+tworysq!
IF d1! >= $8000 THEN
LET d1!=d1!+dx!+rysq!
ELSE
LET y=y-1
LET dy!=dy!-tworxsq!
LET d1!=d1!+dx!-dy!+rysq!
ENDIF
WEND
RETURN
Posted: Fri Feb 25, 2011 9:34 am
by Mike
CurtisP wrote:OK, an elipse drawing program would look something like this (i say something like this, because this code is untested).
No need to test it, because the routine won't work anyway, for several reasons.
The code you wrote above was most probably inspired by an implementation of the mid-point algorithm by Gursharan Singh Tatla found on
eazynotes, albeit it misses one half of it. As soon as the slope magnitude is greater than one, your version is not able anymore to follow the shape, furthermore the two points intersecting the x-axis are missing:
(green shows the ellipse with rx=100, ry=200; red the overlaid output of your routine)
But you don't even get to this point. As I noted above, some variables require at least 32 bits for storage if you want to draw half-axes up to 255. Integer overflow will make the 16-bit values in 'tworxsq', 'tworysq', 'dx', 'dy', and 'd1' completely meaningless for anything bigger than rx,ry>30.
The unaltered algorithm by Gursharan Singh Tatla is not without problems, either. First, it still requires float variables, second, ellipses with small values of ry and big values of rx are not drawn correctly - they end up too short.
Posted: Sat Feb 26, 2011 2:04 pm
by CurtisP
Mike wrote:No need to test it, because the routine won't work anyway, for several reasons.
Thanks for the input. I really think that any kind of Bressenham or Mid-Point Algorithm is outside the scope of small or tiny languages.
Posted: Sat Feb 26, 2011 2:52 pm
by Mike
CurtisP wrote:I really think that any kind of Bressenham or Mid-Point Algorithm is outside the scope of small or tiny languages.
A Bresenham-style line routine for a resolution up to 256x256 pixels implements quite easily with
anything that supports 8-bit values. The corresponding code in MINIGRAFIK is roughly 100 bytes in size.
There's already an implementation of the ellipse routine I posted above in 6502 code, it is part of MAXIGRAFIK. This routine is
slightly larger, 700 to 900 bytes, that depends on whether the workspace variables are put into zeropage, or not. As you noted, multiplication and signed arithmetic needed to be done, in a way not wasting too much space. The multiplication does 8-bit factor x 24-bit factor -> 32-bit result.
At the time I went to code it, I would have been very thankful for a small language or the like for the 6502 supporting 32-bit values. Maybe even something like SWEET16 for the Apple II, but with 32-bit registers instead. So now, everything is done 'by hand' - 4x LDA,ADC,STA for additions, etc. Judicious use of subroutines for otherwise identical code sections helped (a bit) to keep down the size.
Thanks for the input.
Smoke testing the routine in, say, CBM BASIC + MINIGRAFIK would have revealed immediately, that your implementation was not working. And, unlike logic errors buried deep within some code, the errorneous graphics output is right before your eyes.
I know I was not too subtle about that.
All in all, the usefulness of TCB will be measured against the number of programs which will be written with it. The inclusion of 32-bit arithmetics would help to reach a bigger audience.
Greetings,
Michael