******************************** * * * Amper-fdraw * * By Andy McFadden * * For fdraw version 0.3 * * * * Applesoft ampersand * * interface for fdraw. * * * * Developed with Merlin-16 * * * ******************************** lst off org $1d60 * All of the handler entry points can fit on a single * page, so it's possible to save a few bytes by * dropping the high jump table and just hardcoding * the first page into the jump. This requires that * the ORG be at $xx00. PUT FDRAW.DEFS * Applesoft BASIC tokens. tok_plot equ $8d tok_hgr2 equ $90 tok_hgr equ $91 tok_hcolor equ $92 tok_hplot equ $93 tok_draw equ $94 tok_xdraw equ $95 tok_inverse equ $9e tok_clear equ $bd tok_new equ $bf tok_to equ $c1 tok_at equ $c5 *tok_sgn equ $d2 tok_scrn equ $d7 tok_exp equ $dd tok_cos equ $de tok_sin equ $df * System locations. PCL equ $3a ;used by monitor PCH equ $3b ;used by monitor A1L equ $3c ;used by monitor A1H equ $3d ;used by monitor LINNUM equ $50 ;50-51 FACLO equ $a1 CHRGET equ $b1 ;advance ptr, get next tok CHRGOT equ $b7 ;get next tok (no advance) TXTPTR equ $b8 HPAG equ $e6 ;$20 or $40 AMPERV equ $3f5 TXTCLR equ $c050 TXTSET equ $c051 MIXCLR equ $c052 MIXSET equ $c053 LOWSCR equ $c054 HISCR equ $c055 LORES equ $c056 HIRES equ $c057 ERROR equ $d412 ;error based on X reg FRMNUM equ $dd67 SynError equ $dec9 ;throw SYNTAX ERROR CHKCOM equ $debe IllQError equ $e199 ;throw ILLEGAL QUANTITY ERROR GETADR equ $e752 GETBYT equ $e6f8 ;gets byte, in X/FACLO HFNS equ $f6b9 ;get hi-res x/y for hplot * Prepare the ampersand vector. * * Ideally we'd check to see if the existing vector is * different from ours, and if so, jump to it when we * get a token we don't recognize. Not convinced * there's an actual use case for this. init lda #$4c ;JMP, in case it got sta AMPERV ; trashed lda #dispatch sta AMPERV+2 rts * Entry point from BASIC. The token is in A. dispatch ldx #:cmdend-:cmdtab-1 ]loop cmp :cmdtab,x beq :match dex bpl ]loop jmp SynError :match lda :jmptabh,x * lda #>h_new ;all on first page pha lda :jmptabl,x pha jmp CHRGET ;eat token, jump :cmdtab dfb tok_new dfb tok_hgr dfb tok_hgr2 dfb tok_scrn dfb tok_hcolor dfb tok_inverse dfb tok_clear dfb tok_hplot dfb tok_xdraw dfb tok_draw dfb tok_exp dfb tok_cos dfb tok_sin dfb tok_at dfb tok_plot :cmdend :jmptabl dfb h_new-1 dfb >h_hgr-1 dfb >h_hgr2-1 dfb >h_scrn-1 dfb >h_hcolor-1 dfb >h_inverse-1 dfb >h_clear-1 dfb >h_hplot-1 dfb >h_xdraw-1 dfb >h_draw-1 dfb >h_exp-1 dfb >h_cos-1 dfb >h_sin-1 dfb >h_at-1 dfb >h_plot-1 ******************************** * &NEW - initialize h_new lda #$20 ;match Init result sta g_cur_page lda #$00 sta g_hcolor tax ;init "previous hplot" tay ; coord to zero jsr storeprv ldx #139 ;279/2 ldy #0 lda #95 ;191/2 jsr storeac jmp f_Init ******************************** * &HGR - show page 1 with mixed text, and clear screen. * Sets the color to zero. h_hgr ldx #$20 ;page 1 lda #$00 ;$c054 beq hgr_com ******************************** * &HGR2 - show page 2 with no text, and clear screen. * Sets the color to zero. h_hgr2 ldx #$40 ;page 2 lda #$01 ;$c055 ;fall through to hgr_com * We go slightly out of our way to clear the screen * before tripping the softswitches. This avoids * flashing the previous hi-res page contents when * entering from text mode. * * We also want to go nomix-page2 but page1-mix * (note reverse order) to avoid flashing text pg 2. hgr_com stx f_in_arg stx g_cur_page stx HPAG ;probably useful pha jsr f_SetPage lda #$00 sta f_in_arg jsr f_SetColor jsr f_Clear lda g_hcolor ;restore color sta f_in_arg jsr f_SetColor bit TXTCLR ;$c050 bit HIRES ;$c057 pla beq :pg1 bit MIXCLR ;$c052 bit HISCR ;$c055 rts :pg1 bit LOWSCR ;$c054 bit MIXSET ;$c053 rts ******************************** * &SCRN({1,2}) - set the current hi-res page h_scrn jsr GETBYT cpx #1 beq :okay cpx #2 beq :okay jmp IllQError :okay jsr CHRGET ;eat ')' (we assume) txa ;X/Y unaltered asl asl asl asl asl ;multiply x32 sta g_cur_page sta f_in_arg jmp f_SetPage ******************************** * &HCOLOR={0-7} - set the current color h_hcolor jsr GETBYT ;get color cpx #8 blt :okay jmp IllQError :okay stx f_in_arg stx g_hcolor jmp f_SetColor ******************************** * &INVERSE - flip pages * * If we're currently drawing on $20, we set the page * to $40 and hit $c054 to show $20. And vice-versa. * The goal is to make double-buffered animation easy. h_inverse lda g_cur_page eor #$60 sta g_cur_page ldx #$00 cmp #$40 ;about to start drawing on 2? beq :showpg1 ;yes, show page 1 inx ;no, show page 2 :showpg1 ldy LOWSCR,x sta f_in_arg jmp f_SetPage ******************************** * &CLEAR - clear current page to current color. h_clear jmp f_Clear ;well, that was easy ******************************** * &XDRAW left,top,right,bottom - draw rectangle outline h_xdraw jsr getltrb jmp f_DrawRect ******************************** * &DRAW left,top,right,bottom - draw filled rectangle h_draw jsr getltrb jmp f_FillRect ******************************** * &EXP {0,1} - set line draw mode h_exp jsr GETBYT cpx #2 blt :okay jmp IllQError :okay stx f_in_arg jmp f_SetLineMode ******************************** * &COS cx,cy,rad - draw outline circle h_cos jsr getcxcyr jmp f_DrawCircle ******************************** * &SIN cx,cy,rad - draw filled circle h_sin jsr getcxcyr jmp f_FillCircle ******************************** * &AT x,y - select center for array draw h_at jsr HFNS jmp storeac ******************************** * &PLOT vertexAddr, indexAddr, indexCount [AT cx,cy] * draw lines from arrays of vertices and indices h_plot jmp array_draw ******************************** * &HPLOT x,y - draw a point * &HPLOT TO x,y - draw a line from last point to x,y * &HPLOT x0,y0 to x1,y1 - draw a line lst on ;last token handler -- h_hplot equ * ; must be on first page lst off ; to omit high byte table jsr CHRGOT ;check next token lst off cmp #tok_to ;is this an "HPLOT TO"? beq :leadingto jsr getx1y1 ;get the first coord jsr copy1to0 jsr CHRGOT ;see if single point cmp #tok_to beq :hplot_to ;nope, draw line jsr copy0toprev ;draw point, and save x/y jmp f_DrawPoint ; for subsequent HPLOT TO :leadingto ;"HPLOT TO", restore the lda g_prevxl ; previous coord to x0/y0 sta f_in_x0l ;(can't rely on f_in_zzz lda g_prevxh ; being there -- we might sta f_in_x0h ; have drawn a rect) lda g_prevy sta f_in_y0 :hplot_to jsr CHRGET ;eat the TO jsr getx1y1 ;get the coords jsr f_DrawLine ;draw it jsr copy1to0 ;shift 1->0 for next round jsr CHRGOT cmp #tok_to ;another TO? beq :hplot_to ;yes, branch jmp copy0toprev ;no, save prev and bail * Get coordinates and store in X1/Y1. getx1y1 jsr HFNS store1 stx f_in_x1l ;store X/Y/A in coord1 sty f_in_x1h sta f_in_y1 rts * Save x0/y0 as our "previous" coordinate. copy0toprev ldx f_in_x0l ldy f_in_x0h lda f_in_y0 storeprv stx g_prevxl ;store X/Y/A in g_prev sty g_prevxh sta g_prevy rts * Copy X1/Y1 into X0/Y0. copy1to0 ldx f_in_x1l ldy f_in_x1h lda f_in_y1 store0 stx f_in_x0l ;store X/Y/A in coord 0 sty f_in_x0h sta f_in_y0 rts * Store X/Y/A into array-center. storeac stx g_ac_xl sty g_ac_xh sta g_ac_y rts * Get left/top/right/bottom coordinates. getltrb jsr HFNS jsr store0 ;save as X0/Y0 jsr CHKCOM ;eat a comma jsr HFNS jsr store1 ;save as X1/Y1 rts * Get center coordinates and radius. getcxcyr jsr HFNS ;get CX and CY jsr store0 ;save as X0/Y0 jsr CHKCOM ;eat a comma jsr GETBYT ;convert to 0-255 stx f_in_rad rts * Array-draw handler. * * We know that fdraw doesn't use LINNUM or A1L/A1H, * so it's safe to use them here. array_draw ]vertices equ A1L ;2b ]indices equ LINNUM ;2b ]count equ PCL ]cur equ PCH jsr FRMNUM ;get vertex buffer address jsr GETADR lda LINNUM ;copy to A1L sta ]vertices lda LINNUM+1 sta ]vertices+1 jsr CHKCOM ;eat the comma jsr FRMNUM ;get index buffer address jsr GETADR ;leave it in LINNUM jsr CHKCOM jsr GETBYT ;get the count cpx #128 ;range check (0-127) blt :countok jmp IllQError :countok txa beq :done ;nothing to do asl ;double it sta ]count ;stash it lda #$00 sta ]cur * Check for optional AT cx,cy. jsr CHRGOT cmp #tok_at bne :noat JSR CHRGET ;eat the AT lda LINNUM ;the code that reads the pha ; hi-res coordinates will lda LINNUM+1 ; overwrite LINNUM, so pha ; we have to save & restore jsr h_at pla sta LINNUM+1 pla sta LINNUM :noat ]loop jsr getvertex bcs :skip2 jsr store0 jsr getvertex bcs :skip jsr store1 jsr f_DrawLine dfb $2c ;BIT addr :skip2 inc ]cur :skip lda ]cur cmp ]count blt ]loop :done rts * Get the Nth vertex, specified by ]cur, and load it * into X/Y/A (xlo/xhi/y). Returns with carry set if * the vertex is invalid. * * Increments ]cur by 1. getvertex ldy ]cur inc ]cur lda (]indices),y bmi :badv ;must be 0-127 jsr :calcvertex ldx g_out_x ldy g_out_x+1 beq :xok ;0-255, ok cpy #1 bne :badv ;512+ cpx #280-256 bge :badv ;280-511 :xok lda g_out_y+1 bne :badv ;Y is neg or > 255 lda g_out_y cmp #192 bcc :goodv :badv sec :goodv rts * Get VX and VY, merging with AC, and store in * 16-bit g_out_x and g_out_y. Range not checked * here. On entry, A has vertex index. :calcvertex asl tay ldx #$00 ;hi byte of vertex lda (]vertices),y ;x-coord bpl :xpos dex ;sign-extend hi byte :xpos clc adc g_ac_xl sta g_out_x txa adc g_ac_xh sta g_out_x+1 iny ldx #$00 lda (]vertices),y ;y-coord bpl :ypos dex ;sign-extend hi byte :ypos clc adc g_ac_y sta g_out_y bcc :nocarry inx :nocarry stx g_out_y+1 rts ******************************** * Global variables g_cur_page ds 1 ;$20 or $40 g_hcolor ds 1 g_prevxl ds 1 g_prevxh ds 1 g_prevy ds 1 g_ac_xl ds 1 ;Center-point coordinates g_ac_xh ds 1 ; for array-based line g_ac_y ds 1 ; draw (&AT, &PLOT). g_out_x ds 2 ;16-bit coordinates for g_out_y ds 2 ; array-based line draw lst on end equ * sav amperfdraw lst off