PAGE SBTTL "--- MACHINE-DEPENDENT I/O: CBM64 ---" ; ---------------------------- ; FETCH ASCII KEYCODE INTO [A] ; ---------------------------- ; EXIT: ASCII IN [A] & [IOCHAR] CYCLE EQU $E0 ; SHORT BLINK CYCLE ; FIRST INITIALIZE THE CURSOR CURSON: LDX #$FF STX CURSOR ; FORCE THE CURSOR "ON" INX ; = 0 STX BLINK+LO ; RESET THE BLINK COUNTER STX BLINK+HI ; FOR A LONG DELAY SEC ; GET CURSOR POSITION JSR PLOT ; [Y]=XPOS, [X]=YPOS TXA ; CALC SPRITE Y-POS ASL A ASL A ASL A CLC ADC #57 STA SP0Y TYA ; GET X-POS INTO [A] CMP #40 ; STRIP OFF BCC NOLOG ; LOGICAL LINE OFFSET SBC #40 NOLOG: LDX #0 CMP #29 ; IF X-POS < 29 BCC CURX ; MSB OF SPRITE POS IS ZERO INX ; ELSE SET BIT 0 CURX: STX MSIGX ; OF X-POS MSB ASL A ; CALC LSB OF SPRITE X-POS ASL A ASL A CLC ADC #24 STA SP0X RTS ; AND THE RTN TO GET A PROPER KEY GETKEY: JSR CURSON ; INIT CURSOR GETK1: JSR GETIN ; GET A KEY TAX ; SAVE HERE INC BLINK+LO ; TIME TO BLINK YET? BNE NOBLIN ; NOT TILL BOTH BLINK TIMERS INC BLINK+HI ; ARE ZERO BNE NOBLIN LDA #CYCLE ; RESET MSB OF BLINK COUNTER STA BLINK+HI ; FOR SHORT BLINK INTERVAL LDA CURSOR ; FLIP THE CURSOR EOR #$FF ; SHAPE STA CURSOR ; AND UPDATE IT NOBLIN: TXA ; KEY BEING PRESSED? BEQ GETK1 ; NOT IF CODE WAS ZERO ; CONVERT & MASK KEYCODE IN [A] GOTKEY: ; ENTRY POINT FROM INPUT CMP #'A' ; CONVERT UNSHIFTED ALPHA BCC MASK ; TO ASCII LOWER CASE CMP #'Z'+1 BCS MASK ADC #$20 JMP TICK ; SKIP REST OF MALARKY (LC-A) MASK: LDX #GLISTL ; PASS ON ANY GOOD KEYS (LC-A) GOODL: CMP GOODLST,X ; LOCATED IN BAD RANGE (LC-A) BEQ TICK ; (LC-A) DEX ; (LC-A) BPL GOODL ; (LC-A) AND #%01111111 ; SCREEN OUT SHIFTS CMP #SPACE ; ANYTHING ELSE < "SPACE" BCC BADKEY ; IS BAD LDX #BLISTL ; IGNORE OBSTINATE NON ALLOWED KEYS BADL: CMP BADLIST,X BEQ BADKEY DEX BPL BADL CMP #'z'+1 ; PASS L-C ALPHA BCS BADKEY CMP #'a' BCS TICK CMP #'Z'+1 ; PASS U-C ALPHA BCC TICK ; AND OTHER ASCII CHARS BADKEY: JSR BOOP ; REJECT BAD KEYPRESS JMP GETK1 ; AND TRY AGAIN ; "CLICK" THE KEY (LC-A = JUST LEAVE I HATE CLICKING) TICK: STA IOCHAR ;HOLD ON TO IT ADC RAND ;FUTZ WITH RANDOM STA RAND EOR RASTER STA RASTER LDA IOCHAR ; GET CHAR INTO [A] RTS ; AND RETURN IT GOODLST: DB EOL,BACKSP,$11,$1D,$91,$5E,$9D ; DOWN,RIGHT,UP,UP,LEFT ARROWS GLISTL EQU $-GOODLST-1 BADLIST: DB '%','&','+','=','@','<','>' BLISTL EQU $-BADLIST-1 ; ------------------------- ; OUTPUT AN ASCII CHARACTER ; ------------------------- LETTER: CMP #'a' ; LOWER-CASE? BCC LET0 ; NO, CONTINUE CMP #'z'+1 BCS LETEX ; CTRL CHARS AND #%01011111 ; ELSE MASK FOR LOWER-CASE JMP CHROUT LET0: CMP #'A' ; UPPER-CASE? BCC LETEX CMP #'Z'+1 BCS LETEX ORA #%00100000 ; MAKE UPPER LETEX: JMP CHROUT ; ----------------- ; PRINT CHAR IN [A] ; ----------------- CHAR: STA IOCHAR ; SAVE HERE TXA ; SAVE [X] AND [Y] PHA TYA PHA SEC ; GET CURSOR X- AND Y-POS JSR PLOT ; INTO [Y] AND [X], RESPECTIVELY TYA CMP #40 ; STRIP OFF THE BCC CHKEOL ; LOGICAL LINE OFFSET SBC #40 ; UPDATE [Y] IF NECESSARY TAY CHKEOL: LDA IOCHAR ; RESTORE CHAR CMP #EOL ; IS IT EOL? BEQ OUTEOL ; YES, SPECIAL HANDLING ; HANDLE A NON-EOL CHAR CPX #YSIZE-1 ; ON LAST SCREEN LINE? BCC NOSCRL ; NO, NO SCROLL NEEDED CPY #XSIZE ; LAST CHAR ON LINE? BCC NOSCRL ; NO, DON'T SCROLL ; SCROLL THE SCREEN (BOTTOM SCREEN ONLY) DOSCRL: DEX ; PUSH CURSOR UP ONE LINE CLC JSR PLOT ; RESET THE CURSOR LDX SLINE ; GET CURRENT SCROLL LINE SRL0: CPX #YSIZE BEQ SRL2 ; SCROLL DONE LDA LOLINE,X ; GET ADDR OF DEST LINE STA LTO+LO ; INTO [LTO] LDA HILINE,X STA LTO+HI INX LDA LOLINE,X ; GET ADDR OF SOURCE LINE STA LFROM+LO ; INTO [LFROM] LDA HILINE,X STA LFROM+HI LDY #XSIZE SRL1: LDA (LFROM),Y ; MOVE SOURCE LINE STA (LTO),Y ; TO DEST LINE DEY BPL SRL1 BMI SRL0 ; LOOP TILL [X] = XSIZE SRL2: LDX #XSIZE LDA #SPACE SRL3: STA SCREEN+960,X ; CLEAR LAST LINE DEX ; OF SCREEN RAM BPL SRL3 NOSCRL: LDA IOCHAR ; RESTORE CHAR CMP #$22 BNE NOQUOT JSR LETTER ; QUOTE MARKS SCREW LDA #0 ; UP SYSTEM, SO STA QUOTMOD ; TURN OFF QUOTE MODE FLAG JMP CHDUN NOQUOT: JSR LETTER ; OFF TO THE SCREEN! CHDUN: PLA ; RESTORE [X] AND [Y] TAY PLA TAX RTS ; HANDLE EOL OUTEOL: CPX #YSIZE-1 ; LAST SCREEN LINE? BCC NOSCRL ; NO, DON'T SCROLL BCS DOSCRL ; ELSE SCROLL ; --------------------- ; FETCH A LINE OF INPUT ; --------------------- ; ENTRY: ABS ADDR OF READ BUFFER IN [ARG1] ; EXIT: # CHARS READ IN [A] INPUT: JSR LINOUT ; FLUSH [LBUFF] LDX #0 ; RESET LINE COUNT STX LENGTH STX CHRCNT STX LINCNT ; RESET LINE COUNT STX NDX ; CLEAR INPUT QUEUE INX ; = 1 STX SPENA ; ENABLE CURSOR DMA DEC NARGS ; CHECK IF TIME LIMIT (EZIP) DEC NARGS BEQ INP2 ; NO LDA ARG3+LO ; GET DELAY WANTED STA I+HI LDA #0 STA J+HI STA J+LO DEC NARGS ; IS THERE A FCN? BEQ INP4 ; NO LDA ARG4+LO ; YES, SET IT STA J+LO LDA ARG4+HI STA J+HI INP4: JSR CURSON ; INIT CURSOR JSR TIMEIN ; AND GET A KEY WITHIN TIME LIMIT BCC INP5 JMP LEXBAD ; ELSE ABORT INP5: LDY #0 STY CHARCNT BEQ INLP1 ; (JMP) [A] NOW HAS A CHAR INP2: LDY #0 ; AND CHAR COUNT STY CHARCNT INLOOP: JSR GETKEY ; GET ASCII INTO [A] AND [IOCHAR] INLP1: CMP #UP BEQ CBAD ; CLEAR OFF ARROWS (EZIP) CMP #$5E ; UP BEQ CBAD CMP #DOWN BEQ CBAD CMP #LEFT BEQ CBAD CMP #RIGHT BEQ CBAD CMP #EOL ; EOL? BEQ ENDLIN ; LINE DONE IF SO CMP #BACKSP ; BACKSPACE? BEQ BACKUP ; SPECIAL HANDLING LDY CHARCNT STA LBUFF,Y ; ELSE ADD CHAR TO INPUT BUFFER INC CHARCNT ; NEXT POSITION IN LINE SHOWIT: JSR CHAR ; SEND TO SCREEN LDY CHARCNT CPY #77 ; ALL THE CHARS ALLOWED? BCC INLOOP ; NO, GET ANOTHER CHAR ; HANDLE LINE OVERFLOW NOMORE: JSR GETKEY CMP #EOL ; IF EOL, BEQ ENDLIN ; WRAP UP THE LINE CMP #BACKSP ; BACKSPACE BEQ BACKUP ; IS OKAY TOO JSR BEEP ; ELSE COMPLAIN JMP NOMORE ; AND INSIST ; HANDLE BACKSPACE BACKUP: DEC CHARCNT ; BACK UP THE POINTER BPL SHOWIT ; JMP LDY #0 ; RESET POINTER STY CHARCNT CBAD: JSR BEEP ; ELSE SCREAM WITH PAIN JMP INLOOP ; AND WAIT FOR SOMETHING BETTER ; HANDLE END OF LINE ENDLIN: LDY CHARCNT STA LBUFF,Y ; SHIP EOL TO BUFFER INY ; UPDATE INDEX STY LINLEN ; SAVE HERE FOR "READ" STY PRLEN ; AND HERE FOR "PPRINT" LDX #0 STX SPENA ; DISABLE CURSOR DMA JSR CHAR ; AND SEND EOL TO SCREEN ; MOVE [LBUFF] TO [ARG1] W/LC CONVERSION LEX0: LDA LBUFF-1,Y ; GET A CHAR FROM [LBUFF] CMP #'A' ; IF CHAR IS ALPHA, BCC LEX2 ; CONVERT TO LOWER CASE CMP #'Z'+1 BCS LEX2 ADC #$20 LEX2: STA (RDTBL1),Y ; MOVE CHAR TO INPUT BUFFER AT [ARG1] DEY ; LOOP TILL BNE LEX0 ; ALL CHARS MOVED JSR PPRINT ; SCRIPT [LBUFF] IF ENABLED LDA LINLEN ; RESTORE # CHARS RTS ; INTO [A] LEXBAD: LDA #0 ; TIME OUT OCCURRED (EZIP) RTS ; ZERO CHARS OBTAINED CHARCNT: DB 0 ; ----------------------- ; DIRECT PRINT LINE [X/A] ; ----------------------- ; ENTRY: STRING ADDRESS IN [X/A] (LSB/MSB) ; STRING LENGTH IN [Y] DLINE: STX STRING+LO ; DROP STRING ADDRESS STA STRING+HI ; INTO DUMMY BYTES LDX #0 ; INIT CHAR-FETCH INDEX DOUT: DB $BD ; 6502 "LDA nnnn,X" OPCODE STRING: DW $0000 ; DUMMY OPERAND BYTES JSR CHAR INX DEY ; LOOP TILL BNE DOUT ; OUT OF CHARS RTS ; ----------------------- ; SEND [LBUFF] TO PRINTER ; ----------------------- ; ENTRY: LENTH OF LINE IN [PRLEN] ; NOW WITH IMPROVED ERROR PROTECTION! (BM 11/24/84) SFLAG: DB 0 ; FOR RESTART PPRINT: LDA SCRIPT ; SCRIPTING INTERNALLY ENABLED? BEQ PEX ; NO, SCRAM IMMEDIATELY LDA ZBEGIN+ZSCRIP+1 ; CHECK SCRIPT FLAG AND #%00000001 ; SCRIPTING ON? BEQ PP3 ; NO, CHECK FOR "UNSCRIPT" LDA PSTAT ; CHECK PRINTER STATUS BMI PEX ; CAN'T OPEN IF NEGATIVE BNE PP1 ; ALREADY OPEN, SCRIPT THE LINE ; OPEN THE PRINTER FOR OUTPUT LDA FAST ; FAST-READ? (LC-A) BEQ PP0 ; NO (LC-A) JSR FOFF ; ELSE DISENGAGE (LC-A) LDA #8 ; & RESET DRIVE (LC-A) JSR DOPEN ; (LC-A) PP0: LDX #0 ; (LC-A) STX FAST ; BYE FAST DRIVE! (LC-A) LDX #1 STX PSTAT ; SET STATUS TO "PRINTER OPENED" (1) LDA #4 ; LOGICAL FILE #4 TAX ; DEVICE #4 LDY #7 ; ALLOW UPPER/LOWER CASE JSR SETLFS ; SET UP LOGICAL FILE LDA #0 JSR SETNAM ; NO FILENAME REQUIRED JSR OPEN ; OPEN THE CHANNEL BCC PP1 ; OPEN OKAY IF CARRY CLEAR PPERR: LDA #$FF ; ELSE SET PRINTER STATUS STA PSTAT ; TO "CAN'T OPEN" BNE PP5 ; AND SIMULATE AN "UNSCRIPT" PP1: LDX #4 ; SET PRINTER CHANNEL JSR CHKOUT ; TO "OUTPUT" LDY #0 ; INIT INDEX PP2: LDA LBUFF,Y CMP #$0D BEQ PP6 AND #%01111111 CMP #$20 BCC PP4 ; NO CONTROLS PP6: JSR LETTER BCS PPERR ; ERROR IF CARRY SET PP4: INY DEC PRLEN BNE PP2 BEQ PEX ; RESET & RETURN ; CHECK FOR "UNSCRIPT" PP3: LDA PSTAT ; CHECK PRINTER STATUS BEQ PEX ; EXIT IF PRINTER WAS OFF BMI PEX ; OR UNOPENABLE PCLOSE: LDA #0 ; RESET PRINTER STATUS FLAG STA PSTAT ; TO "CLOSED" ; ENTRY FOR PRINTER ERROR PP5: LDA #4 JSR CLOSE ; CLOSE THE PRINTER CHANNEL LDA FAST ; (LC-A) BEQ PEX ; (LC-A) LDA #8 ; (LC-A) JSR DOPEN ; (LC-A) JMP FINIT ; (LC-A) PEX: JMP CLRCHN ; ------------ ; SPLIT SCREEN ; ------------ ; SPLIT SCREEN AT LINE [ARG1] ; DISABLE SPLIT IF [ARG1] = 0 ; IGNORE IF SPLIT ALREADY ENABLED OR [ARG1] >= 23 ; ON C-128, I AM USING KERNAL RTNS TO ACCESS SCREEN MEMORY ; AS SCREEN MEMORY IS IN ITS OWN WORLD. IN ORDER NOT TO ; SCROLL THE TOP WINDOW WHEN SCREEN IS SPLIT, THE C-128 ; TOP OF SCREEN INDICATOR MUST BE USED, BUT: NO ACCESS IS ; ALLOWED ABOVE THAT INDICATOR. THAT INDICATOR (WTOP) WILL ; THEREFORE BE SET ONLY WHEN SCROLLING MIGHT OCCUR, LEAVING ; [WTOP] SET TO #0 OTHERWISE. THE SPLIT LINE IS KEPT TRACK ; OF IN [TOP]. ZSPLIT: LDA ZBEGIN+ZMODE AND #%00100000 ;CHECK IF ENABLED (EZIP) BEQ ZSPOUT ;NOT, LEAVE LDX ARG1+LO ;GET # OF LINES FOR SCREEN BEQ NORL ;IF 0 THEN RESTORE SCREEN CPX #24 ;IS SPLIT REALLY = WHOLE SCREEN BCS ZSPOUT ;YES, IGNORE STX SLINE ; SET THE TOP OF THE SECOND SCREEN STX SPSTAT ; NON ZERO = SCREEN IS SPLIT CPX LINCNT ; IS SCROLLING SCREEN NOW LESS THAN LINCNT? BCC ZSPL3 ; NO STX LINCNT ; YES ZSPL3: LDA #YSIZE-1 ; CALCULATE # LINES TO SCROLL SEC ; BEFORE "MORE" APPEARS: SBC SLINE ; LMAX = YSIZE-SLINE-1 STA LMAX DEC LMAX ZSPOUT: RTS ; -------------------- ; DISABLE SPLIT SCREEN ; -------------------- NORL: JSR TOBOT SPLOFF: LDX #0 STX SLINE ; SPLIT AT LINE 1 STX SPSTAT ; TURN OFF STATUS FLAG STX LINCNT ; RESET LINE COUNT LDA #24 STA LMAX ; SET MAXIMUM LINE SCROLL RTS ; ------ ; SCREEN ; ------ ; GO TO TOP WINDOW (TOP OF SCREEN) IF [A] = 1 ; GO TO BOTTOM OF SCREEN IF [A] = 0 ; IGNORE IF SPLIT NOT ENABLED OR [A] <> 0 OR 1 ; FLAG SPLITF WILL BE SET FOR OUTPUT TO DETERMINE ; IF AND WHICH WINDOW TO DISPLAY TO ; (0=BOTTOM 1=TOP) ZSCRN: LDA ZBEGIN+ZMODE AND #%00000001 ; CHECK IF ENABLED (EZIP) BEQ ZSPOUT ; NOT, LEAVE LDA SPSTAT ; CHECK IF SCREEN IS SPLIT BEQ ZSPOUT ; NO, SO JUST LEAVE LDA ARG1+LO ; CHECK WHICH WINDOW BNE SCRN1 ; TOP SCREEN LDA #$FF ; SCROLLING SCREEN SO STA SCRIPT ; ALLOW SCRIPTING LDA #0 STA SPLITF ; SET FLAG TO SPLIT SCREEN (0) TOBOT: JSR ZSPL3 LDX #23 ; BOTTOM OF SCREEN JMP DOSCRN ; JMP TO RTN SCRN1: CMP #01 BNE ZSPOUT ; INVALID SCREEN ID STA SPLITF ; SET FLAG TO UNSCROLLING SCREEN (1) LDA #0 STA SCRIPT ; SET SCRIPTNG OFF, NOT ALLOWED THIS SCREEN TOTOP: LDX #24 STX LMAX LDX #0 ; TOP, NON SCROLLING SCREEN DOSCRN: LDY #0 STY LINCNT CLC JSR PLOT ; SET CURSOR (+ LEAVE) JMP NEWLOG ; ----- ; SOUND ; ----- ; ARG1 = BOOP (2) BEEP (1) ALL OTHERS INVALID ; (EZIP) ZSOUND: LDA ZBEGIN+ZMODE AND #%00100000 BEQ ZSOEX ; NOT ENABLED LDX ARG1+LO ; GET SOUND WANTED DEX BNE ZSO1 JMP BEEP ZSO1: DEX BEQ BOOP ZSOEX: RTS ; INVALID BOOP: LDA #96 ; FREQ LSB STA FRELO1 LDA #22 ; MSB STA FREHI1 LDA #$F2 STA TIME JMP SOUND BEEP: LDA #60 ; FREQ LSB STA FRELO1 LDA #50 ; MSB STA FREHI1 LDA #$FC STA TIME SOUND: LDA #%11110000 STA SUREL1 ; FULL SUSTAIN LDA #%10001111 STA SIGVOL ; FULL VOLUME LDA #%01000001 STA VCREG1 ; START PULSE RAZZ: LDA TIME BNE RAZZ STA VCREG1 ; STOP PULSE LDA #%10000000 STA SIGVOL ; VOLUME OFF RTS ; -------------------------------- ; RETURN RANDOM BYTES IN [A] & [X] ; -------------------------------- RANDOM: INC RAND ; RANDOM FROM APPLE (11/4/86 LD) DEC RASTER LDA RAND ADC RNUM1 TAX LDA RASTER SBC RNUM2 STA RNUM1 STX RNUM2 RTS ; ------------------------ ; CLEAR SCREEN & COLOR RAM ; ------------------------ CLS: LDA #HIGH SCREEN ; POINT [I] TO STA I+HI ; SCREEN RAM LDA #HIGH COLRAM ; POINT [J] TO STA J+HI ; COLOR RAM LDY #0 ; RESET PAGE INDEX STY I+LO ; BOTH RAMS START STY J+LO ; ON PAGE BOUNDARIES STY SPENA ; MAKE SURE CURSOR IS DEAD! LDX #4 ; CLEAR 4 PAGES CLS0: LDA #SPACE ; SPACE CHAR STA (I),Y ; FOR SCREEN LDA #1 ; "WHITE" CODE STA (J),Y ; FOR COLOR RAM INY BNE CLS0 INC I+HI ; POINT TO INC J+HI ; NEXT PAGE DEX ; 4 PAGES DONE? BNE CLS0 ; LOOP TILL EMPTY LDA #13 ; RESET THE STA SPT0 ; SPRITE RAM POINTER JSR TOTOP ; SET CURSOR TO (0,1) JSR SPLOFF ; DISABLE SPLIT SCREEN SEI LDA #LOW DORTI ; POINT THE SYSTEM NMI VECTOR STA NMINV+LO ; TO A SIMPLE "RTI" INSTRUCTION LDA #HIGH DORTI ; TO DISABLE THE STOP/RESTORE EXIT STA NMINV+HI CLI ; FALL THROUGH ... ; ---------------------------- ; RESET THE LOGICAL LINE TABLE ; ---------------------------- NEWLOG: LDX #24 NLG0: LDA HILINE,X ; GET MSB OF LINE ADDRESS ORA #%10000000 ; SET THE HIGH BIT STA LDTB1,X ; STORE IN THE LINE TABLE DEX BPL NLG0 RTS DORTI: RTI ; RTI INSTRUCTION FOR NMI ; ------------------- ; LINE ADDRESS TABLES ; ------------------- LOLINE: DB $00,$28,$50,$78,$A0,$C8,$F0,$18 DB $40,$68,$90,$B8,$E0,$08,$30,$58 DB $80,$A8,$D0,$F8,$20,$48,$70,$98 DB $C0 HILINE: DB $04,$04,$04,$04,$04,$04,$04,$05 DB $05,$05,$05,$05,$05,$06,$06,$06 DB $06,$06,$06,$06,$07,$07,$07,$07 DB $07 END