BLARG -- Basic Language Graphics extension ----- version 1.0 2/10/97 BLARG is a little BASIC extension which adds some graphics commands to the normal C-64 BASIC. In addition it supports the SuperCPU optimization modes, as well as double buffering. Finally, it is free for use in your own programs, so feel free to do so! My goal was to write a BASIC extension which was compact, fast, and actually available for downloading :). Also, something that wasn't BASIC7.0. It doesn't have tons of features but I deem it to be Nifty. Anyways, these are adaptations of my algorithms from C=Hacking and such. They are not the most efficient implementations, but they are fairly zippy, and fairly well beat the snot out of BASIC7.0 commands! For instance, the times from moire3 (a line drawing test): Stock 64 1200 jiffies (1X) SCPU Mode 17 137 jiffies (9.1X) SCPU Mode 16 59 jiffies (20.2X) BASIC7.0 (1MHz) 4559 jiffies (1/3.5 X) So lines are nearly 4x faster on a stock 64 than a 128 running BASIC7.0. Running in mode16 on a SuperCPU is 77 times faster than BASIC7.0!!! Let's not even talk about BASIC7.0 circles. Well, what the heck, let's talk about them :) circletest1: Stock 64 360 jiffies (1x) SCPU Mode 17 50 jiffies (7x) SCPU Mode 16 22 jiffies (15.4x) BASIC7.0 17394 j :) 1/49x Bottom line: mode 16 circles are, if you can believe it, 790x faster than BASIC7.0 circles (and much better looking, especially at large radii -- BASIC7.0 circles are actually 128-sided polygons :-/ ). The total size of the program right now is a bit over 2k, and sits at $C000. To install the program, just load and run. To re-initialize the system (after a warm reset for instance) just type SYS49152. The command list is located near $C000, immediately followed by the routine addresses, in case you want to take a peek. A second program, BLARG$8000, is included in the archive. To use it, load ,8,1 and type SYS32768 to initialize. This program is included so that it may be loaded from a BASIC program. Several demo programs are included, and offer a good way of learning the commands (for instance, try typing ORIGIN 10,10 before running MOIRE3). Words to the wise: 1 - If you use a DOS extension like JiffyDOS then be sure to enclose filenames etc. in quotes (unless you want them to be tokenized, in which case feel free to omit quotes). 2 - If it looks like your machine has completely frozen try typing RUN-STOP, shift-clear, MODE 17 (Sometimes you can break the program before it can tell VIC where the screen is located). MODE16 and MODE17 will always fix stuff up, whereas run/stop-restore doesn't always do the trick. 3 - Always keep in mind that MODE 16,17, and 18 may hose string variables. 4 - IF/THEN bypasses the IGONE vector, so a statement like IF A=0 THEN GRON will fail. The statement IF A=0 THEN:GRON will work fine. Without further ado: GRON [COLOR] -- Turns graphics on. If the optional parameter COLOR is specified the bitmap is cleared and the colormap is initialized to that value, specifically, COLOR = 16*foreground color + background color Examples: GRON -- Turn on bitmap without clearing it. GRON20 -- Turn on bitmap, clear, purple bg, white fg GROFF -- Turns graphics off (sticks you back into text mode, or whatever mode you were in when you last called GRON) CLEAR [color] -- Clear current graphics buffer. CLEAR is part of the GRON routine, but will not set VIC or CIA#2. COLOR n -- COLOR 1 sets the drawing color to the foreground color; COLOR 0 sets it to background. ORIGIN CX, CY -- Sets the upper-left corner of the screen to have coordinates CX,CY. More precisely, commands will subtract CX,CY from coordinates passed into it. Among other things, this provides a mechanism for negative numbers to be handled -- LINE -10,0,40,99 will not work, but ORIGIN 10,0: LINE 0,0,50,99 will. This value is initialized to (0,0) whenever SYS49152 is called. PLOT X,Y -- Sticks a point at coordinates X,Y. (Actually at coordinates X-XC, Y-YC). X may be in the range 0..319 and Y may be 0..199; points outside this range will not be plotted. LINE X1,Y1,X2,Y2 -- Draws a line from X1,Y1 to X2,Y2 (subtracting XC,YC as necessary). X1 may be any 16-bit value and Y2 may be any 8-bit value. Coordinates off of the screen will simply not be plotted! CIRCLE XC,YC,R -- Draws a circle of radius R centered at XC,YC (translating as necessary). The algorithm is smart and can handle R=0..255 correctly (as far as I know!). I used a modified version of my algorithm, which makes very nice circles in my quite biased opinion, except for a few radii which come out a little ovalish. Oh well. MODE n -- New graphics MODE. MODE 16 -- SuperCPU mode. This moves the bitmap screen to $A000, the colormap to $8C00, the text screen to $8800, and sets the SuperCPU bank 2 optimization mode. $9000-$9FFF is totally unused :(. MODE 17 -- Normal mode. Bitmap->$E000, Colormap ->$CC00, text screen -> $0400. MODE 18 -- Double buffer mode. MODE16 memory configuration, no SCPU optimization. MODE16, MODE17, and MODE18 reset the BASIC memtop and stringtop pointers, so any defined strings may get hosed. (They also execute GROFF, and hence turn on the text screen). Any other MODE parameter will be set to the BITMASK parameter. What is BITMASK? Anything drawn to the screen is first ANDed with BITMASK. (Try MODE 85 sometime). (Although these numbers are reserved for future expansion). BUFFER n -- Set drawing buffer. When double-buffer mode is activated (MODE 18), both buffers are available for drawing and displaying. It is then possible to draw in one buffer while displaying the other. BUFFER n selects which buffer the PLOT,LINE,CIRCLE, and CLEAR commands will affect. If n=0 then it swaps the target buffer. Otherwise, n=odd references the buffer at $A000 and n=even selects the buffer at $E000. SWAP -- Swap displayed buffer. Assuming MODE18 is selected, SWAP simply selects which buffer is displayed on the screen; specifically, it flips between the two. SWAP only affects what is displayed on the screen; BUFFER only affects the target of the drawing commands. I think that's it :). ---------------------------------------------------------------------------- * * GRABAS * * A graphics extension for C-64 BASIC * * SLJ 12/29/96 (Completed 2/10/97) * v1.0 * ORG $0801 * Constants TXTPTR = $7A ;BASIC text pointer IERROR = $0300 ICRUNCH = $0304 ;Crunch ASCII into token IQPLOP = $0306 ;List IGONE = $0308 ;Execute next BASIC token CHRGET = $73 CHRGOT = $79 CHROUT = $FFD2 GETBYT = $B79E ;BASIC routine GETPAR = $B7EB ;Get a 16,8 pair of numbers CHKCOM = $AEFD NEW = $A642 CLR = $A65E LINNUM = $14 ;Number returned by GETPAR TEMP = $FF TEMP2 = $FB POINT = $FD Y1 = $05 X1 = LINNUM X2 = $02 Y2 = $04 DY = $26 DX = $27 BUF = $0200 ;Input buffer CHUNK1 = $69 ;Circle routine stuff OLDCH1 = $6A CHUNK2 = $6B OLDCH2 = $6C CX = $A3 CY = $A5 X = $6D Y = $6E RADIUS = $6F LCOL = $A6 ;Left column RCOL = $A7 TROW = $A8 ;Top row BROW = $A9 ;Bottom row DA :LINK ;link DA 1997 DFB $9E ;SYS TXT '2063:' DFB $A2 ;NEW DFB 00 ;End of line :LINK DA 0 ;end of program INSTALL LDA #PBEGIN STA POINT+1 LDA #PEND SBC #>PBEGIN STA TEMP2+1 LDA #$C0 ;Copy to $C000 STA X2+1 LDY #00 STY X2 :LOOP LDA (POINT),Y STA (X2),Y INY BNE :LOOP INC POINT+1 INC X2+1 DEC TEMP2+1 BNE :LOOP LDY TEMP2 :LOOP2 LDA (POINT),Y STA (X2),Y DEY CPY #$FF BNE :LOOP2 LDX #5 ;Copy CURRENT vectors :LOOP3 LDA ICRUNCH,X STA OLDCRNCH,X DEX BPL :LOOP3 JMP INIT TXT 'so, you want a secret message, eh? ' TXT 'narnia, narnia, narnia, awake.' TXT ' love. think. speak. be walking trees.' TXT ' be talking beasts. be divine waters.' TXT 'stephen l. judd wuz here 1/20/97' PBEGIN ORG $C000 * * Init routine -- modify vectors * and set up values. * INIT LDX #5 ;Copy vectors :LOOP LDA :TABLE,X STA ICRUNCH,X DEX BPL :LOOP INX STX ORGX STX ORGY *------------------------------- DO 0 JSR GRON ;A little init thingy LDX #20 JSR CLEARCOL ;Part of GRON LDA #160 STA CX LDA #100 STA CY LDA #00 STA CX+1 LDA #$FF ;Mode 1 STA BITMASK :ILOOP LDX :TEMP JSR CIRCENT LDA :TEMP CLC ADC #10 STA :TEMP CMP #135 BCC :ILOOP LDA #00 ;Mode 0 STA BITMASK :ILOOP2 LDX :TEMP JSR CIRCENT LDA :TEMP SEC SBC #10 STA :TEMP CMP #5 BCS :ILOOP2 FIN *------------------------------- JMP MODE17 ;Mode 17 :TEMP DFB 05 :TABLE DA CRUNCH DA LIST DA EXECUTE JMPCRUN DFB $4C ;JMP OLDCRNCH DS 2 ;Old CRUNCH vector OLDLIST DS 2 OLDEXEC DS 2 * * Keyword list * Keywords are stored as normal text, * followed by the token number. * All tokens are >128, * so they easily mark the end of the keyword * KEYWORDS TXT 'plot',E0 TXT 'line',E1 TXT 'circle',E2 TXT 'gr',91,E3 ;grON TXT 'groff',E4 ;Graphics off TXT 'mode',E5 DFB $B0 ;OR TXT 'igin',E6 TXT 'clear',E7 ;Clear bitmap TXT 'buffer',E8 ;Set draw buffer TXT 'swap',E9 ;Swap foreground and background TXT 'col',B0,EA ;Set color DFB 00 ;End of list * * Table of token locations-1 * Subtract $E0 first * Then check to make sure number isn't greater than NUMWORDS * TOKENLOC :E0 DA PLOT-1 :E1 DA LINE-1 :E2 DA CIRCLE-1 :E3 DA GRON-1 :E4 DA GROFF-1 :E5 DA MODE-1 :E6 DA ORIGIN-1 :E7 DA CLEAR-1 :E8 DA BUFFER-1 :E9 DA SWAP-1 :EA DA COLOR-1 HITOKEN EQU $EB * * CRUNCH -- If this is one of our keywords, then tokenize it * CRUNCH JSR JMPCRUN ;First crunch line normally LDY #05 ;Offset for KERNAL ;Y will contain line length+5 :LOOP STY TEMP JSR ISWORD ;Are we at a keyword? BCS :GOTCHA :NEXT JSR NEXTCHAR BNE :LOOP ;Null byte marks end STA BUF-3,Y ;00 line number LDA #$FF ;'tis what A should be RTS ;Buh-bye * Insert token and crunch line :GOTCHA LDX TEMP ;If so, A contains opcode STA BUF-5,X :MOVE INX LDA BUF-5,Y STA BUF-5,X ;Move text backwards BEQ :NEXT INY BPL :MOVE * * ISWORD -- Checks to see if word is * in table. If a word is found, then * C is set, Y is one past the last char * and A contains opcode. Otherwise, * carry is clear. * * On entry, TEMP must contain current * character position. * ISWORD LDX #00 :LOOP LDY TEMP :LOOP2 LDA KEYWORDS,X BEQ :NOTMINE CMP #$E0 BCS :RTS ;Tokens are >=$E0 CMP BUF-5,Y BNE :NEXT INY ;Success! Go to next char INX BNE :LOOP2 :NEXT INX LDA KEYWORDS,X ;Find next keyword CMP #$E0 BCC :NEXT INX BNE :LOOP ;And check again :NOTMINE CLC :RTS RTS * * NEXTCHAR finds the next char * in the buffer, skipping * spaces and quotes. On * entry, TEMP contains the * position of the last spot * read. On exit, Y contains * the index to the next char, * A contains that char, and Z is set if at end of line. * NEXTCHAR LDY TEMP :LOOP INY LDA BUF-5,Y BEQ :DONE CMP #$8F ;REM BNE :CONT LDA #00 :SKIP STA TEMP2 ;Find matching character :LOOP2 INY LDA BUF-5,Y BEQ :DONE CMP TEMP2 BNE :LOOP2 ;Skip to end of line BEQ :LOOP :CONT CMP #$20 ;Space BEQ :LOOP CMP #$22 ;Quote BEQ :SKIP :DONE RTS * * LIST -- patches the LIST routine * to list my tokens correctly. * LIST CMP #$E0 BCC :NOTMINE ;Not my token CMP #HITOKEN BCS :NOTMINE BIT $0F ;Check for quote mode BMI :NOTMINE SEC SBC #$DF ;Find the corresponding text TAX STY $49 LDY #00 :LOOP DEX BEQ :DONE :LOOP2 INY LDA KEYWORDS,Y CMP #$E0 BCC :LOOP2 INY BNE :LOOP :DONE LDA KEYWORDS,Y BMI :OUT JSR $FFD2 INY BNE :DONE :OUT CMP #$B0 ;OR BEQ :OR CMP #$E0 ;It might be BASIC token BCS :CONT ;e.g. GRON LDY $49 :NOTMINE AND #$FF JMP (OLDLIST) ;QPLOP :CONT LDY $49 JMP $A700 ;Normal exit :OR LDA #'o' ;For ORIGIN JSR CHROUT LDA #'r' JSR CHROUT INY BNE :DONE * * EXECUTE -- if this is one of my * tokens, then execute it. * EXECUTE JSR CHRGET PHP CMP #$E0 BCC :NOTMINE CMP #HITOKEN BCS :NOTMINE PLP JSR :DISP JMP $A7AE ;Exit through NEWSTT :DISP EOR #$E0 ASL ;Mult by two TAX LDA TOKENLOC+1,X PHA LDA TOKENLOC,X PHA JMP CHRGET ;Exit to routine :NOTMINE PLP JMP $A7E7 ;Normal routine * * PLOT -- plot a point! * ORGX DFB 00 ;Upper-left corner of the screen ORGY DFB 00 DONTPLOT DFB 01 ;0=Don't plot point, just compute ;coordinates (used by e.g. circles) PLOT JSR GETPAR ;Get coordinate pair LDA LINNUM ;Add in origin offset SEC SBC ORGX STA LINNUM BCS :CONT1 DEC LINNUM+1 BMI :ERROR ;Underflow SEC :CONT1 TXA SBC ORGY BCC :ERROR TAX CPX #200 ;Check range BCS :ERROR LDA LINNUM CMP #<320 LDA LINNUM+1 SBC #>320 BCC SETPOINT :ERROR RTS ;Just don't plot point *:ERROR LDX #14 * JMP (IERROR) SETPOINT ;Alternative entry point ;X=y-coord, LINNUM=x-coord * ;X is preserved * STX TEMP2 * STY TEMP2+1 ;On exit, X,Y are AND #$07 ;i.e. are set up correctly. TXA AND #248 STA POINT LSR LSR LSR ADC BASE ;Base of bitmap STA POINT+1 LDA #00 ASL POINT ROL ASL POINT ROL ASL POINT ROL ADC LINNUM+1 ADC POINT+1 STA POINT+1 TXA AND #7 TAY LDA LINNUM AND #248 CLC ;Overflow is possible! ADC POINT STA POINT BCC SETPIXEL INC POINT+1 SETPIXEL LDA LINNUM AND #$07 TAX LDA DONTPLOT BEQ :RTS LDA POINT+1 SEC SBC BASE ;Overflow check CMP #$20 BCS :RTS SEI ;Get underneath ROM LDA #$34 STA $01 LDA (POINT),Y EOR BITMASK AND BITTAB,X EOR (POINT),Y STA (POINT),Y LDA #$37 STA $01 CLI * LDX TEMP2 * LDY TEMP2+1 ;On exit, X,Y are AND #$07 ;i.e. are set up correctly. ;for more plotting :RTS RTS BITMASK DFB #$FF ;Set point BITTAB DFB $80,$40,$20,$10,$08,$04,$02,$01 *------------------------------- * Drawin' a line. A fahn lahn. * * To deal with off-screen coordinates, the current row * and column (40x25) is kept track of. These are set * negative when the point is off the screen, and made * positive when the point is within the visible screen. * Little bit position table BITCHUNK HEX FF7F3F1F0F070301 CHUNK EQU X2 OLDCHUNK EQU X2+1 * DOTTED -- Set to $01 if doing dotted draws (diligently) * X1,X2 etc. are set up above (x2=LINNUM in particular) * Format is LINE x2,y2,x1,y1 LINE JSR GETPAR STX Y2 LDA LINNUM STA X2 LDA LINNUM+1 STA X2+1 JSR CHKCOM JSR GETPAR STX Y1 :CHECK LDA X2 ;Make sure x1=y1? EOR #$FF ;Otherwise dy=y1-y2 ADC #$01 LDX #$88 ;DEY :DYPOS STA DY STX YINCDEC STX XINCDEC LDA X1 ;Sub origin from 1st point SEC SBC ORGX STA X1 LDA X1+1 SBC #00 STA X1+1 PHP ;Save carry flag STA TEMP ;Next compute column LDA X1 LSR TEMP ROR LSR TEMP ROR LSR TEMP ROR STA CX ;X-column PLP BCC :NEGX ;If negative, then fix up CMP #40 ;If past column 40, then punt! BCC :CONT1 RTS :NEGX LDA X1 ;coordinate start and count AND #$07 STA X1 LDA #00 STA X1+1 LDA CX :CONT1 LDA Y1 ;Now do the same for Y SEC SBC ORGY STA Y1 TAX ;X=y-coord PHP ;Save carry bit LSR LSR LSR STA CY ;Y-column (well, OK, row then) PLP BCC :NEGY ;If negative, then fix stuff up! SBC #25 ;Check if we are past bottom of BCC :CONT2 ;screen ORA #$80 ;Otherwise, 128+rows past 24 STA CY ;(for plot range checking) TXA AND #$07 ORA #8*24 ;Start in last row TAX BMI :CONT2 :NEGY ORA #$E0 ;Set high bits of column STA CY TXA AND #$07 TAX ;Start in 1st row :CONT2 LDA #00 STA DONTPLOT JSR SETPOINT ;Set up X,Y and POINT INC DONTPLOT LDA BITCHUNK,X STA OLDCHUNK STA CHUNK SEI ;Get underneath ROM LDA #$34 STA $01 LDX DY CPX DX ;Who's bigger: dy or dx? BCC STEPINX ;If dx, then... LDA DX+1 BNE STEPINX * * Big steps in Y * * To simplify my life, just use PLOT to plot points. * * No more! * Added special plotting routine -- cool! * * X is now counter, Y is y-coordinate * * On entry, X=DY=number of loop iterations, and Y= * Y1 AND #$07 STEPINY LDA #00 STA OLDCHUNK ;So plotting routine will work right LDA CHUNK SEC LSR ;Strip the bit EOR CHUNK STA CHUNK TXA BNE :CONT ;If dy=0 it's just a point INX :CONT LSR ;Init counter to dy/2 * * Main loop * YLOOP STA TEMP * JSR LINEPLOT LDA CX ;Range check ORA CY BMI :SKIP LDA (POINT),Y ;Otherwise plot EOR BITMASK AND CHUNK EOR (POINT),Y STA (POINT),Y :SKIP YINCDEC INY ;Advance Y coordinate CPY #8 BCC :CONT ;No prob if Y=0..7 JSR FIXY :CONT LDA TEMP ;Restore A SEC SBC DX BCC YFIXX YCONT DEX ;X is counter BNE YLOOP YCONT2 LDA (POINT),Y ;Plot endpoint EOR BITMASK AND CHUNK EOR (POINT),Y STA (POINT),Y YDONE LDA #$37 STA $01 CLI RTS YFIXX ;x=x+1 ADC DY LSR CHUNK BNE YCONT ;If we pass a column boundary... ROR CHUNK ;then reset CHUNK to $80 STA TEMP2 LDA CX BMI :CONT ;Skip if column is negative CMP #39 ;End if move past end of screen BCS YDONE LDA POINT ;And add 8 to POINT ADC #8 STA POINT BCC :CONT INC POINT+1 :CONT INC CX ;Increment column LDA TEMP2 DEX BNE YLOOP BEQ YCONT2 * * Big steps in X direction * * On entry, X=DY=number of loop iterations, and Y= * Y1 AND #$07 COUNTHI DFB 00 ;Temporary counter ;only used once STEPINX LDX DX LDA DX+1 STA COUNTHI LSR ;Need bit for initialization STA Y1 ;High byte of counter TXA BNE :CONT ;Could be $100 DEC COUNTHI :CONT ROR * * Main loop * XLOOP LSR CHUNK BEQ XFIXC ;If we pass a column boundary... XCONT1 SBC DY BCC XFIXY ;Time to step in Y? XCONT2 DEX BNE XLOOP DEC COUNTHI ;High bits set? BPL XLOOP XDONE LSR CHUNK ;Advance to last point JSR LINEPLOT ;Plot the last chunk EXIT LDA #$37 STA $01 CLI RTS * * CHUNK has passed a column, so plot and increment pointer * and fix up CHUNK, OLDCHUNK. * XFIXC STA TEMP JSR LINEPLOT LDA #$FF STA CHUNK STA OLDCHUNK LDA CX BMI :CONT ;Skip if column is negative CMP #39 ;End if move past end of screen BCS EXIT LDA POINT ADC #8 STA POINT BCC :CONT INC POINT+1 :CONT INC CX LDA TEMP JMP XCONT1 * * Check to make sure there isn't a high bit, plot chunk, * and update Y-coordinate. * XFIXY DEC Y1 ;Maybe high bit set BPL XCONT2 ADC DX STA TEMP LDA DX+1 ADC #$FF ;Hi byte STA Y1 JSR LINEPLOT ;Plot chunk LDA CHUNK STA OLDCHUNK LDA TEMP XINCDEC INY ;Y-coord CPY #8 ;0..7 is ok BCC XCONT2 STA TEMP JSR FIXY LDA TEMP JMP XCONT2 * * Subroutine to plot chunks/points (to save a little * room, gray hair, etc.) * LINEPLOT ;Plot the line chunk LDA CX ORA CY BMI :SKIP LDA (POINT),Y ;Otherwise plot EOR BITMASK ORA CHUNK AND OLDCHUNK EOR CHUNK EOR (POINT),Y STA (POINT),Y :SKIP RTS * * Subroutine to fix up pointer when Y decreases through * zero or increases through 7. * FIXY CPY #255 ;Y=255 or Y=8 BEQ :DECPTR :INCPTR ;Add 320 to pointer LDY #0 ;Y increased through 7 LDA CY BMI :CONT1 ;If negative, then don't update CMP #24 BCS :TOAST ;If at bottom of screen then quit LDA POINT ADC #<320 STA POINT LDA POINT+1 ADC #>320 STA POINT+1 :CONT1 INC CY RTS :DECPTR ;Okay, subtract 320 then LDY #7 ;Y decreased through 0 LDA CY BEQ :TOAST BMI :CONT2 CMP #$7F ;It is possible we just decreased BNE :C1 ;through row 25 LDA #24 STA CY ;In which case, set correct row :C1 LDA POINT SEC SBC #<320 STA POINT LDA POINT+1 SBC #>320 STA POINT+1 :CONT2 DEC CY RTS :TOAST PLA ;Remove old return address PLA JMP EXIT ;Restore interrupts, etc. * * CIRCLE draws a circle of course, using my * super-sneaky algorithm. * CIRCLE cx,cy,radius (16,8,8) * CIRCLE JSR GETPAR STX CY ;CX,CY = center LDA X1 SEC SBC ORGX STA CX STA X1 LDA X1+1 SBC #00 STA CX+1 STA X1+1 PHP ;Save carry LSR ;Compute which column we start LDA CX ;in ROR LSR LSR PLP BCS :CONT ;Underflow means negative column TAX LDA X1 ;Set X to first column AND #$07 STA X1 LDA #00 STA X1+1 TXA ORA #$E0 ;so set high bits :CONT STA RCOL STA LCOL BMI :SKIP CMP #40 ;Check for benefit of SETPOINT BCC :SKIP LDA X1 ;Set X in last column AND #$07 ORA #64-8 ;312+X AND 7 STA X1 LDA #1 STA X1+1 :SKIP JSR CHKCOM JSR GETBYT CIRCENT ;Alternative entry point STX Y STX RADIUS TXA BNE :C ;Skip R=0 LDX CY JMP SETPOINT ;Plot it as a point. :C CLC ADC CY BCS :BLAH SEC SBC ORGY BCS :C4 ;cy+y200 then set pointer to BCC :C2 ;last row, but set TROW CLC ;correctly :C3 TAY AND #$07 ORA #$C0 ;Last row, set Y1 correctly TAX TYA :C2 ROR LSR LSR STA TROW ;Top row LDA #00 STA DONTPLOT ;Don't plot points JSR SETPOINT ;Plot XC,YC+Y STY Y2 ;Y AND 07 LDA BITCHUNK,X STA CHUNK1 ;Forwards chunk STA OLDCH1 LSR EOR #$FF STA CHUNK2 ;Backwards chunk STA OLDCH2 LDA POINT STA TEMP2 ;TEMP2 = forwards high pointer STA X2 ;X2 = backwards high pointer LDA POINT+1 STA TEMP2+1 STA X2+1 LDA CY ;Now compute upper points SEC SBC ORGY BCS :CSET SEC ;We are so very negative SBC Y CLC BCC :BNEG :CSET SBC Y ;Compute CY-Y-ORGY :BNEG PHP TAX LSR ;Compute row LSR LSR STA BROW PLP BCS :CONT ORA #$E0 ;Make row negative STA BROW TXA AND #07 ;Handle underflow special! TAX :CONT JSR SETPOINT ;Compute new coords STY Y1 LDA POINT STA X1 ;X1 will be the backwards LDA POINT+1 ;low-pointer STA X1+1 ;POINT will be forwards SEI ;Get underneath ROM LDA #$34 STA $01 LDA Y LSR ;A=r/2 LDX #00 STX X ;y=0 * Main loop :LOOP INC X ;x=x+1 LSR CHUNK1 ;Right chunk BNE :CONT1 JSR UPCHUNK1 ;Update if we move past a column :CONT1 ASL CHUNK2 BNE :CONT2 JSR UPCHUNK2 :CONT2 ;LDA TEMP SEC SBC X ;a=a-x BCS :LOOP ADC Y ;if a<0 then a=a+y; y=y-1 TAX JSR PCHUNK1 JSR PCHUNK2 LDA CHUNK1 STA OLDCH1 LDA CHUNK2 STA OLDCH2 TXA DEC Y ;(y=y-1) DEC Y2 ;Decrement y-offest for upper BPL :CONT3 ;points JSR DECYOFF :CONT3 LDY Y1 INY STY Y1 CPY #8 BCC :CONT4 JSR INCYOFF :CONT4 LDY X CPY Y ;if y<=x then punt BCC :LOOP ;Now draw the other half * * Draw the other half of the circle by exactly reversing * the above! * NEXTHALF LSR OLDCH1 ;Only plot a bit at a time ASL OLDCH2 LDA RADIUS ;A=-R/2-1 LSR EOR #$FF :LOOP TAX JSR PCHUNK1 ;Plot points JSR PCHUNK2 TXA DEC Y2 ;Y2=bottom BPL :CONT1 JSR DECYOFF :CONT1 INC Y1 LDY Y1 CPY #8 BCC :CONT2 JSR INCYOFF :CONT2 LDX Y BEQ :DONE CLC ADC Y ;a=a+y DEC Y ;y=y-1 BCC :LOOP INC X SBC X ;if a<0 then x=x+1; a=a+x LSR CHUNK1 BNE :CONT3 TAX JSR UPCH1 ;Upchunk, but no plot :CONT3 LSR OLDCH1 ;Only the bits... ASL CHUNK2 ;Fix chunks BNE :CONT4 TAX JSR UPCH2 :CONT4 ASL OLDCH2 BCS :LOOP :DONE CIRCEXIT ;Restore interrupts LDA #$37 STA $01 CLI LDA #1 ;Re-enable plotting STA DONTPLOT RTS * * Decrement upper pointers * DECYOFF TAY LDA #7 STA Y2 LDA TROW ;First check to see if Y is in BEQ EXIT2 CMP #25 ;range (rows 0-24) BCS :SKIP LDA X2 ;If we pass through zero, then SEC SBC #<320 ;subtract 320 STA X2 LDA X2+1 SBC #>320 STA X2+1 LDA TEMP2 SEC SBC #<320 STA TEMP2 LDA TEMP2+1 SBC #>320 STA TEMP2+1 :SKIP TYA DEC TROW RTS EXIT2 PLA ;Grab return address PLA JMP CIRCEXIT ;Restore interrupts, etc. * Increment lower pointers INCYOFF TAY LDA #00 STA Y1 LDA BROW BMI :ISKIP ;If <0 then don't update pointer. CMP #24 ;If we hit bottom of screen then BEQ EXIT2 ;just quit LDA X1 CLC ADC #<320 STA X1 LDA X1+1 ADC #>320 STA X1+1 LDA POINT CLC ADC #<320 STA POINT LDA POINT+1 ADC #>320 STA POINT+1 :ISKIP TYA INC BROW RTS * * UPCHUNK1 -- Update right-moving chunk pointers * Due to passing through a column * UPCHUNK1 TAX JSR PCHUNK1 UPCH1 LDA #$FF ;Alternative entry point STA CHUNK1 STA OLDCH1 LDA RCOL BMI :DONE ;Can start negative LDA TEMP2 CLC ADC #8 STA TEMP2 BCC :CONT INC TEMP2+1 CLC :CONT LDA POINT ADC #8 STA POINT BCC :DONE INC POINT+1 :DONE TXA INC RCOL RTS * * UPCHUNK2 -- Update left-moving chunk pointers * UPCHUNK2 TAX JSR PCHUNK2 UPCH2 LDA #$FF STA CHUNK2 STA OLDCH2 LDA LCOL CMP #40 BCS :DONE LDA X2 SEC SBC #8 STA X2 BCS :CONT DEC X2+1 SEC :CONT LDA X1 SBC #8 STA X1 BCS :DONE DEC X1+1 :DONE TXA DEC LCOL RTS * * Plot right-moving chunk pairs for circle routine * PCHUNK1 LDA RCOL ;Make sure we're in range CMP #40 BCS :SKIP2 LDA CHUNK1 ;Otherwise plot EOR OLDCH1 STA TEMP LDA BROW ;Check for underflow BMI :SKIP LDY Y1 LDA (POINT),Y EOR BITMASK AND TEMP EOR (POINT),Y STA (POINT),Y :SKIP LDA TROW ;If CY+Y >= 200... CMP #25 BCS :SKIP2 LDY Y2 LDA (TEMP2),Y EOR BITMASK AND TEMP EOR (TEMP2),Y STA (TEMP2),Y :SKIP2 RTS * * Plot left-moving chunk pairs for circle routine * PCHUNK2 LDA LCOL ;Range check in X CMP #40 BCS :SKIP2 LDA CHUNK2 ;Otherwise plot EOR OLDCH2 STA TEMP LDA BROW ;Check for underflow BMI :SKIP LDY Y1 LDA (X1),Y EOR BITMASK AND TEMP EOR (X1),Y STA (X1),Y :SKIP LDA TROW ;If CY+Y >= 200... CMP #25 BCS :SKIP2 LDY Y2 LDA (X2),Y EOR BITMASK AND TEMP EOR (X2),Y STA (X2),Y :SKIP2 RTS * * GRON -- turn graphics on. If a number appears * afterwards, then initialize the colormap to that * number and clear the bitmap. * BASE DFB $E0 ;Address of bitmap, hi byte BANK DFB 0 ;Bank 3=default OLDBANK DFB $FF ;VIC old bank OLDD018 DFB 00 GRON LDA $D011 ;Skip if bitmap is already on. AND #$20 BNE CLEAR LDA $DD02 ;Set the data direction regs ORA #3 STA $DD02 LDA $DD00 PHA AND #$03 STA OLDBANK PLA AND #252 ORA BANK STA $DD00 LDA $D018 STA OLDD018 LDA #$38 ;Set color map to base+$1C00 STA $D018 ;bitmap to 2nd 8k LDA $D011 ;And turn on bitmap ORA #$20 STA $D011 CLEAR JSR CHRGOT ;See if there's a color BEQ GRONDONE JSR GETBYT ;Get the char CLEARCOL LDA #00 ;Low byte of base address STA POINT LDA BASE ;Colormap is at base-$14 SEC SBC #$14 STA POINT+1 TXA LDY #00 LDX #4 :LOOP STA (POINT),Y INY BNE :LOOP INC POINT+1 DEX BNE :LOOP LDA BASE ;Now clear bitmap STA POINT+1 LDX #32 TYA :LOOP2 STA (POINT),Y INY BNE :LOOP2 INC POINT+1 DEX BNE :LOOP2 GRONDONE RTS * GROFF -- Restore old values if graphics are on. GROFF LDA $D011 AND #$20 BEQ GDONE GSET LDA $DD02 ;Set the data direction regs ORA #3 STA $DD02 LDA $DD00 AND #$7C ORA OLDBANK STA $DD00 LDA OLDD018 STA $D018 LDA $D011 AND #$FF-$20 STA $D011 GDONE RTS * * COLOR -- Set drawing color * COLOR JSR GETBYT COLENT CPX #00 ;MODE enters here BEQ :C2 :C1 CPX #01 BNE :RTS LDX #$FF :C2 STX BITMASK :RTS RTS * * MODE -- catch-all command. Currently implemented: * 00 Erase (background color) * 01 Foreground color * 16 SuperCPU mode -- screen -> A000, etc. * 17 Normal mode * 18 Double buffer mode * * Anything else -> BITMASK * MODENUM DFB 17 ;Current mode MODE JSR GETBYT CPX #2 BCC COLENT :C16 CPX #16 BNE :C18 STX MODENUM :SET16 LDA #$A0 ;Bitmap -> $A000 STA BASE LDA #01 STA BANK ;Bank 2 STA OLDBANK LDA #$FF ;End of BASIC memory STA $37 STA $33 LDA #$87 STA $38 STA $34 LDA #$24 ;Screen mem -> $8800 STA OLDD018 JSR GSET ;Part of GROFF LDA #$88 STA 648 ;Tell BASIC where the screen is STA $D07E ;Enable SuperCPU regs STA $D074 ;Bank 2 optimization STA $D07F ;Disable regs RTS :C18 CPX #18 ;Double-buffer mode! BNE :C17 STX MODENUM JSR :SET16 ;Set up mode 16 STA $D07E STA $D077 ;Turn off optimization STA $D07F RTS :C17 CPX #17 BNE MODEDONE MODE17 STX MODENUM LDA #$E0 STA BASE LDA #00 ;Bank 3 STA BANK LDA #3 ;Bank 0 == normal bank STA OLDBANK LDA #$FF STA $37 STA $33 LDA #$9F STA $38 STA $34 LDA #$14 ;Screen mem -> $0400 STA OLDD018 JSR GSET ;Part of GROFF LDA #$04 STA 648 ;Tell BASIC where the screen is STA $D07E STA $D077 ;No optimization STA $D07F RTS MODEDONE STX BITMASK RTS * * BUFFER -- Sets the current drawing buffer to 1 or 2, * depending on arg being even or odd. If double- * buffer mode is not enabled then punt. * * Now, buffer=0 swaps draw buffers, even/odd otherwise. * BUFFER JSR GETBYT LDA MODENUM CMP #18 BNE :PUNT LDY #$A0 TXA BNE :CONT CPY BASE BNE :CONT LDA #1 :CONT LSR BCC :LOW ;even = low buffer LDY #$E0 ;odd = high buffer :LOW STY BASE :PUNT RTS * * SWAP -- Swap displayed buffers. MODE 18 must * be enabled first. * SWAP LDA MODENUM CMP #18 BNE :PUNT LDA $DD00 ;Ooooooohhh, real tough! EOR #$01 STA $DD00 :PUNT RTS * * ORIGIN -- Set upper-left corner of the screen to * new coordinate offset. * ORIGIN JSR GETBYT STX ORGX JSR CHKCOM JSR GETBYT STX ORGY RTS ORG ;re-org PEND ;To get that label right :) begin 644 blarg1.0.lnx M`0A;"`H`ES4S,C@P+#`ZES4S,C@Q+#`ZES8T-BS"*#$V,BDZF2*3$1$1$1$1 M$1$B.IDB("`@("!54T4@3%E.6"!43R!$25-33TQ612!42$E3($9)3$4B.HDQ M,`````T@,B`@*DQ93E@@6%9)22`@(%=)3$P@0T]23$59#2`X(`U"3$%21RY3 M54U-05)9H*"@#2`S(`U0#2`R,S`@#4),05)',2XPH*"@H*"@H*`-(#$P(`U0 M#2`W(`U"3$%21R0X,#`PH*"@H*"@#2`Y(`U0#2`Q,C<@#4U/25)%,Z"@H*"@ MH*"@H*`-(#(@#5`-(#@P(`U454Y.14PRH*"@H*"@H*"@#2`R(`U0#2`R,C<@ M#4Q)4U-!2D]5,J"@H*"@H*`-(#(@#5`-(#(Q,2`-0TE20TQ%5$535*"@H*"@ MH`T@,2`-4`T@,3DV(`U03TQ!4E!,3U2@H*"@H*"@#2`R(`U0#2`Q,S`@#3.@ MH*"@H*"@H*"@`````````````@```((3`5153DY%3#*@H*"@H*"@H*`````` M```````"````@A,"3$E34T%*3U4RH*"@H*"@H`````````````(```""$P-# M25)#3$5415-4H*"@H*"@`````````````0```((3!U!/3$%24$Q/5*"@H*"@ MH*`````````````"`$),05)',2XP("`M+2!33$H@,B\Y-PU154E#2R!#3TU- M04Y$(%-534U!4EDZ#2`@6UT@1$5.3U1%4R!/4%1)3TY!3"!005)!345415(- M("!.14=!5$E612!005)!345415)3($%212!.3U0@04Q,3U=%1`T@("`@*%53 M12!/4DE'24X@24Y35$5!1"D-#4=23TX@6T-/3$]272`M(%154DX@3TX@1U)! M4$A)0U,@4T-2145.+`T@($E.251)04Q)6D4@248@0T],3U(@4U!%0TE&245$ M+`T@($-/3$]2/3$V*D9/4D5'4D]53D0K0D%#2T=23U5.1`U'4D]&1B`M(%15 M4DX@1U)!4$A)0U,@3T9-,14%2(%M#3TQ/4ET@+2!#3$5!4B!"251-05`O M0T],3U)-05`-4$Q/5"!8+%D@+2!3150@4$])3E0@24X@0U524D5.5"!#3TQ/ M4@U,24Y%(%@Q+%DQ+%@R+%DR("T@1%)!5R!,24Y%#4-)4D-,12!8+%DL4B`M M($-%3E1%4B!8+%D@4D%$2553(%(-3U))1TE.(%@L62`M(%-%5"!43U`@3$5& M5"!#3U).15(@5$\@6"Q9#4-/3$]2($X@+2`P/4)!0TM'4D]53D0@,3U&3U)% M1U)/54Y$#4U/1$4@3B`M(#$V/4)!3DL@,B!30U!5($]05$E-25I%1"P-("`Q M-SU"04Y+(#,L(#$X/41/54),12!"549&15(@34]$10U"549&15(@6TY=("T@ M4T54($1205<@0E5&1D52("A-3T1%(#$X*0U35T%0("T@4T54($1)4U!,05E% M1"!"549&15(@*$U/1$4@,3@I#0U#3TU03$5412!$3T-5345.5$%424].*U-/ M55)#12!!5`U(5%10.B\O4U1205154RY%4T%-+DY752Y%1%4O2E5$1"]&4DE$ M1T4-#4]2(%=2251%(%1/(%-*541$0$Y752Y%1%4-]WG.5UUWR M=5UG6DJ77S4I2@$(#0C-!YXR,#8S.J(```"I`(7]J0F%_JGS..D`A?NI$.D) MA?RIP(4#H`"$`K']D0+(T/GF_N8#QOS0\:3[L?V1`HC`_]#WH@6]!`.='<#* M$/=,`,!33RP@64]5(%=!3E0@02!314-2150@34534T%'12P@14@_($Y!4DY) M02P@3D%23DE!+"!.05).24$L($%704M%+B!,3U9%+B!42$E.2RX@4U!%04LN M($)%(%=!3$M)3D<@5%)%15,N($)%(%1!3$M)3D<@0D5!4U13+B!"12!$259) M3D4@5T%415)3+E-415!(14X@3"X@2E5$1"!755H@2$5212`Q+S(P+SDWH@6] M%L"=!`/*$/?HCEO!CES!3'O'!7?`Y\`UP4P```````!03$]4X$Q)3D7A0TE2 M0TQ%XD=2D>-'4D]&1N1-3T1%Y;!)1TE.YD-,14%2YT)51D9%4NA35T%0Z4-/ M3+#J`%W!\,'^PW_&Z<8DQ^+'L\:SQ]+'$L<@',"@!83_()_`L`L@PL#0])G] M`:G_8*;_G?L!Z+G[`9W[`?#GR!#TH@"D_[TCP/`8R>"P%=G[`=`$R.C0[NB] M(\#)X)#XZ-#A&&"D_\BY^P'P',F/T!"I`(7[R+G[`?`.Q?O0]O#FR2#PXLDB M\.I@R>"0,\GKL"\D#S`K..G?JH1)H`#*\`O(N2/`R>"0^,C0\KDCP#`&(-+_ MR-#UR;#P$,G@L`>D22G_;!_`I$E,`*>I3R#2_ZE2(-+_R-#4(',`",G@D!K) MZ[`6*"!(P4RNITG@"JJ]8L!(O6'`2$QS`"A,YZ<```$@Z[>E%#CM6\&%%+`% MQA4P%CB*[5S!D`^JX,BP"J44R4"E%>D!D`%@BBGXA?U*2DIM?,:%_JD`!OTJ M!OTJ!OTJ915E_H7^BBD'J*44*?@89?V%_9`"YOZE%"D'JJU=P?`@I?XX[7S& MR2"P%GBI-(4!L?U-X,$]X<%1_9']J3>%`5A@_X!`(!`(!`(!_W\_'P\'`P$@ MZ[>&!*44A0*E%84#(/VN(.NWA@6E`CCE%*JE`^45L!JE!*0%A06$!*44I`*$ M%(4"I0.D%845A`.0VH4HAB>BR*4$..4%L`9)_VD!HHB%)H[HPHZ8PZ44..U; MP844I17I`(45"(7_I11&_VI&_VI&_VJ%HRB0!JJ0"- M7<$@AL'N7<&]Z<&%`X4">*DTA0&F)N0GD'&E*-!MJ0"%`Z4".$I%`H4"BM`! MZ$J%_Z6C!:4P"[']3>#!)0)1_9']R,`(D`,@O<.E_SCE)Y`4RM#;L?U-X,$E M`E']D?VI-X4!6&!E)D8"T.9F`H7[I:,P#LDGL.BE_6D(A?V0`N;^YJ.E^\K0 MI_#*`*8GI2B-,,-*A06*T`/.,,-J1@+P%^4FD#?*T/7.,,,0\$8"(*?#J3>% M`5A@A?\@I\.I_X4"A0.EHS`.R2>PYZ7]:0B%_9`"YO[FHZ7_3$;#Q@40Q64G MA?^E*&G_A04@I\.E`H4#I?_(P`B0K87_(+W#I?],2L.EHP6E,`^Q_4W@P04" M)0-%`E']D?U@P/_P&:``I:4P$,D8L"^E_6E`A?VE_FD!A?[FI6"@!Z6E\!HP M%%%*D`A16*">"%IX6F,!#)*)`,I10I M!PDXA12I`845(/VN()ZWAFZ&;XK0!::E3(;!&&6EL`G!A6F%:DI)_X5KA6RE_87[ MA0*E_H7\A0.EI3CM7,&P!CCE;AB0`N5N"*I*2DJ%J2BP"`G@A:F**0>J((;! MA`6E_844I?Z%%7BI-(4!I6Y*H@"&;>9M1FG0`R#(Q09KT`,@\,4XY6VPZV5N MJB`:QB!+QJ5IA6JE:X5LBL9NQ@00`R!MQ:0%R(0%P`B0`R"=Q:1MQ&Z0OD9J M!FRE;TI)_ZH@&L8@2\:*Q@00`R!MQ>8%I`7`")`#()W%IF[P(1AE;L9ND-OF M;>5M1FG0!*H@S,5&:@9KT`2J(/3%!FRPP:DWA0%8J0&-7<%@J*D'A02EJ/`B MR1FP&J4"..E`A0*E`^D!A0.E^SCI0(7[I?SI`87\F,:H8&AH3&+%J*D`A06E MJ3`>R1CP[J44&&E`A12E%6D!A16E_1AI0(7]I?YI`87^F.:I8*H@&L:I_X5I MA6JEIS`6I?L8:0B%^Y`#YOP8I?UI"(7]D`+F_HKFIV"J($O&J?^%:X5LI:;) M*+`6I0(XZ0B%`K`#Q@,XI13I"(44L`+&%8K&IF"EI\DHL"JE:45JA?^EJ3`- MI`6Q_4W@P27_4?V1_:6HR1FP#:0$L?M-X,$E_U'[D?M@I:;)*+`JI6M%;(7_ MI:DP#:0%L11-X,$E_U$4D12EJ,D9L`VD!+$"3>#!)?]1`I$"8.``_P"M$=`I M(-`MK0+="0.-`MVM`-U(*0.-?L9H*?P-?<:-`-VM&-"-?\:I.(T8T*T1T`D@ MC1'0('D`\#`@GK>I`(7]K7S&..D4A?Z*H`"B!)']R-#[YO[*T/:M?,:%_J(@ MF)']R-#[YO[*T/9@K1'0*2#P(:T"W0D#C0+=K0#=*7P-?L:-`-VM?\:-&-"M M$=`IWXT1T&`@GK?@`/`&X`'0!:+_CN#!8!$@GK?@`I#JX!#0,XXDQZF@C7S& MJ0&-?<:-?L:I_X4WA3.IAX4XA32I)(U_QB#QQJF(C8@"C7[0C730C7_08.`2 MT!".),<@,\>-?M"-=]"-?]!@X!'0-8XDQZG@C7S&J0"-?<:I`XU^QJG_A3>% M,ZF?A3B%-*D4C7_&(/'&J02-B`*-?M"-=]"-?]!@CN#!8"">MZTDQ\D2T!2@ MH(K0!\Q\QM`"J0%*D`*@X(Q\QF"M),?)$M`(K0#=20&-`-U@()ZWCEO!(/VN M()ZWCES!8/VNO7=\IJY/K5ZZ]RNO=/TEN5YVI7=.4[:Z[=WRFKD^M7KKO*Z[ MLK3]6;F2=JEK3[;<^S4OK73]9FI7=.4_:UN=WRFKD^M7K6[*UNT_26Y7G:E= MTY3MK5)W?*:N3ZU>M4LK5*RO*4JM5*UU:^JEF2NJEW752[U6533U4N]UU2N] MJ3V]6VUF:4]6GJM=6OJE9DI/5KZJ2KY M4JJIJDK/MWUE2DOKW7;M;::?96Y+Z^3K2E*U)3LKKUJ>JKOUJ^;KJJVUV[6V M2^OM2DK*JKOUJ^;KJJVLK75KZJ69*L]2E*`("I>87]J8"%_JEL..EY MA?NIB.F`A?RIP(4#H`"$`K']D0+(T/GF_N8#QOS0\:3[L?V1`HC`_]#WH@6] M!`.='<#*$/=,`,!42$%4($)/62!!24Y4(%))1TA4+5-415!(14X@3"X@2E5$ M1"!755H@2$5212`R+S$P+SDWH@6]%L"=!`/*$/?HCEO!CES!3'O'!7?`Y\`U MP4P```````!03$]4X$Q)3D7A0TE20TQ%XD=2D>-'4D]&1N1-3T1%Y;!)1TE. MYD-,14%2YT)51D9%4NA35T%0Z4-/3+#J`%W!\,'^PW_&Z<8DQ^+'L\:SQ]+' M$L<@',"@!83_()_`L`L@PL#0])G]`:G_8*;_G?L!Z+G[`9W[`?#GR!#TH@"D M_[TCP/`8R>"P%=G[`=`$R.C0[NB](\#)X)#XZ-#A&&"D_\BY^P'P',F/T!"I M`(7[R+G[`?`.Q?O0]O#FR2#PXLDB\.I@R>"0,\GKL"\D#S`K..G?JH1)H`#* M\`O(N2/`R>"0^,C0\KDCP#`&(-+_R-#UR;#P$,G@L`>D22G_;!_`I$E,`*>I M3R#2_ZE2(-+_R-#4(',`",G@D!K)Z[`6*"!(P4RNITG@"JJ]8L!(O6'`2$QS M`"A,YZ<```$@Z[>E%#CM6\&%%+`%QA4P%CB*[5S!D`^JX,BP"J44R4"E%>D! MD`%@BBGXA?U*2DIM?,:%_JD`!OTJ!OTJ!OTJ915E_H7^BBD'J*44*?@89?V% M_9`"YOZE%"D'JJU=P?`@I?XX[7S&R2"P%GBI-(4!L?U-X,$]X<%1_9']J3>% M`5A@_X!`(!`(!`(!_W\_'P\'`P$@Z[>&!*44A0*E%84#(/VN(.NWA@6E`CCE M%*JE`^45L!JE!*0%A06$!*44I`*$%(4"I0.D%845A`.0VH4HAB>BR*4$..4% ML`9)_VD!HHB%)H[HPHZ8PZ44..U;P844I17I`(45"(7_I11&_VI&_VI&_VJ% MHRB0!JJ0"-7<$@AL'N7<&]Z<&%`X4">*DTA0&F)N0G MD'&E*-!MJ0"%`Z4".$I%`H4"BM`!Z$J%_Z6C!:4P"[']3>#!)0)1_9']R,`( MD`,@O<.E_SCE)Y`4RM#;L?U-X,$E`E']D?VI-X4!6&!E)D8"T.9F`H7[I:,P M#LDGL.BE_6D(A?V0`N;^YJ.E^\K0I_#*`*8GI2B-,,-*A06*T`/.,,-J1@+P M%^4FD#?*T/7.,,,0\$8"(*?#J3>%`5A@A?\@I\.I_X4"A0.EHS`.R2>PYZ7] M:0B%_9`"YO[FHZ7_3$;#Q@40Q64GA?^E*&G_A04@I\.E`H4#I?_(P`B0K87_ M(+W#I?],2L.EHP6E,`^Q_4W@P04")0-%`E']D?U@P/_P&:``I:4P$,D8L"^E M_6E`A?VE_FD!A?[FI6"@!Z6E\!HP%% M%*D`A16*">"%IX6F,!#)*)`,I10I!PDXA12I`845(/VN()ZWAFZ&;XK0!::E M3(;!&&6EL`G!A6F%:DI)_X5KA6RE_87[A0*E_H7\A0.EI3CM7,&P!CCE;AB0`N5N M"*I*2DJ%J2BP"`G@A:F**0>J((;!A`6E_844I?Z%%7BI-(4!I6Y*H@"&;>9M M1FG0`R#(Q09KT`,@\,4XY6VPZV5NJB`:QB!+QJ5IA6JE:X5LBL9NQ@00`R!M MQ:0%R(0%P`B0`R"=Q:1MQ&Z0OD9J!FRE;TI)_ZH@&L8@2\:*Q@00`R!MQ>8% MI`7`")`#()W%IF[P(1AE;L9ND-OF;>5M1FG0!*H@S,5&:@9KT`2J(/3%!FRP MP:DWA0%8J0&-7<%@J*D'A02EJ/`BR1FP&J4"..E`A0*E`^D!A0.E^SCI0(7[ MI?SI`87\F,:H8&AH3&+%J*D`A06EJ3`>R1CP[J44&&E`A12E%6D!A16E_1AI M0(7]I?YI`87^F.:I8*H@&L:I_X5IA6JEIS`6I?L8:0B%^Y`#YOP8I?UI"(7] MD`+F_HKFIV"J($O&J?^%:X5LI:;)*+`6I0(XZ0B%`K`#Q@,XI13I"(44L`+& M%8K&IF"EI\DHL"JE:45JA?^EJ3`-I`6Q_4W@P27_4?V1_:6HR1FP#:0$L?M- MX,$E_U'[D?M@I:;)*+`JI6M%;(7_I:DP#:0%L11-X,$E_U$4D12EJ,D9L`VD M!+$"3>#!)?]1`I$"8.``_P"M$=`I(-`MK0+="0.-`MVM`-U(*0.-?L9H*?P- M?<:-`-VM&-"-?\:I.(T8T*T1T`D@C1'0('D`\#`@GK>I`(7]K7S&..D4A?Z* MH`"B!)']R-#[YO[*T/:M?,:%_J(@F)']R-#[YO[*T/9@K1'0*2#P(:T"W0D# MC0+=K0#=*7P-?L:-`-VM?\:-&-"M$=`IWXT1T&`@GK?@`/`&X`'0!:+_CN#! M8!$@GK?@`I#JX!#0,XXDQZF@C7S&J0&-?<:-?L:I_X4WA3.IAX4XA32I)(U_ MQB#QQJF(C8@"C7[0C730C7_08.`2T!".),<@,\>-?M"-=]"-?]!@X!'0-8XD MQZG@C7S&J0"-?<:I`XU^QJG_A3>%,ZF?A3B%-*D4C7_&(/'&J02-B`*-?M"- M=]"-?]!@CN#!8"">MZTDQ\D2T!2@H(K0!\Q\QM`"J0%*D`*@X(Q\QF"M),?) M$M`(K0#=20&-`-U@()ZWCEO!(/VN()ZWCES!8";9;TZ;;5K*T_5VYDG:I:T^ MV7/MI+ZUT_69J5W3E/VNO7=\IJY/K5ZZ]RNO=/TEN5YVI7=.4[:Z[=WRFKD^ MM7KKO*Z[LK3]6;F2=JEK3[;<^S4OK73]9FI7=.4_:UN=WRFKD^M7K6[*UNT_ M26Y7G:E=TY3MK5)W?*:N3ZU4U2E*`0@2"`4`5$DDLB(P,#`P,#`B`!L("@#C M(#(P`$0(%`!80[*U*+LH,2FL,3`PJC$Q,"DZ64.RM2B[*#$IK#$P,*HU,"D` M=0@>`(%)LC"D,S$YJ3(ZZC$ZX2!80RQ90RQ)+#`ZZC`ZX2!80RQ90RQ)JC$L M,#J"`*H(*`"!2;(PI#$Y.:DR.NHQ.N$@6$,L64,L,S$Y+$DZZC`ZX2!80RQ9 M0RPS,3DL2:HQ.H(`X`@R`(%)LC,Q.:0PJ:LR.NHQ.N$@6$,L64,L22PQ.3DZ MZC`ZX2!80RQ90RQ)JS$L,3DY.H(`$@D\`(%)LC$Y.:0PJ:LR.NHQ.N$@6$,L M64,L,"Q).NHP.N$@6$,L64,L,"Q)JS$Z@@`;"6,`2;)420`N"60`H4$D.HM! M)+(B(J""T`CR!2/3DP*D-/4R@T*E!(22D`]@@R M`(\@4CTV,"HH,2U#3U,H4$A)*2D`$`DW`(\@4CTU,"HH,2TR*D-/4RA02$DI M*0`?"3P`CR!2/3$R*E!(20`S"48`CR!2/34P*D-/4RA02$DI`$T)9`!8LE*L MOBA02$DI.EFR4JR_*%!(22D`70EN`.`@6$.J6"Q90ZM9`&,)>`""`'8)YP.A 6020ZBT$DLB(BISDY.0!\">@#Y````.`@ ` end