; ----------------------------------------------------------- ; geosDemo (code: ShadowM) ; demo program for "Intro to GEOS Programming" talk at ECCC 2015 ; ----------------------------------------------------------- .if Pass1 .include shadowSym .include shadowMac .endif ; ----------------------------------------------------------- PROC_RCT = 0 ;process IDs PROC_GFX = 1 PROC_STR = 2 NUM_PROC = 3 RCT_TXT = 7 ;offsets to demo menu texts GFX_TXT = 12 STR_TXT = 17 ; ----------------------------------------------------------- lda #2 ;50% stipple jsr SetPattern LoadB r2L,0 LoadB r2H,199 LoadW r3,0 LoadW r4,319 jsr Rectangle ;clear screen jsr initDAs LoadW r0,frzIcons jsr DoIcons LoadW r0,mainMenu lda #0 jsr DoMenu jsr primeRnd LoadW r0,procTbl lda #NUM_PROC ;number of processes jsr InitProcesses ;don't activate rts ;to MainLoop ; ------------------------------------------------------------ ; Calculate number of items in "geos" sub-menu by counting DAs on ; disk and adding 1 for "info" item; update menu tables. Number of ; DAs is stored in a9L. ; ------------------------------------------------------------ initDAs: LoadW r6,DA0Text LoadB r7L,#DESK_ACC LoadB r7H,6 ;max. no. of DA's LoadW r10,#0 ;never mind permanent name jsr FindFTypes txa beq 20$ jmp errHndlr 20$ lda #6 sec sbc r7H ;r7: 6 - no. of DAs found beq 60$ sta a9L ;no. of DAs found clc adc #2 ;for info and font menu items pha ora #VERTICAL | CONSTRAINED sta geosMenu+6 pla sta a0L asl a asl a asl a asl a sbc a0L ;mult. by 16 & sub. twice sbc a0L ;is same as mult. by 14 adc #14 ;(X-pos. of first selection) sta geosMenu+1 LoadB a9H,0 ;DA name string length LoadW r0,DA0Text 30$ jsr strWidth ;find longest DA name lda a0L ;won't be > 256 cmp a9H bcc 40$ sta a9H ;save longest name 40$ dec a9L ;DA counter beq 50$ AddVW 17,r0 ;next DA name bra 30$ 50$ LoadW r0,blank ;padding jsr strWidth lda a0L asl a ;on either side clc adc a9H sta geosMenu+4 60$ rts ; ----------------------------------------------------------- doInfo: jsr GotoFirstMenu LoadW r0,infoDB jsr DoDlgBox rts ; ----------------------------------------------------------- doFont: jsr GotoFirstMenu LoadW r0,fontDB LoadB r7L,FONT ;search for font files LoadW r5,fontName ;selected font name LoadW r10,0 ;ignore permanent name jsr DoDlgBox lda r0L cmp #OK bne 30$ ;user clicked Cancel icon? lda fontName beq 20$ ;no fonts found? jsr getPtSiz ;load point sizes, show DB bcs 30$ ;user canceled? lda strEnabl ;string demo running? beq 10$ LoadW r0,fontLoad jsr LoadCharSet ;change while running 10$ rts 20$ LoadW fontMsg,fontMsg0 ;"No fonts found." bra 40$ 30$ LoadW fontMsg,fontMsg1 ;"No font selected." 40$ LoadB fontName,0 lda strEnabl bne 50$ ;no cancel dialog if demo is running LoadW r0,noFontDB jsr DoDlgBox 50$ rts ; ----------------------------------------------------------- doQuit: jsr GotoFirstMenu ldx #NUM_PROC-1 10$ jsr BlockProcess ;doesn't trash .X dex bpl 10$ jmp EnterDeskTop ; ----------------------------------------------------------- frzSvc: ldx #0 ;"Freeze" icon service routine 10$ lda rctEnabl,x beq 20$ jsr FreezeProcess 20$ inx cpx #3 bne 10$ LoadW frzBmp,rsmIcon LoadW frzPtr,rsmSvc LoadW r0,frzIcons jsr DoIcons rts ; ----------------------------------------------------------- rsmSvc: ldx #0 ;"Resume" icon service routine 10$ lda rctEnabl,x beq 20$ jsr UnfreezeProcess 20$ inx cpx #3 bne 10$ LoadW frzBmp,frzIcon LoadW frzPtr,frzSvc LoadW r0,frzIcons jsr DoIcons rts ; ----------------------------------------------------------- ; Get point sizes for font; prompt user to select one. ; pass: name of selected font at fontName ; return: carry set if user canceled, clear otherwise ; ----------------------------------------------------------- getPtSiz: LoadW r0,fontName jsr OpenRecordFile txa beq 10$ jmp errHndlr 10$ ldx #2 LoadB a9L,0 ;record counter 20$ txa lsr a tay dey ;.Y = record no. lda fileHeader,x beq 30$ ;empty or unused? tya ldy a9L sta points,y iny sty a9L cpy #9 ;point table full? beq 40$ 30$ inx inx bne 20$ 40$ lda #0 ldy a9L sta points,y ldx #0 ;build point sizes string LoadW a6,lblPts 50$ lda points,x jsr byte2asc ;.X preserved, .Y set to null inx lda points,x ;look ahead beq 60$ lda #',' sta (a6),y ;replaces null iny lda #' ' sta (a6),y iny tya clc adc a6L sta a6L lda #0 adc a6H sta a6H bra 50$ 60$ LoadW r0,pointDB LoadB pointSz,0 LoadW r5,pointSz jsr DoDlgBox lda r0L cmp #CANCEL ;user clicked Cancel icon? beq 100$ lda pointSz ;else OK or Return beq 100$ ;empty string? jsr valPoint ;validate point size bcc 70$ ;validation OK? LoadW r0,badPntDB jsr DoDlgBox bra 60$ ;try again 70$ jsr PointRecord txa beq 80$ jmp errHndlr 80$ LoadW r2,$6000-fontLoad LoadW r7,fontLoad jsr ReadRecord txa beq 90$ jmp errHndlr 90$ jsr CloseRecordFile clc ;font loaded OK rts 100$ jsr CloseRecordFile sec ;user canceled rts ; ----------------------------------------------------------- ; Validate point size. ; pass: pointSz, entered point size (ASCII) ; return: .A, point size (record number) ; carry set on failure, clear otherwise ; ----------------------------------------------------------- valPoint: ldx #0 10$ lda pointSz,x beq 40$ cmp #'0' ;numeric? bcs 20$ sec rts 20$ cmp #'9'+1 bcc 30$ rts 30$ inx bne 10$ 40$ dex ;convert point size to binary lda pointSz,x ;one's position and #$0f sta a9L dex bmi 50$ ;two digits? lda pointSz,x ;ten's position and #$0f sta a8L lda #10 sta a7L ldx #a8L ;destination ldy #a7L jsr BBMult lda a9L clc adc a8L sta a9L ;can't be > 256 50$ ldx #0 ;entered point size in table? 60$ lda points,x bne 70$ sec ;end of table, not found rts 70$ cmp a9L ;point size to load beq 80$ inx bne 60$ 80$ clc ;validation successful rts ; ----------------------------------------------------------- ; Run a Desk Accessory. ; ----------------------------------------------------------- runDA: tax dex ;DAs start at 2nd menu entry dex stx a9L jsr GotoFirstMenu lda a9L ;use menu selection no. asl a ;to index into table of asl a ;DA filenames asl a asl a clc ;mult. by 16 and add 1 adc a9L ;is same as mult. by 17 adc #[DA0Text sta r6L lda #0 adc #]DA0Text sta r6H LoadB r0L,0 ;standard loading for DA's LoadB r10L,0 ;unused (see HHGG) jsr GetFile ;run the DA ; ------------------------------------------------ ; return from DA ; ------------------------------------------------ txa beq 50$ jmp errHndlr 50$ MoveB screencolors,r2L ;restore screen colors LoadW r1,$8C00 ;color data (by card) LoadW r0,(25*40) jsr FillRam LoadB r2L,15 ;recover screen image LoadB r2H,199 LoadW r3,0 LoadW r4,319 jsr RecoverRectangle 60$ rts ; ----------------------------------------------------------- ; Rectangles demo (solid and filled). ; ----------------------------------------------------------- rctDemo: jsr GotoFirstMenu lda rctEnabl beq 10$ ldx #PROC_RCT jsr BlockProcess lda #0 sta rctEnabl jsr dsablFrz LoadW demoMenu+RCT_TXT,rctText jsr resetScr rts 10$ ldx #PROC_RCT jsr RestartProcess jsr enablFrz lda #$ff sta rctEnabl LoadW demoMenu+RCT_TXT,rctTextE rts ; ----------------------------------------------------------- ; Graphics demo: splatter some bitmaps across the screen. ; ----------------------------------------------------------- gfxDemo: jsr GotoFirstMenu lda gfxEnabl beq 10$ ldx #PROC_GFX jsr BlockProcess lda #0 sta gfxEnabl jsr dsablFrz LoadW demoMenu+GFX_TXT,gfxText jsr resetScr rts 10$ LoadB whichPic,0 ldx #PROC_GFX jsr RestartProcess jsr enablFrz lda #$ff sta gfxEnabl LoadW demoMenu+GFX_TXT,gfxTextE rts ; ----------------------------------------------------------- ; String handling demo. ; ----------------------------------------------------------- strDemo: jsr GotoFirstMenu lda strEnabl beq 10$ ldx #PROC_STR jsr BlockProcess lda #0 sta strEnabl jsr dsablFrz LoadW demoMenu+STR_TXT,strText jsr resetScr rts 10$ LoadW r0,askStrDB LoadB showText,0 ;initialize to empty string LoadW r5,showText ;address of input string jsr DoDlgBox lda r0L cmp #CANCEL beq 40$ ;user clicked Cancel icon? lda showText ;else OK or Return beq 40$ ;empty string? lda fontName ;font selected? beq 20$ LoadW r0,fontLoad jsr LoadCharSet bra 30$ 20$ jsr UseSystemFont 30$ LoadB styleNdx,0 ldx #PROC_STR jsr RestartProcess jsr enablFrz lda #$ff sta strEnabl LoadW demoMenu+STR_TXT,strTextE 40$ rts ; ----------------------------------------------------------- ; Enable Freeze icon when first demo starts. ; ----------------------------------------------------------- enablFrz: lda rctEnabl ;any already running? ora gfxEnabl ora strEnabl bne 10$ LoadW frzBmp,frzIcon LoadW frzPtr,frzSvc LoadW r0,frzIcons jsr DoIcons 10$ rts ; ----------------------------------------------------------- ; Disable Freeze icon when last demo stops. ; ----------------------------------------------------------- dsablFrz: lda rctEnabl ;any still running? ora gfxEnabl ora strEnabl bne 10$ LoadW frzBmp,frzIconD LoadW frzPtr,0 LoadW r0,frzIcons jsr DoIcons 10$ rts ; ----------------------------------------------------------- ; Show rectangle at random location (without stepping on menu). ; Sizes range from 24-88 pixels wide and 24-56 high. ; ----------------------------------------------------------- showRct: LoadW r2,65 ;(88 - 24) + 1 jsr sidRnd lda r1L clc adc #24 sta wd ;width of rectangle LoadW r2,33 ;(56 - 24) + 1 jsr sidRnd lda r1L clc adc #24 sta ht ;height of rectangle ; ------------------------------------------------ ; calculate X and Y positions of rectangle ; ------------------------------------------------ lda #184 ;((200 - ht) - 15) - 1 sec sbc ht sta r2L lda #0 sta r2H jsr sidRnd lda r1L clc adc #15 ;don't step on menu sta ypos lda #[319 ;(320 - [demoMenu+4] - wd) - 1 sec sbc wd sta r2L lda #]319 sbc #0 sta r2H lda demoMenu+1 ;menu bottom edge cmp ypos bcc 10$ ;might cover menu? lda r2L sec sbc demoMenu+4 ;menu right edge sta r2L lda r2H sbc demoMenu+5 sta r2H 10$ jsr sidRnd lda demoMenu+1 ;need to correct? cmp ypos bcc 20$ lda r1L clc adc demoMenu+4 ;adjust to protect menu sta r1L lda r1H adc demoMenu+5 sta r1H 20$ MoveW r1,xpos ; ------------------------------------------------ ; set up for call to Rectangle or FrameRectangle ; ------------------------------------------------ lda ypos sta r2L clc adc ht sta r2H lda xpos sta r3L clc adc wd sta r4L lda xpos+1 sta r3H adc #0 sta r4H ; ------------------------------------------------ ; fill some rectangles with a pattern, others frame only ; ------------------------------------------------ lda #$ff ;solid line doRect: jsr FrameRectangle lda xpos clc lsr a bcc 30$ ;even ? fill : frame only rts 30$ PushW r2 LoadW r2,32 ;(0 - 31) jsr sidRnd ;choose random fill pattern lda r1L jsr SetPattern PopW r2 inc r2L ;fill within rectangle borders dec r2H inc r3L bne 40$ inc r3H 40$ lda r4L bne 50$ dec r4H 50$ dec r4L jsr Rectangle ;draw fill pattern rts ; ----------------------------------------------------------- ; Show bitmap at random location (without stepping on menu). ; Bitmaps are 11 cards wide by 56 pixels high; menu is 15 pixels high. ; ----------------------------------------------------------- showGfx: LoadW r2,128 ;((200 - 56) - 15) - 1 jsr sidRnd lda r1L clc adc #15 ;don't step on menu sta ypos lda demoMenu+1 ;menu buttom edge cmp ypos bcc 10$ ;might cover menu? lda demoMenu+4 ;menu right edge in pixels lsr a lsr a lsr a ;convert to cards tax inx stx a9L ;adjust to protect menu lda #30 ;(40 - a9L - 11) + 1 sec sbc a9L sta r2L lda #0 sta r2H jsr sidRnd lda r1L clc adc a9L ;offset from left sta xpos bne 20$ 10$ LoadW r2,30 ;(40 - 11) + 1 = 30 jsr sidRnd MoveB r1L,xpos 20$ lda whichPic ;get bitmap address and size asl a tax lda picAddrs,x sta r0L lda picAddrs+1,x sta r0H MoveB xpos,r1L ;low byte from Ddiv MoveB ypos,r1H ;low byte from Ddiv lda picDims,x sta r2L lda picDims+1,x sta r2H doBitmap: jsr BitmapUp ldx whichPic inx cpx #4 bcc 30$ ldx #0 30$ stx whichPic rts ; ----------------------------------------------------------- ; Show string at random location (without stepping on ; menu), using different font styles. Since PutString uses ; the baseline as the Y position, take the baseline offset ; into account. ; ----------------------------------------------------------- showStr: ldx styleNdx ;get next style in list inx cpx #STYLECNT bne 10$ ldx #0 10$ stx styleNdx lda styles,x sta style+1 lda styleBits,x tax lda showText jsr GetRealSize ;get character dimensions sta a9L ;baseline offset ; ------------------------------------------------ LoadW r2,186 ;Y position: (200-15-char. height)+1 lda r2L sec sbc a9L sta r2L lda r2H sbc #0 sta r2H jsr sidRnd lda r1L clc adc #15 adc a9L ;character height sta ypos ; ------------------------------------------------ LoadW r2,272 ;X position (at least one character) lda ypos sec sbc a9L ;baseline offset sta a9H ;position of top of character lda demoMenu+1 ;menu bottom edge cmp a9H ;might cover menu? bcc 20$ lda r2L sec sbc demoMenu+4 ;menu right edge sta r2L lda r2H sbc demoMenu+5 sta r2H jsr sidRnd lda r1L clc adc demoMenu+4 sta xpos lda r1H adc demoMenu+5 sta xpos+1 bra 30$ 20$ jsr sidRnd MoveW r1,xpos ; ------------------------------------------------ 30$ LoadW StringFaultVec,PutStrFault MoveW xpos,r11 MoveB ypos,r1H LoadW r0,style doString: jsr PutString LoadW StringFaultVec,0 rts ; ----------------------------------------------------------- ; Hack for PutString trying to find the next character ; that will fit when a margin fault occurs: advance the ; pointer to the null at the end of the string. See HHGG. ; ----------------------------------------------------------- PutStrFault: ldy #0 10$ inc r0L ;advance to next character bne 20$ inc r0H 20$ lda (r0),y ;read it bne 10$ ;end of string? rts ;yes, we've faked out GetString ; ----------------------------------------------------------- ; Convert binary byte to decimal string by repeated subtraction. ; pass: .A, binary number ; a6, address to put string (four bytes) ; return: null-terminated decimal string at (a6) ; .Y points to null byte at end ; destroyed: a0L (minuend) ; a1L (accumulator) ; a1H (division constant) ; ----------------------------------------------------------- byte2asc: sta a0L ldy #0 sty a1L lda #100 sta a1H 10$ lda a0L 20$ cmp a1H bcc 30$ sec sbc a1H sta a0L inc a1L bne 20$ 30$ lda a1L bne 35$ cpy #0 ;no leading zeros beq 37$ 35$ ora #$30 sta (a6),y iny lda #0 sta a1L 37$ lda a1H cmp #10 beq 40$ lda #10 sta a1H bne 10$ 40$ lda a0L ora #$30 sta (a6),y iny lda #0 sta (a6),y rts ; ----------------------------------------------------------- ; Reset screen (erase except for menu area). ; ----------------------------------------------------------- resetScr: lda #2 ;50% stipple jsr SetPattern LoadB r2L,15 ;don't erase menu LoadB r2H,199 LoadW r3,0 LoadW r4,319 jsr Rectangle ;clear screen rts ; ----------------------------------------------------------- ; Get string width in pixels. ; pass: string address in r0 ; return: string width in a0 ; destroyed: a1L ; ----------------------------------------------------------- strWidth: ldy #0 sty a0L sty a0H 10$ lda (r0),y beq 20$ sty a1L jsr GetCharWidth clc adc a0L sta a0L lda #0 adc a0H sta a0H ldy a1L iny bne 10$ ;string must be < 256 chars. 20$ rts ; ----------------------------------------------------------- ; Generic beep. ; ----------------------------------------------------------- beep: jsr enableIO LoadB $d400,#$31 ;voice 1 frequency low LoadB $d401,#$1c ;voice 1 frequency high LoadB $d405,#$00 ;voice 1 attack/decay LoadB $d406,#$f9 ;voice 1 sustain/release LoadB $d418,#$0c ;no filters, volume 12 LoadB $d404,#$11 ;gate on triangle, voice 1 LoadB $d404,#$10 ;gate off voice 1 jsr restoreIO rts ; ----------------------------------------------------------- ; Prime SID chip to generate random numbers. ; ----------------------------------------------------------- primeRnd: jsr enableIO lda #0 sta $d40e ;voice 3 frequency low lda #$80 ;frequency to $8000 sta $d40f ;voice 3 frequency high sta $d412 ;noise waveform, gate off 3 jsr restoreIO rts ; ----------------------------------------------------------- ; Pseudo-random number generator (uses SID chip). ; pass: r2, high limit (1-based) ; return: r1, pseudo-random number ; destroyed: .A, .X, .Y, r1, r8, r9 ; ----------------------------------------------------------- sidRnd: LoadW r1,65535 ;r2 loaded on entry ldx #r1 ldy #r2 jsr Ddiv MoveW r1,r2 ;r2 = 65535 / high limit jsr enableIO lda $d41b sta r1L ldx #7 ;delay at least 32 cycles 10$ dex bne 10$ lda $d41b sta r1H jsr restoreIO ldx #r1 ldy #r2 jsr Ddiv ;r1 = r1 / r2 rts ; ----------------------------------------------------------- ; Enable/disable IO. ; ----------------------------------------------------------- enableIO: php pla sta flagSave sei lda $01 sta ioSave and #$f8 ora #$05 sta $01 rts ; ------------------------------------------------ restoreIO: lda ioSave sta $01 lda flagSave pha plp rts ; ----------------------------------------------------------- ; Generic error handler. ; pass: error no. in .a (99 denotes internal error) ; return: kills program and exits to deskTop ; ----------------------------------------------------------- errHndlr: pha and #$f0 lsr a lsr a lsr a lsr a ora #$30 sta errorNum pla and #$0f ora #$30 sta errorNum+1 jsr beep LoadW r0,errorDB jsr DoDlgBox jmp EnterDeskTop