4x4 mode and circle routine
By Malcontent.
This is a rather simple and fast way of painting blocky pixels. To use it you need to modify the character set so that each 4×4 tile has a nibble that matches its pattern.
For example:
%xxxx0001 = 00 xxxx0110 = 01 01 10
A single point than can be bitshifted into position based on whether division of the co-ordinate leaves a carry. The point is then masked into the screen memory location with ORA. Starting with %00001000, LSR once to move the point to the right, and twice to move it down.
Tables speed up the conversion of x y co-ordinates to screen memory locations.
Much like the kernal plot, the registers are reversed. On entry have the x point in the y register and put the y point in the x register. There is no check for out of screen points. That shouldn't be hard to add.
On top of that I've added an implementation of Steve Judd's circle algorithm from C=Hacking #9. The plot routine alone uses few zeropage locations, so it could exit cleanly to BASIC, but the circle routine does not exit cleanly. It uses signed 16-bit integers for the center and the radius. It tries to be crafty in the way it decides what parts of the circle to draw. For smaller circles that don't need to be clipped, the algorithm can be further simplified/sped up. For BASIC compatibility, the variables need to be moved from the zeropage. Any optimizations to my weird clipping routine are welcome.
Download the binary, source, and char data: fourfour.zip
!to "fourfour.prg",cbm zp1 = $fb zp2 = $fc zx = $fd zy = $fe pnt = $fa ;Circle variables are signed 16-bit values r = $e0 ;Radius xc = $e2 ;X circle center yc = $e4 ;Y " " xp = $e6 ;X plot on circle edge yp = $e8 ;Y " " xd = $ea ;Transformed point to draw yd = $ec ; tx = $ee tmp = $f0 ;Single byte screendia = 93 ;If the center of the ;circle is on the screen, ;and the radius is larger ;than the screen diagonal, ;then the circle does not ;need to be drawn. blank = 96 ;Offset to chars ;Also blank char *=$2800 chars !bin "junkchars.b" *=$c000 ;sys49152 INIT lda $d018 ;Point vic to chars eor #$e sta $d018 jsr CLEARSCREEN lda #20 sta xc sta yc lda #0 sta xc+1 sta r+1 sta yc+1 lda #5 sta r - inc $d020 jsr CIRCLE dec $d020 inc xc inc r bne - rts ;This is the main plot routine, a straight ;shot to here will plot a 4x4 pixel to the ;co-ordinates held in the x and y registers PLOT lda #%00001000 sta pnt tya lsr bcc + lsr pnt ;Point shifts right + tay ;if division has a txa ;remainder lsr bcc + lsr pnt ;Point moves down lsr pnt ;on remainder + tax ;x and y registers now hold 0-24,0-39 ;so long as valid numbers went in. ;'pnt' contains the mask for the char. lda lotable,x ;Table holds the sta zp1 ;leftmost screen lda hitable,x ;address of row. sta zp2 lda (zp1),y ;Get screen graphic ora pnt ;mask in point. sta (zp1),y rts CLEARSCREEN ldx #0 - lda #blank sta $0400,x sta $0400+$ff,x sta $0400+$ff+$ff,x sta $0400+$ff+$ff+$ff,x lda #10 sta $d800,x sta $d800+$ff,x sta $d800+$ff+$ff,x sta $d800+$ff+$ff+$ff,x inx bne - rts lotable !byte $00,$28,$50,$78,$a0,$c8,$f0 !byte $18,$40,$68,$90,$b8,$e0 !byte $08,$30,$58,$80,$a8,$d0,$f8 !byte $20,$48,$70,$98,$c0 hitable !byte $04,$04,$04,$04,$04,$04,$04 !byte $05,$05,$05,$05,$05,$05 !byte $06,$06,$06,$06,$06,$06,$06 !byte $07,$07,$07,$07,$07 ;----------------------------------- !macro PLOTCIRCLE { ;Plot a circle point lda xd+1 ;Is xy in screen? bne *+21 ;If no, branch to lda yd+1 ;instruction after bne *+17 ;the macro. ldx yd cpx #50 bcs *+11 ldy xd cpy #80 bcs *+5 jsr PLOT ;Point is plotable } ;Reject drawing the circle if the bounding ;box does not cross the screen. ;if x<0 and if x+r>0 then check y ;if x>0 and if x-r<screenwidth check y nocir rts CIRCLE lda xc+1 bmi negxcen lda xc sec sbc r sta xd lda xc+1 sbc r+1 bmi checky bne nocir lda xd cmp #80 bcc checky rts negxcen lda xc clc adc r sta xd lda xc+1 adc r+1 bmi nocir checky lda yc+1 bmi negycen lda yc sec sbc r sta yd lda yc+1 sbc r+1 bmi bbok bne nocir lda yd cmp #50 bcc bbok rts negycen lda yc clc adc r sta yd lda yc+1 adc r+1 bmi nocir bbok lda r ;Init radius and sta xp ;set first point sta tx ;to draw. lda r+1 sta xp+1 lsr sta tx+1 ;tx=r/2 ror tx lda #0 sta yp sta yp+1 ;jmp cloop ;*************** lda #$18 ;clc - sta radovfl ;Clear draw skips sta top sta topleft sta topright sta bottom sta bottomleft sta bottomright ;Some comparisons here modify the looping ;code that draws. Saves us from having to ;do the comparisons within the loop. ldx #$38 ;sec ldy #$02 ;decremented flag lda xc+1 bmi drawr ;x<0 bne drawl ;x>$ff lda xc cmp #80 ;Screen width bcs drawl ;x>80 dey ;x in screen bne yarc drawr stx topleft stx bottomleft ;skip these jmp yarc drawl stx topright stx topright yarc lda yc+1 bmi drawb ;y<0 bne drawt ;y>$ff lda yc cmp #50 ;Screen height bcs drawl ;y>50 dey ;y in screen bne cloop lda r+1 ;Center in screen. bne toobig ;Compare radius to lda r ;screen diagonal. cmp #screendia bcc cloop toobig rts drawb stx top ;skip this jmp cloop drawt stx bottom ;All set up. Now the heart of the circle ;calculations. From C=Hacking. ;70 IF X<=Y THEN 100 ;80 Y=Y+1:TX=TX-Y ;90 IF TX<0 THEN X=X-1:TX=TX+X cloop jsr plot8 inc yp bne + inc yp+1 + sec lda tx sbc yp sta tx lda tx+1 sbc yp+1 sta tx+1 bcs cloop dec xp bne + dec xp+1 + lda tx adc xp sta tx lda tx+1 adc xp+1 sta tx+1 sec lda xp sbc yp sta tmp lda xp+1 sbc yp+1 ora tmp bcs cloop jsr plot8 ;Get last bit rts ;plot8 is modified by the circle init ;routine to avoid drawing unnecessary ;parts of the circle. It pokes SECs over ;CLCs to branch or fall through to the ;right transformations ; ;30 DRAW1,X+XO,Y+YO:DRAW1,Y+XO,X+YO ;40 DRAW1,XO-X,YO+Y:DRAW1,XO-Y,YO+X ;50 DRAW1,XO-X,YO-Y:DRAW1,XO-Y,YO-X ;60 DRAW1,XO+X,YO-Y:DRAW1,XO+Y,YO-X noplot1 rts plot8 radovfl clc ;Radius overflow bcs noplot1 top clc ;clc draw top bcc topleft jmp bottom topleft clc bcs topright sec lda xc sbc xp sta xd lda xc+1 sbc xp+1 sta xd+1 sec lda yc sbc yp sta yd lda yc+1 sbc yp+1 sta yd+1 +PLOTCIRCLE sec lda xc sbc yp sta xd lda xc+1 sbc yp+1 sta xd+1 sec lda yc sbc xp sta yd lda yc+1 sbc xp+1 sta yd+1 +PLOTCIRCLE topright clc bcs bottom lda xc adc xp sta xd lda xc+1 adc xp+1 sta xd+1 sec lda yc sbc yp sta yd lda yc+1 sbc yp+1 sta yd+1 +PLOTCIRCLE clc lda xc adc yp sta xd lda xc+1 adc yp+1 sta xd+1 sec lda yc sbc xp sta yd lda yc+1 sbc xp+1 sta yd+1 +PLOTCIRCLE bottom clc ;bottom half bcc bottomleft rts bottomleft clc bcs bottomright sec lda xc sbc xp sta xd lda xc+1 sbc xp+1 sta xd+1 clc lda yc adc yp sta yd lda yc+1 adc yp+1 sta yd+1 +PLOTCIRCLE sec lda xc sbc yp sta xd lda xc+1 sbc yp+1 sta xd+1 clc lda yc adc xp sta yd lda yc+1 adc xp+1 sta yd+1 +PLOTCIRCLE bottomright clc bcs noplot lda xp adc xc sta xd lda xp+1 adc xc+1 sta xd+1 clc lda yp adc yc sta yd lda yp+1 adc yc+1 sta yd+1 +PLOTCIRCLE clc lda yp adc xc sta xd lda yp+1 adc xc+1 sta xd+1 clc lda xp adc yc sta yd lda xp+1 adc yc+1 sta yd+1 +PLOTCIRCLE noplot rts