Concept & Design of 3.3V Parallel 16-bit VGA Boards

Topics relating to PALs, CPLDs, FPGAs, and other PLDs used for the support or creation of 65-family processors, both hardware and HDL.
User avatar
Arlet
Posts: 2353
Joined: 16 Nov 2010
Location: Gouda, The Netherlands
Contact:

Re: Concept & Design of 3.3V Parallel 16-bit VGA Boards

Post by Arlet »

If you don't count the code for the straight line segments, and the sine tables, the polygon method takes less code. And like I said, it works well for partial circles. The Bresenham circle algorithm is probably the fastest, if implemented well.
ElEctric_EyE
Posts: 3260
Joined: 02 Mar 2009
Location: OH, USA

Re: Concept & Design of 3.3V Parallel 16-bit VGA Boards

Post by ElEctric_EyE »

I'm going to bring back the hardware Random Number Generator for some speed tests with randomly placed origins, colors, radii, etc.
Also I would like to try filled circles, which would be done by decreasing the max radius by one until zero and plotting the circles around each origin. Also, if there's 8 quadrants I can envision some kind of math affecting the color to simulate a kind of light source to make a 3d effect. Just thinking as for an experiment. I'm not hell bent on it.
Also, I want to do a spiral where the radius decreased by some amount before the circumference is complete.

I would like to do all this without the use if lookup tables, load down the system then do a comparison with the LUT's. I have plenty of blockRAM left.
User avatar
8BIT
Posts: 1787
Joined: 30 Aug 2002
Location: Sacramento, CA
Contact:

Re: Concept & Design of 3.3V Parallel 16-bit VGA Boards

Post by 8BIT »

I tried the fill circle by reducing the radius but it leaves holes scattered. My 320x240 display modified the circle routine to draw horizontal lines from top to bottom, using the endpoints calculated by the original circle routine. It runs fast and leaves no holes. I do not have 65c02 code for it but its not hard to figure out.

Daryl
Please visit my website -> https://sbc.rictor.org/
ElEctric_EyE
Posts: 3260
Joined: 02 Mar 2009
Location: OH, USA

Re: Concept & Design of 3.3V Parallel 16-bit VGA Boards

Post by ElEctric_EyE »

8BIT wrote:
I tried the fill circle by reducing the radius but it leaves holes scattered...Daryl
Yes, this is what I would've expected too. We have several algorithms donated now. Might be good to check them all out. I just checked out Lee's since I have it in my system already. But you can see also where there should have been some vertical tracing at the very right edge, but there is nothing. And this error eats its way to the center, but the rest seems ok.

Just in case I made an error in the code, I post it... Been a long day at work, but this is some good motivating stuff!

Code: Select all

START:	    LDA #$0000
            TAZP                 ;SET ZEROPAGE @$0000_0000
            LDA #$0001
            TASP                 ;SET STACKPAGE @$0001_0000
            
            LDWi $0000           ;  LDW #$0000 - SET BLACK SCREEN ACCORDING TO COLOR LUT
            LDBaw COLTABLE       ;  LDB COLTABLE,w
            STBzp SCRCOL          ;  STB SCRCOL
            JSR CLRSCR
            
            LDWi $0005           ;  LDW #$0005 - SET PIXEL COLOR GREEN ACCORDING TO COLOR LUT
            LDBaw COLTABLE       ;  LDB COLTABLE,w
            STBzp PXLCOL         ; B ACCUMULATOR, PIXELCOLOR
            LDA #$0000
            STA Numberh
            LDFi $00F0           ;F ACCUMULATOR, RADIUS
AD          STFzp Number         ;RADIUS
            JSR Square
            LDA Sqr
            STA RSQR             ;RSQR= R^2
            CLC
            
            LDWi $0000           ; LDW #$0000
            
AC          LDA #$0140           ; X CENTER OF 640x480 SCREEN 
            STA SCRLO
            LDA #$80F0           ; Y CENTER OF 640x480 SCREEN
            STA SCRHI            ; START @ORIGIN
                        
            STWzp Number         ; This 'Number' is actually X 
            JSR Square           ; Now square it!
            LDA RSQR             ; Radius SQUARED
            SBC Sqr              ; Sqr=Number^2
            STA Numberl          ; This Numberl IS (R^2-X^2)
            JSR SqRoot           ; THIS ROUTINE WILL RETURN Root=SQRT(Numberl)
            
            TWY
            LDA SCRHI            ;A ACCUMULATOR, Y COORDINATE
            SBCAopCzp Root       ;C ACCUMULATOR, Y MATH
            STCzp SCRHI
            STBiy SCRLO          ;PLOT 1st Quadrant
            ADCAopCzp Root
            STCzp SCRHI
            STBiy SCRLO          ;PLOT 2nd Quadrant
                                    
            INW
            CPWi $00EF           ; CPW #$00EF
            BNE AC
            DECF
            CMPFi $0000
            BNE AD
           
END         JMP END
Attachments
P1000967.JPG
ElEctric_EyE
Posts: 3260
Joined: 02 Mar 2009
Location: OH, USA

Re: Concept & Design of 3.3V Parallel 16-bit VGA Boards

Post by ElEctric_EyE »

Maybe a better way would be to draw lines from the center to the circumference.

Daryl you wouldn't happen to have Bresenham line algorithm would you?

EDIT: Sorry, I missed this part of your post originally:
8BIT wrote:
...using the endpoints calculated by the original circle routine...

Daryl
User avatar
8BIT
Posts: 1787
Joined: 30 Aug 2002
Location: Sacramento, CA
Contact:

Re: Concept & Design of 3.3V Parallel 16-bit VGA Boards

Post by 8BIT »

If I understand you correctly, drawing something like a radar sweep? I have line drawing routines that work relatively well, but I think you will still end up with holes.

If you look closely at my circle routine, you see that it only calculates points over a 45 degree arc. It then just uses the mirror image to calculate the other 7 arcs that complete the circle.

x = 0-45 - calculated
x1 = 90 - x
x2 = 90 + x
x3 = 180 - x
x4 = 180 + x
x5 = 270 - x
x6 = 270 + x
x7 = 360 - x

By choosing two of the end points in pairs, you just draw a horizontal line which is much faster than a sloped line.

x1 to x2
x to x3
x4 to x7
x5 to x6

This can also be done with vertical lines equally fast.

Hope this helps.

BTW, here is the line code (unedited from cc65 this time... you will need to edit it some and convert the local lables to globals)

All variables are in ZP. Some are two byte, others are one byte.

Code: Select all

; ------------------------------------------------------------------------
; LINE: Draw a line from X1/Y1 to X2/Y2, where X1/Y1 = ptr1/ptr2 and
; X2/Y2 = ptr3/ptr4 using the current drawing color.
;
; Must set an error code: NO
;

LINE:

@CHECK: lda     X2           ;Make sure x1<x2
        sec
        sbc     X1
        tax
        lda     X2+1
        sbc     X1+1
        bpl     @CONT
        lda     Y2           ;If not, swap P1 and P2
        ldy     Y1
        sta     Y1
        sty     Y2
        lda     Y2+1
        ldy     Y1+1
        sta     Y1+1
        sty     Y2+1
        lda     X1
        ldy     X2
        sty     X1
        sta     X2
        lda     X2+1
        ldy     X1+1
        sta     X1+1
        sty     X2+1
        bcc     @CHECK

@CONT:  lsr
	  sta     DX+1
        txa			
        ror		
        sta     DX
        bne     @NVT  
        jmp     @VERT
@NVT:   stz     IX
        stz     IY	
        sec
        lda     Y2           ;Calculate dy
        sbc     Y1
        bne     @NHZ
	  jmp     @HORZ
@NHZ:	  bcc	    @YNEG
@YPOS:  lsr
        sta     DY
        jsr    SETPIXELCLIP		;plot first point
@LOOP0: ldx     #$00
@LOOP:  clc
        lda     IX
        adc     DX
        sta     IX
        bcc     @LOOP1
        inx
        inc     X1
        bne     @LOOP1
        inc     X1+1
@LOOP1: clc
        lda     IY
        adc     DY
        sta     IY
        bcc     @LOOP2
	  inx
        INC     Y1  
@LOOP2: txa
        beq     @LOOP     ; no adjustment yet
        jsr    SETPIXELCLIP
        lda    DX
        beq    @END0      ; if DX=0, skip x end test
        lda     X1
        cmp     X2
        bne     @END0
        lda     X1+1 
        cmp     X2+1
        beq     @END
@END0:  lda     DY
        beq     @LOOP0    ; if dy=0, skip y end test   
        lda     Y1
        cmp     Y2
        bne     @LOOP0    ; reset x=0         
@END:   lda     X2
        sta     X1
        lda     X2+1
        sta     X1+1
        lda     Y2
        sta     Y1
        jsr     SETPIXELCLIP
	  rts	

@YNEG:  jsr     SETPIXELCLIP		;plot first point
        lda     Y1
        sec
        sbc     Y2
        lsr
        sta     DY
@LOOPX: ldx     #$00
@LOOP3: clc
        lda     IX
        adc     DX
        sta     IX
        bcc     @LOOP4
        inx
        inc     X1
        bne     @LOOP4
        inc     X1+1
@LOOP4: clc
        lda     IY
        adc     DY
        sta     IY
        bcc     @LOOP5
	  inx
        DEC     Y1  
@LOOP5: txa
        beq     @LOOP3     ; no adjustment yet
        jsr    SETPIXELCLIP
        lda     DX
        beq     @LOOP6      ; if DX=0, skip x end test
        lda     X1
        cmp     X2
        bne     @LOOP6
        lda     X1+1
        cmp     X2+1
        beq     @END
@LOOP6: lda     DY
        beq     @LOOPX      ; reset x
        lda     Y1
        cmp     Y2
        beq     @END
        bra     @LOOPX         

@VERT:  sec
        lda     Y2           ;Calculate dy
        sbc     Y1
	  bcc	    @VNEG
        bra     @VP2
@VP1:   inc    Y1
@VP2:   jsr    SETPIXELCLIP
        sec
	  lda    Y2
        sbc    Y1
        bne    @VP1
        rts
@VN1:   dec    Y1
@VNEG:  jsr    SETPIXELCLIP
        sec
	  lda    Y2
        sbc    Y1
        bne    @VN1
        rts

@HRZ:   inc    X1
        bne    @HORZ
        inc    X1+1
@HORZ:  jsr    SETPIXELCLIP
        sec
	  lda    X2
        sbc    X1
        bne    @HRZ
        lda    X2+1
        sbc    X1+1
        bne    @HRZ
        rts
Daryl
Please visit my website -> https://sbc.rictor.org/
User avatar
8BIT
Posts: 1787
Joined: 30 Aug 2002
Location: Sacramento, CA
Contact:

Re: Concept & Design of 3.3V Parallel 16-bit VGA Boards

Post by 8BIT »

Guess I should explain the method for the line drawing routine. I thought this up myself about 30 years ago but doubt that it's an original idea.

First set it up so you increment from x1 to x2, swapping the two points if needed.
You calculate the difference between x1 and x2, store it in dx. Do the same for y1 and y2 stored in dy.
Test for vertical or horizontal lines, and move to optimized code for either.

Now test to see if you will increment or decrement y and point to the proper code section.

Create new variables ix and iy and set to zero. Now, simply add dx to ix and if it carries, inc x1. Then add dy to iy and if it carries, increment y1 (or decrement based in the test above). Plot the new x1,y1 point only when x1 or y1 change.

Keep adding dx to ix and dy to iy until x1 reaches x2.

The loop cycles more times but since there is no multiplication or division, it runs quite fast.

Daryl
Please visit my website -> https://sbc.rictor.org/
ElEctric_EyE
Posts: 3260
Joined: 02 Mar 2009
Location: OH, USA

Re: Concept & Design of 3.3V Parallel 16-bit VGA Boards

Post by ElEctric_EyE »

Awesome. That's all I need is the code. I can work with that and adapt it. Again, thanks a million!
ElEctric_EyE
Posts: 3260
Joined: 02 Mar 2009
Location: OH, USA

Re: Concept & Design of 3.3V Parallel 16-bit VGA Boards

Post by ElEctric_EyE »

Added a random number generator. So far it's only being used for the color look up table for plotting pixels. So the pic above is now made of randomized pixels. Also, in a separate experiment, I made it so each concentric circle had its own color as I was originally trying to perform a filled-in circle.
The idea now is to use the RNG to plot many circles, each with a random radius, position and color as fast as 65Org16 machinely possible.

Also, I've been working on a simple start/stop counter based on the 100MHz clock to quantify testing. I already think I know which one will be the highest speed, so I will concentrate first on the accuracy of the timer, then on that conversion to 65Org16 on my next 2 days off.

I have my timer circuit almost done in Verilog. 2 things left for the timer, then to plot characters utilizing the new plotting setup using indirect indexed. This way I'll be able to plot the timing results, I will need text!

I decided to post the code now, even though the reset bit isn't working on the counter yet...just in case anyone sees a better way of an overall Timer construction! I'm thinking this Timer is using too many resources.

I use extremely loose address decoding in order to save FPGA resources.

Code: Select all

`timescale 1ns / 1ps

module Timer(input clk,
				 input [15:0] cpuDO,				//cpu databus out
				 input cpuWE,						//cpu write, active high
				 input cntrCS,						//active high from $c000_0000-$cfff_ffff
				 output reg [15:0] countDO		//from count [15:0] to cpuDataIn
				 );

reg [19:0] counter = 0;		//main .01sec counter from 100MHz clock

reg [4:0] seconds = 0;		//up to 32 seconds
reg [3:0] tens = 0;			//10 tenths
reg [6:0] hundreds = 0;		//100 hundredths

wire [15:0] count;

reg stp = 0; 					//control bit for stop
reg res = 0;					//control bit for reset main .01sec counter

assign count [15:0] = {seconds,tens,hundreds};  //assign the bit values when reading the counter

always @(posedge clk)
	if(cntrCS) begin
		if(cpuWE) begin
			res <= cpuDO [15];				//write to $c000_0000, bit 15 = reset
			stp <= cpuDO [14];				//write to $c000_0000, bit 14 = stop
		end
			else  
			countDO <= count;		//read from $c000_0000 16-bit count {sec,tens,hundreds} = xxxxx_xxxx_xxxxxxx
	end
	else countDO <= 16'h0000;

always @(posedge clk) begin
		if (res)
			counter <= 20'b0000_0000_0000_0000_0000;
		
		if (tens == 4'b1001) begin
			tens <= 4'b0000;
			seconds <= seconds + 1;
		end
		else if (hundreds == 7'b110_0011) begin
			hundreds <= 7'b0000000;
			tens <= tens + 1;
		end
				
		if (stp)
			counter <= counter;
			else if (counter >= 20'b1111_0100_0010_0011_1111) begin 	//0 to 999,999 for .01sec counter
			counter <= 20'b0000_0000_0000_0000_0000;
			hundreds <= hundreds + 1;
			end
				else
				counter <= counter + 1;
end
			
endmodule
ElEctric_EyE
Posts: 3260
Joined: 02 Mar 2009
Location: OH, USA

Re: Concept & Design of 3.3V Parallel 16-bit VGA Boards

Post by ElEctric_EyE »

This works, now onto converting Daryl's Bresenham...
Added toggle to reset:

Code: Select all

module Timer(input clk,
				 input [15:0] cpuDO,				//cpu databus out
				 input cpuWE,						//cpu write, active high
				 input cntrCS,						//active high from $c000_0000-$cfff_ffff
				 output reg [15:0] countDO		//from cpuDataOut
				 );

reg [19:0] counter = 0;		//main .01sec counter from 100MHz clock

reg [4:0] seconds = 0;		//up to 31.99 seconds
reg [3:0] tens = 0;			//10 tenths (0..9)
reg [6:0] hundreds = 0;		//100 hundredths 0..99)

wire [15:0] count;

reg stp = 0; 					//control bit for stop(hold)
reg res = 0;					//control bit to reset main .01sec counter

assign count [15:0] = {seconds,tens,hundreds};  //assign the bit values ($xxxxx_xxxx_xxxxxxx) when reading the counter

always @(posedge clk) begin
	if(cntrCS & cpuWE) begin
			res <= cpuDO [15];				//a write to $c000_0000, bit 15 = reset
			stp <= cpuDO [14];				//a write to $c000_0000, bit 14 = stop
		end
	if(cntrCS & !cpuWE) 
		countDO [15:0] <= count [15:0];	//read from $c000_0000 16-bit count {sec,tens,hundreds} = xxxxx_xxxx_xxxxxxx
			else 
			countDO [15:0] <= 16'h0000;
	if (res)
		res <= ~res;							//auto toggle back to count
end

always @(posedge clk) begin
		if (res) 
		counter <= 20'b0000_0000_0000_0000_0000;
			else if (stp)
			counter <= counter;
		else begin
			if (tens == 4'b1001) begin
				tens <= 4'b0000;
				seconds <= seconds + 5'b0_0001;
			end
				else if (hundreds == 7'b110_0011) begin
					hundreds <= 7'b000_0000;
					tens <= tens + 4'b0001;
				end
					else begin
						if (counter >= 20'b1111_0100_0010_0011_1111) begin 	//0 to 999,999 for .01sec counter
						counter <= 20'b0000_0000_0000_0000_0000;
						hundreds <= hundreds + 7'b000_0001;
						end
							else
							counter <= counter + 20'b0000_0000_0000_0000_0001;
					end
		end
end
			
endmodule
EDIT: Sorry, reposted code several times. This one is final.
ElEctric_EyE
Posts: 3260
Joined: 02 Mar 2009
Location: OH, USA

Re: Concept & Design of 3.3V Parallel 16-bit VGA Boards

Post by ElEctric_EyE »

Hmmm, I thought I would do a character plot routine in order to plot the result of the Timer before doing the circle routine. So I finished already and came to the realization that video memory would be best placed at the very bottom $0000_0000-$0fff_ffff. Then the 64K zero-page and 64K stack sit above this. All is working. Now I must do a 'plot number' routine and adapt it to the output of the Timer module.
ElEctric_EyE
Posts: 3260
Joined: 02 Mar 2009
Location: OH, USA

Re: Concept & Design of 3.3V Parallel 16-bit VGA Boards

Post by ElEctric_EyE »

woops, I realized I don't need a tenths counter. Just 2 8 bit counters that max at 99, 1 for seconds and 1 for 10ths and 100ths :oops:
I'll try to adapt a bit of Verilog code I have in a book that converts 8-bit binary to BCD.
User avatar
Arlet
Posts: 2353
Joined: 16 Nov 2010
Location: Gouda, The Netherlands
Contact:

Re: Concept & Design of 3.3V Parallel 16-bit VGA Boards

Post by Arlet »

Instead of 7'b110_0011 I would prefer to write 7'd99.

And if you want BCD, it's easier to make a BCD counter than it is to make a binary counter + binary to BCD converter.
ElEctric_EyE
Posts: 3260
Joined: 02 Mar 2009
Location: OH, USA

Re: Concept & Design of 3.3V Parallel 16-bit VGA Boards

Post by ElEctric_EyE »

Of course! Thank you!
I need to refresh my memory with some old school TTL datasheets. Vague memories are there with a great many cobwebs from when I first experimented with counters and 7-segment displays on a breadboard.

EDIT: I was on the right track, just trying to be to fancy for my own good. Hopefully I can get this timer section (Verilog & software) done today.
ElEctric_EyE
Posts: 3260
Joined: 02 Mar 2009
Location: OH, USA

Re: Concept & Design of 3.3V Parallel 16-bit VGA Boards

Post by ElEctric_EyE »

ARG, I HATE GHOSTS IN THE MACHINE!
I would've been done 5 min's after the last post if it wasn't for the ghost. Old variables (with same names) in ISim were not changing, and I was chasing my tail. Now I'm going on to write this easy HEX plotting software!

Code: Select all

module Timer(input clk,
				 input [15:0] cpuDO,				//cpu databus out
				 input cpuWE,						//cpu write, active high
				 input cntrCS,						//active high from $c000_0000-$cfff_ffff
				 output reg [15:0] countDO		//from cpuDataOut
				 );

reg [16:0] counter = 0;		//main .001sec counter from 100MHz clock

reg [3:0] seconds = 0;		
reg [3:0] tenths = 0;
reg [3:0] hundredths = 0;		
reg [3:0] thousandths = 0;

wire [15:0] count;

reg stp = 0; 					//control bit for stop(hold)
reg res = 0;					//control bit to reset main .001sec counter

assign count [15:0] = {seconds, tenths, hundredths, thousandths};  //assign the bit values when reading the counter

always @(posedge clk) begin
	if(cntrCS & cpuWE) begin
			res <= cpuDO [15];				//a write to $c000_0000, bit 15 = reset
			stp <= cpuDO [14];				//a write to $c000_0000, bit 14 = stop
		end
	if(cntrCS & !cpuWE) 
		countDO [15:0] <= count [15:0];	//read from $c000_0000 16-bit BCD count 
			else 
			countDO [15:0] <= 16'h0000;
	if (res)
		res <= ~res;							//auto toggle back to count
end

always @(posedge clk) begin
		if (res) 
			counter <= 17'd0;
			else if 
				(stp) counter <= counter;
				else begin
					 if (tenths == 4'd9) begin
							tenths <= 4'd0;
							seconds <= seconds + 4'd1;
					 end
							else if (hundredths == 4'd9) begin		
								hundredths <= 4'd0;
								tenths <= tenths + 4'd1;
							end
								else if (thousandths == 4'd9) begin
									thousandths <= 4'd0;
									hundredths <= hundredths + 4'd1;
								end
									else begin 
										if (counter == 17'd99999) begin 	//0 to 99,999 for .001sec counter
											counter <= 17'd0;
											thousandths <= thousandths + 4'd1;
										end
											else 
												counter <= counter + 17'd1;
									end
													
				end
end
			
endmodule
Looks like I'm seeming to favor the 'IF's.
@ Arlet, thanks for the tip of decimal. I think I've learned the bit value is absolute key for proper bit placement. I didn't realize this before, hence my avoiding it and using strictly binary.
@anyone. What's the alternative to all the 'IF's?
Post Reply