; PRINT2.src ; ; SPECTRUM PRINTOUT ROUTINE ; USES WORKSPACE POINTED TO BY IY ; 17/02/1986 J.G.HARSTON ; 18/06/2012 Comments added from original paper version ; Coding style and layout is rather... um... untidy ; PART ONE ; CONTROL CHARACTERS ORG CODEBASE LOAD ; Entry points ENTRY INIT JP INIT0 ; Initialise PRINT JP PRINT0 ; Print a char PERMS JP MOVE_1 ; Set perm settings from temp TEMPS JP MOVE_2 ; Set temp settings from perm JP MOVE_3 ; Just save output position ; PRINT A CHARACTER ; ----------------- ; On entry, A=character ; On exit, all registers corrupted ; except IY ; PRINT0 CALL SA_IY CALL PR_OUT IY_RET POP IY EI RET SA_IY EX (SP),IY PUSH IY DI LD IY,WORKIY RET ; Initialise INIT0 CALL SA_IY CALL INITMODE JR IY_RET ; Copy current window to current definition MOVE_1 CALL SA_IY CALL PERMS1 JR IY_RET ; Copy current window back to definition MOVE_2 CALL SA_IY CALL TEMPS1 JR IY_RET ; Copy current cursor positions back to definition MOVE_3 CALL SA_IY CALL SA_POSN JR IY_RET PERMS1 LD A,(IY-WIND_P) ; Get current window PERMS2 AND 7 LD (IY-WIND_T),A ; Set as current window CALL W_ADD1 ; Find window definition LD BC,#10 ; Copy to current definition LDIR RET ; Find window definition W_ADDR LD A,(IY-WIND_T) W_ADD1 ADD A,A ADD A,A ADD A,A ADD A,A OR WBASE & 255 LD L,A LD H,WBASE / 256 ; HL=>window definition LD DE,CURRENT ; DE=current window RET ; Just copy text position to definition SA_POSN LD BC,#04 JR TEMPS2 ; Copy current window back to definitions TEMPS1 LD BC,#10 TEMPS2 CALL W_ADDR EX DE,HL LDIR RET ; Print a character, needs IY set PR_OUT BIT 6,(IY-FLAGS) ; b6=VDU on/off JR Z,PR_GO ; VDU enabled, print character CP 2 RET NZ ; Not VDU ENABLE, exit RES 6,(IY-FLAGS) ; Enable VDU RET PR_GO BIT 7,(IY-FLAGS) JP NZ,PO_OP ; b7=1, waiting for parameters CALL PO_FETCH ; Get DFCC and POSN ; CP 127 ; JP Z,PO_DEL CP 32 JP NC,PO_ABLE ; 32-127 - printable chars CP 16 JP NC,OPERS ; 16-31 - ctrls with parameters LD HL,T_CTRLS ; Index into control chars LD D,0 LD E,A ADD HL,DE LD E,(HL) ; Fetch address ADD HL,DE PUSH HL JP PO_FETCH ; Jump by fetching DFCC and POSN T_CTRLS DEFB NUL-$ DEFB NUL-$ DEFB EN_VDU-$ ; Enable VDU DEFB DS_VDU-$ ; Disable VDU DEFB HOME-$ ; Home cursor to (0,0) DEFB COL_W-$ ; Collapse windows DEFB COMMA-$ ; Print comma to next field DEFB BELL-$ ; Bell DEFB LEFT-$ ; Left DEFB RIGHT-$ ; Right DEFB DOWN-$ ; Down DEFB UP-$ ; Up DEFB CLS-$ ; CLS DEFB ENTER-$ ; Newline DEFB SH_OUT-$ ; Select Shift Out chars DEFB SH_IN-$ ; Select Shift In chars DS_VDU SET 6,(IY-FLAGS) ; Disable VDU EN_VDU NUL RET HOME LD BC,0 LD (COORDS),BC ; Plot point to (0,0) RES 7,(IY-PFLAG) ; Cancel pending scroll JP CL_S_1 ; Jump to set POSN to (0,0) LEFT DEC C ; Dec. X JR NZ,L_END ; Not zero, set ok LD C,(IY-WIND_W) ; X=width UP CALL CHSCRL ; Check for pending scroll DEC B ; Dec. Y JR NZ,L_END ; Not zero, set ok INC B ; Inc. Y back onto screen L_END JP CL_SET RIGHT LD A,C ; Get X CP (IY-WIND_W) JR NC,ENTER ; At max width, need to go down INC C ; Inc. X JR L_END ENTER LD C,1 ; X=0 (1=zero) DOWN CALL CHSCRL ; Check for pending scroll LD A,B ; Get Y CP (IY-WIND_H) JR NC,DSCRL ; At max height, need to scroll INC B ; Inc Y JR L_END DSCRL LD A,C ; Check X DEC A JR NZ,DSCRL2 ; Not in column 0, scroll SET 7,(IY-PFLAG) ; Set pending scroll JR L_END DSCRL2 CALL CHSCR2 JR L_END CHSCRL BIT 7,(IY-PFLAG) RET Z ; No pending scroll CHSCR2 PUSH AF PUSH BC RES 7,(IY-PFLAG) ; Cancel pending scroll CALL SCRL_D ; Scroll window one line POP BC POP AF RET CLS JP CL_SCR ; Clear window BELL LD HL,BELLP LD A,#07 JP OSWORD SH_OUT LD HL,(S_CHAR) ; Change character set JR SH_IN1 SH_IN LD HL,(R_CHAR) ; Change character set SH_IN1 LD (CHARS),HL RET COL_W LD A,(IY-MODE) ; Get current mode JP MODETT ; Reselect it, collapse windows COMMA LD A,(IY-COMDIST) ; Distance moved by comma LD D,A COMM1 CP C ; Current X posn? JR Z,COM_2 ; Yes, jump JR C,COM_2 ; Less than X, jump JR PO_FILL ; Fill with spaces to comma position COM_2 ADD A,D CP (IY-WIND_W) ; Less than width of window? JR C,COMM1 ; Yes, print spaces LD A,(IY-WIND_W) ; Otherwise, use width of window JR PO_FILL ; Control code with parameters OPERS LD (QBASE),A ; Save in VDU queue LD B,1 CP 22 JR C,OPER1 ; 16-21 have 1 param CP 24 JR C,OPER2 ; 22-23 have 2 params CP 25 JR Z,OPER2 ; 25 has 2 params CP 30 JR C,OPER1 ; 24,26-29 have 1 param LD B,4 JR Z,OPER1 ; 30 has 4 params LD B,8 ; 31 has 9 params OPER2 INC B OPER1 LD (IY-QNUM),B ; Number of params waiting SET 7,(IY-FLAGS) ; b7=1 - waiting for params RET ; Control code parameter PO_OP DEC (IY-QNUM) JR Z,PONXT ; No more params LD HL,QBASE LD D,0 LD E,(IY-QNUM) ADD HL,DE ; Index into queue LD (HL),A ; Store parameter RET ; Control characters with parameters ; ---------------------------------- ; 16,n - PAPER ; 17,n - INK ; 18,n - FLASH ; 19,n - BRIGHT ; 20,n - INVERSE ; 21,n - OVER ; 22,y,x - AT ; 23,l,h - TAB ; 24,n - BORDER ; 25,x,y - PLOT ; 26,n - MODE ; 27,n - Escape ; 28,n - ignored ; 29,n - WINDOW ; 30,a,b,c,d - DEFWINDOW ; 31,n,a,b,c,d,e,f,g,h - DEFCHR$ PONXT RES 7,(IY-FLAGS) ; Turn queue flag off LD HL,(QBASE) ; Get control char LD D,A ; D=last param, H=2nd last param LD A,L ; A=control char CP 22 JP C,COLOURS ; 16-21 are colours JR Z,PO_AT ; 22 is AT CP 24 JR C,PO_TAB ; 23 is TAB JP Z,BORDER ; 24 is BORDER CP 26 JP C,PO_PLOT ; 25 is PLOT JR Z,POMODE ; 26 is MODE CP 28 JP C,PO_ESC ; 27 is ESCAPE RET Z ; 28 ignored CP 30 JP C,WINDOW ; 29 is WINDOW JP Z,DEFWIND ; 30 is DEFWINDOW JP DEFCHAR ; 31 is DEFCHR$ PO_TAB LD A,H ; Column to TAB to PO_FILL CALL PO_FETCH SUB C ; Number of spaces needed AND 63 RET Z ; No spaces needed LD D,A ; D=number of spaces to print PO_SPC LD A,32 PUSH DE CALL PR_OUT ; Print a space POP DE DEC D JR NZ,PO_SPC RET PO_AT LD A,D ; A=X BIT 7,H JR NZ,PO_AT1 CP (IY-WIND_W) RET NC ; Past edge of window LD C,A ; Ok, so C=X LD A,H ; A=Y CP (IY-WIND_H) RET NC ; Past bottom of window LD B,A ; Ok, so B=Y RES 7,(IY-PFLAG) ; Cancel any pending scroll JP CL_S_1 PO_AT1 RES 3,(IY-PFLAG) ADD A,A RET NC SET 3,(IY-PFLAG) RET ; Select a screen mode within a window ; ------------------------------------ ; thin double double tele ; mode x y chars width height text flags ; 0 64 12 Y Y A0 ; 1 64 24 Y 81 ; 2 32 12 Y 22 ; 3 32 24 03 ; 4 16 12 Y Y 64 ; 5 16 24 Y 45 ; 6 39 12 Y Y 36 ; 7 40 25 Y 17 ; ; MODE flags ; b7 - Thin characters ; b6 - Double Width ; b5 - Double Height ; b4 - Soft mode/Teletext mode ; b3-b0 - MODE number ; FLAGS: ; b7=VDU parameters waiting ; b6=VDU disabled ; b5= ; b4= ; b3= ; b2=Use VDUVEC ; b1=b0 of Thin X position ; b0=b0 of Thin character POMODE LD E,(IY-MODE) ; E=current mode LD A,D CP 8 JR C,MODE1 ; mode<8 LD A,26 ; A=MODE, D=new mode, E=current mode CALL VDUVEC MODE1 LD H,MTAB / 256 ADD A,MTAB & 255 LD L,A LD A,(HL) ; Get MODE flags LD (IY-MODE),A BIT 4,(IY-MODE) JP NZ,MODETT ; Jump to set teletext mode BIT 4,E JP NZ,MODETT ; Previous was teletext LD A,E ; Get previous MODE CALL POSSET ; Reset window to base values LD A,(IY-MODE) ; Get new MODE CALL POSMODE ; Reset window to new values LD BC,(POSN) JP CL_S_1 ; Recalculate DFCC ; POSSET - convert positions to physical sizes POSSET BIT 7,A JR Z,POS_2 ; Not thin chars POSDIV SRL (IY-POSN_X) SRL (IY-WIND_X) SRL (IY-WIND_W) RET POS_2 BIT 6,A CALL NZ,POSDUB ; Double width BIT 5,A RET Z ; Not double height SLA (IY-POSN_Y) SLA (IY-WIND_Y) SLA (IY-WIND_H) RET POSDUB SLA (IY-POSN_X) SLA (IY-WIND_X) SLA (IY-WIND_W) RET ; POSMODE - convert positions to logical sizes POSMODE BIT 7,A JR NZ,POSDUB ; Thin chars BIT 6,A CALL NZ,POSDIV ; Double width BIT 5,A RET Z ; Not double height SRL (IY-POSN_Y) SRL (IY-WIND_Y) SRL (IY-WIND_H) RET INITMODE ; Connect to Spectrum channel 'P' LD HL,(23631) ; CHANS LD DE,15 ADD HL,DE ; HL=>'P' output address LD DE,PRINT0 LD (HL),E INC HL LD (HL),D ; ; Initialise workspace LD HL,WORKSPACE ; Clear workspace LD DE,WORKSPACE+1 LD BC,255 LD (HL),0 LDIR LD A,#47 ; White INK, black PAPER LD (WORKIY-DATTR),A LD A,16 ; Comma distance LD (WORKIY-COMDIST),A LD HL,CHARBASE-256 ; Point to character set LD (CHARS),HL LD (S_CHAR),HL LD (R_CHAR),HL INC H EX DE,HL LD HL,#3D00 ; Copy characters from ROM LD BC,#300 LDIR LD HL,#3D00 ; Copy initial UDCs LD DE,UDCBASE LD (UDC),DE LD BC,#300 LDIR LD HL,THINSET-128 ; Thin character set LD (TCHARS),HL LD A,3 ; Initialise to MODE 3 LD (WORKIY-MODE),A MODETT LD BC,0 LD (WORKIY-WIND_X),BC LD (POSN),BC LD (COORDS),BC LD (WORKIY-MASK),BC ; Clear MASK, PFLAG, any pending scroll LD (IY-DFADD+0),B LD (IY-DFADD+1),64 ; DFCC=16384 LD (IY-WIND_W),32 ; Width=32 LD (IY-WIND_H),24 ; Height=24 LD B,(IY-DATTR) LD (IY-ATTR),B ; Set default attrs RRA RRA RRA AND 7 LD (IY-BORDCR),A ; Set border colour OUT (254),A BIT 4,(IY-MODE) JR NZ,TMOD2 ; Set up teletext mode CALL SET_ULA ; Select soft display LD A,(IY-MODE) CALL POSMODE ; Convert to logical sizes LD A,0 LD BC,#1800 ; Clear display memory JR TMOD3 TMOD2 CALL SET_SAA ; Select teletext display LD (IY-WIND_W),40 ; Width=40 INC (IY-WIND_H) ; Height=25 LD A,32 LD BC,1023 TMOD3 LD (FILL),A ; Clear screen LD HL,16384 LD DE,16385 LD (HL),A LDIR BIT 4,(IY-MODE) JR NZ,TMOD4 LD A,(IY-ATTR) LD (HL),A LD BC,#2FF ; Clear attributes memory LDIR TMOD4 BIT 4,(IY-MODE) JR Z,TMOD6 ; Not teletext BIT 5,(IY-MODE) JR Z,TMOD6 ; Not double-height teletext DEC (IY-WIND_W) ; Width=39 SRL (IY-WIND_H) ; Height=12 LD DE,40 LD HL,16384 LD B,24 LD A,141 ; Double height char TMOD5 LD (HL),A ; Fill column 0 with CHR$141 ADD HL,DE DJNZ TMOD5 TMOD6 LD HL,CURRENT LD DE,WBASE LD BC,16 LDIR ; Copy to window 0 LD HL,WBASE LD BC,112 ; Copy to windows 1-7 LDIR RET MTAB DEFB #80,#81,#22,#03 ; MODE flags, MODE 0 same as MODE 1 DEFB #64,#45,#36,#17 PO_ESC LD HL,(ESC_V) ; Pass ESCAPE to ESCV JP JMPVEC PO_DEL CALL LEFT BIT 4,(IY-MODE) JR Z,DEL_2 ; Not teletext LD (HL),32 ; Fill with a space BIT 5,(IY-MODE) RET Z ; Not double height LD DE,40 ; Do next line down ADD HL,DE LD (HL),32 ; Another space RET DEL_2 LD A,(IY-PFLAG) ; Save PFLAG PUSH AF AND #FC ; Set OVER 0 LD (IY-PFLAG),A LD A,128 CALL PO_ANY ; Overwrite with hard space POP AF LD (IY-PFLAG),A ; Restore OVER RET COLOURS SUB 17 ; A=0 to 5, D=param ADC A,0 JR Z,CO_3 ; INK and PAPER SUB 2 ADC A,0 JR Z,CO_7 ; FLASH and BRIGHT CP 1 LD A,D LD B,3 ; OVER is b0-b1 JR NZ,CO_2 ; Do OVER RLA RLA LD B,4 ; INVERSE is b2 CO_2 LD HL,WORKIY-PFLAG JR CO_CHNG ; Change PFLAG ; PFLAG: ; b0-b1: OVER ; b2: INVERSE ; b4: INK 9 ; b6: PAPER 9 ; b7: Pending scroll ; INK and PAPER CO_3 LD A,D LD B,#07 ; Mask for INK JR C,CO_4 ; INK RLCA ; Move colour to b3-5 RLCA RLCA LD B,#38 ; Mask for PAPER CO_4 LD C,A LD A,D LD HL,WORKIY-ATTR CP 8 JR C,CO_6 AND 1 LD A,(HL) ; Get ATTR JR Z,CO_5 ; INK/PAPER 8 OR B ; INK/PAPER 9 CPL ; Complement colours AND #24 JR Z,CO_5 LD A,B CO_5 LD C,A CO_6 LD A,C CALL CO_CHNG ; Update ATTR LD A,7 ; Prepare to mask ink CP D SBC A,A CALL CO_CHNG ; Change MASK RLCA RLCA AND #50 LD B,A LD A,8 CP D SBC A,A ; Update ATTR,MASK,PFLAG with mask and colour CO_CHNG XOR (HL) AND B XOR (HL) LD (HL),A INC HL LD A,B RET ; FLASH and BRIGHT CO_7 SBC A,A LD A,D RRCA LD B,128 JR NZ,CO_8 RRCA LD B,64 CO_8 LD C,A LD HL,WORKIY-ATTR CALL CO_CHNG LD A,C RRCA RRCA RRCA JR CO_CHNG BORDER LD A,D AND 7 OUT (254),A LD (IY-BORDCR),A RET WINDOW PUSH DE ; Save new window CALL TEMPS1 ; Copy this window to definitions POP AF CALL PERMS2 ; Copy selected window to current LD (WIND_P),A ; Set as selected window RET PO_PLOT LD B,D ; BC=coords LD C,H ; C=X, B=Y LD (COORDS),BC CALL VDUVEC ; Check extension PLOT_1 CALL PL_RIGHT ; X=X+Window Left RET C ; Past right edge of window CALL PL_DOWN ; Y=Y+Window Bottom RET C ; Past top edge of window CALL PIXELADD ; Convert to pixel address LD B,A ; Bit position of pixel INC B LD A,1 ; One pixel to set PLOT_L RRCA DJNZ PLOT_L ; Rotate into position LD B,A LD A,(HL) ; Get byte from screen XOR B ; Add in plotted pixel BIT 0,(IY-PFLAG) JR NZ,PLOT_3 ; OVER 1, pixel toggled OR B ; Set plotted pixel PLOT_3 BIT 2,(IY-PFLAG) JR Z,PLOT_4 XOR B PLOT_4 LD (HL),A ; Store updated pixel CALL CL_ATTR ; Get ATTR address JP SET_ATTR ; Set attribute PL_RIGHT LD A,(IY-WIND_W) ; Window width CALL CNVPIXW ; Convert to pixels DEC A ; Maximum pixel number CP C ; Is plot point outside window? RET C ; Window width