; ZXZVM: Z-Code interpreter for the Z80 processor ; Copyright (C) 1998-9,2006,2016 John Elliott ; ; This program is free software; you can redistribute it and/or modify ; it under the terms of the GNU General Public License as published by ; the Free Software Foundation; either version 2 of the License, or ; (at your option) any later version. ; ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ; GNU General Public License for more details. ; ; You should have received a copy of the GNU General Public License ; along with this program; if not, write to the Free Software ; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. SCRFONT equ 05000h ;Where we keep a copy of the default screen font PCWFONT equ 0B800h ;Where the PCW keeps its current font inited: defb 0 ;Screen initialised? scrinit: ld a,(inited) or a scf ret nz ld a,1 ld (inited),a ld c,6Dh ;Get the console mode (to disable ctrl-C) ld de,0FFFFh call FDOS ld (conmode),hl ld d,h ld a,l and 0F1h ;Reset bits 1,2,3 of the word ld e,a ld c,6Dh call FDOS ld de,initstr ld c,9 call FDOS xor a call ZXSTYL call font_init call disable_M ;;; call inthook ;<< v0.04 >> Disabled, it causes problems xor a ld (crtplus),a call iscrt ;Returns Carry set if CRT+ is loaded ccf ret c ld a,1 ld (crtplus),a scf ret ; deinit: call pusha ld a,(inited) or a jp z,popa xor a ld (inited),a ld de,(conmode) ld c,6Dh call FDOS xor a call ZXSTYL ld de,unitstr ld c,9 call FDOS call font_deinit call cleanram ;Empty M: call enable_M ;;; call intoff ;<< v0.04 >> disabled, it causes problems ld c,0Dh call FDOS jp popa ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ;Set colours. The PCW doesn't have any colours - unless you're running ZXZVM ;under the JOYCE emulator, and why would you be doing that when there are ;native interpreters for anything JOYCE runs on? ; ;Well, OK, so I'm using JOYCE as the test environment, but that's different, ;honest. ; scol1: scf ret ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; crtplus: defb 0 ;Set to 1 if CRT+ is present. CRT+ allows true bold ;and italic text. ; conmode: defw 0 ;The console mode ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; cls1: ld de,clsstr call printstr scf ret ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ;Escape sequences: ; initstr: defb esc,'0',esc,'y',esc,'f',esc,'w',esc,'E',esc,'H$' unitstr: defb esc,'1',esc,'y',esc,'e',esc,'v',esc,'E',esc,'H$' clsstr: defb esc,'y',esc,'w',esc,'H' clwinstr: defb esc,'E$' winustr: defb esc,'X ' ;Select upper window winuh: defb ' y',0FFh winlstr: defb esc,'X' ;Select lower window winlt: defb ' ' winlh: defb ' y',0FFh curpostr: defb esc,'Y' ;Set cursor position curpos: defb ' ',0FFh alloff1: defb esc,'q',esc,'-b',esc,'-i$' alloff2: defb esc,'q',esc,'u$' revonstr: defb esc,'p$' ;Reverse video on italon1: defb esc,'+i$' ;CRT+ italic on italon2: defb esc,'r$' ;Underline on bldon1: defb esc,'+b$' ;CRT+ bold on bldon2: defb '$' ;No normal bold on curoff: defb esc,'f$' ;Cursor off curon: defb esc,'e$' ;Cursor on morestr1: defb cr,lf,esc,'+g[More]',esc,'-g',8,8,8,8,8,8,'$' morestr: defb cr,lf,'[More]',8,8,8,8,8,8,'$' morecstr: defb ' ',8,8,8,8,8,8,'$' eralstr: defb esc,'K$' ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ;Font bitmaps ; ;Accents not present in the standard font ; thorns: include joyfonta.zsm thornend: ; ;The "picture drawing" characters ; font3: include joyfont3.zsm font3e: ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ;Font selection code ; ;This code uses direct access to the memory banking hardware. This is NOT ;the preferred technique for memory access (USERF should be used instead, ;because it's portable to other computers such as the CPC and Spectrum+3) ;but since I'm tying myself to the PCW hardware in other ways, I might as ;well have the benefits of direct memory manipulation here. ; zfont: defb 1 ;Start in font 1 ; font_init: di ld a,82h ;Memory bank containing the font out (0F2h),a ;Bring the font in at 0B800h ld hl,PCWFONT ld de,SCRFONT ld bc,800h ldir ;Take a copy of the screen font ld hl,thorns ;Put in our "Thorn" characters as numbers ld de,PCWFONT+400h ;80h-83h ld bc,thornend-thorns ldir ld a,86h ;TPA bank out (0F2h),a ei ret ; font_deinit: di ld a,82h ;Memory bank containing the font out (0F2h),a ;Bring the font in at 0B800h ld de,PCWFONT ld hl,SCRFONT ld bc,800h ldir ;Restore the original screen font ld a,86h ;TPA bank out (0F2h),a ei ret ; sfont4: ld (zfont),a push hl ;L = old font ;Select normal Z-font 1 (or 4). di ;Copy in characters 0-127. ld a,82h ;Memory bank containing the font out (0F2h),a ;Bring the font in at 0B800h ld de,PCWFONT ld hl,SCRFONT ld bc,400h ldir ;Restore the original screen font ld a,86h ;TPA bank out (0F2h),a ei pop hl ld a,l scf ret sfont3: ld (zfont),a push hl ;Select Z-font 3. di ;Copy in characters 0-127. ld a,82h ;Memory bank containing the font out (0F2h),a ;Bring the font in at 0B800h ld de,PCWFONT ld hl,font3 ld bc,font3e-font3 ldir ;Restore the original screen font ld a,86h ;TPA bank out (0F2h),a ei pop hl ld a,l ;Old font scf ret ; sfont1: ld hl,(zfont) ;L = old font cp 3 jr z,sfont3 ;Graphics font cp 1 jr z,sfont4 ;Normal font cp 4 jr z,sfont4 ;Fixed font xor a scf ret ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; eraw1: cp 0FFh ;Erase screen? jr z,erall cp 0FEh jp z,erascr or a jr z,eratop cp 1 jr z,erawin erawi: ld hl,badera xor a ret ; eratop: ld a,0FFh defb 1Eh ;LD E, - swallow the XOR A erascr: xor a ;Clear all the screen erawin: call setwin ld de,clwinstr call printstr ld a,(cwin) call setwin call movcur scf ret ; erall: call ZXCLS ;Clear screen and reset everything ld a,(scrh) dec a dec a ld (scrls),a xor a ld (lwtop),a ld (lwx),a ld (lwy),a ld (uwx),a ld (uwy),a ld a,1 ld (cwin),a ld a,(zver) cp 5 jr nc,erall1 ld a,(scrh) dec a ld (lwy),a call movcur erall1: scf ret ; movcur: push af ;Move the cursor to UWX or LWX as appropriate push hl push de ld hl,(uwx) ld a,(cwin) or a jr z,movcr1 ld hl,(lwx) movcr1: ld a,h ld h,l ld l,a ld de,2020h add hl,de ld (curpos),hl ld de,curpostr call printff pop de pop hl pop af ret ; setwin: push af ;Set window in A: 0 = screen 1 = bottom -1 = top cp 0ffh jr z,setuw or a jr z,setscr ld a,(lwtop) ld b,a add a,32 ld (winlt),a ld a,(scrh) and a sub b add a,31 ld (winlh),a push de ld de,winlstr call printff pop de pop af ret ; setuw: ld a,(lwtop) jr setu1 ; setscr: ld a,(scrh) setu1: add a,31 ld (winuh),a push de ld de,winustr call printff pop de pop af ret ; printff: call pusha ; Print 0FFh-terminated string. Used if ex de,hl ; the text might contain a '$' character prff1: ld a,(hl) inc hl cp 0ffh jp z,popa push hl ld e,a ld c,2 call FDOS pop hl jr prff1 ; printstr: call pusha ld c,9 call FDOS jp popa ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ;Get X position in L, chars left in H, total screen width in A ; getx1: ld hl,(uwx) ld a,(cwin) or a jr z,getx2 ld hl,(lwx) getx2: ld a,90 sub l ld h,a ld a,90 scf ret ; ;Get Y position in L ; gety1: ld hl,(uwx) ld a,(cwin) or a jr z,gety2 ld hl,(lwx) gety2: ld l,h ld h,0 scf ret ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ;Work out whether a word will fit on the line. Since the PCW uses a ;fixed-pitch font, we don't need to bother with what the text ;actually says, only with its length. B=no. of letters and C=no. of ;spaces. Return 0 to print everything, 1 to print letters only, 2 ;to print carriage return and then everything. ; bfit1: call ZXGETX ;H = amount of space on the line ld d,0 ;D = value to return. ld a,c add a,b ;A = total chars in line cp h ld a,d ret c ;The whole lot will fit. scf ret z ;The whole lot will fit but cause a wrap. ; ;See if the word will fit. ; inc d ld a,b cp h ld a,d ret c ;Letters only will fit scf ret z ;The letters will fit but cause a wrap. inc a ;Nothing will fit, return 2. scf ret ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; wrapped: defb 0 scrls: defb 31 ;Number of scrolls to [MORE] prompt lwtop: defb 0 ;Top line of lower window lwx: defb 0 ;Lower window X,Y lwy: defb 0 uwx: defb 0 ;Upper window X,Y uwy: defb 0 cwin: defb 1 ;1 = lower ; badera: defb 'Invalid call to erase_windo',0F7h ;'w'+80h ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; zchr1: cp 1 jr z,out1 cp 2 jr z,ts_char ;Output to transcript scf ret ; ts_char: ld a,(printing) ;<< v1.00 rewritten or a jr z,tsend ;Don't if not printing ld a,(cwin) or a jr z,tsend ;Don't print status line text ld a,l cp 0Dh jr nz,ts_ch1 ld e,0Dh ;Append LF to CR ld c,5 call FDOS ld l,0Ah ts_ch1: ld e,l ;Print it ld c,5 call FDOS tsend: scf ;>> v1.00 ret ; out1: ld a,l ;Output to stream 1 (screen) cp 0dh jr nz,zchr2 ; ; << v1.11 Swallow a LF immediately after an automatic wrap. This should allow ; us to print right up to the edge of a page without vertical gaps appearing. ; ld a,(wrapped) or a jr z,out1a xor a ld (wrapped),a scf ret ; ; >> v1.11 ; out1a: ld a,(cwin) or a jr z,ulf jr llf zchr2: xor a ld (wrapped),a ld a,(cwin) call xlchar ld e,l call anych1 ld a,(cwin) or a jr z,stepcu ld a,(lwx) inc a ld (lwx),a cp 90 ret c ld a,1 ld (wrapped),a llf: xor a ld (lwx),a ld a,(lwy) inc a ld (lwy),a push af ld a,(scrls) or a call nz,crlf call z,more dec a ld (scrls),a ld a,(scrh) ld b,a pop af cp b ret c ld a,(scrh) dec a ld (lwy),a scf ret ; stepcu: ld a,(uwx) inc a ld (uwx),a cp 90 ret c ld a,1 ld (wrapped),a ulf: xor a ld (uwx),a ld a,(uwy) inc a ld (uwy),a call movcur scf ret ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ;Split_window... ; swnd1: push af ld a,(lwtop) ld h,a ;H = old LWTOP ld a,(scrh) ld l,a ;L = screen height pop af ld (lwtop),a ld b,a call res_more ld a,(lwy) ;Ensure lower window y >= lwtop add a,h ;A = current Y coordinate cp b jr c,swnd1z sub b ;Make it relative to new window top jr swnd1a ; swnd1z: xor a swnd1a: ld (lwy),a ld a,(uwy) cp b ret c ;Ensure upper window y < lwtop ld hl,0 ld (uwx),hl ld a,(cwin) call setwin call movcur scf ret ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ;Set_window... ; swnd2: and 1 xor 1 ;set_window opcode uses 0 to mean lower window ld (cwin),a call setwin call movcur scf ret nz ld hl,0 ld (uwx),hl scf ret ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ;Set_text_style ; styl1: and 7 call z,alloff bit 0,a call nz,revon bit 1,a call nz,bldon bit 2,a call nz,italon scf ret ; alloff: ld de,alloff1 ld hl,alloff2 jr crtdef ; revon: ld de,revonstr call printstr scf ret ; bldon: ld de,bldon1 ld hl,bldon2 jr crtdef ; italon: ld de,italon1 ld hl,italon2 crtdef: push af ld a,(crtplus) or a call nz,printstr ex de,hl ;<> If CRT+ is not present, print call z,printstr ex de,hl ;<> the alternative string pop af scf ret ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ;set_cursor_position ; scur1: xor a ld (wrapped),a ld a,(scrh) ld e,a bit 7,c ;Negative => set cursor on/off jr nz,cursw dec b dec c ;0-based ld a,c cp 90 jr c,scur1a ld c,89 scur1a: ld a,(cwin) or a jr z,scur2 ld a,b cp e jr c,scur1b ld a,e dec a scur1b: ld b,a ld a,e ;Reset the scroll counter dec a dec a sub b ld (scrls),a ld (lwx),bc call movcur scf ret ; scur2: ld a,b ; << v0.02 Don't bother to check if cp e ; this takes the cursor outside jr c,scur2b ; the upper window. ld b,e dec b scur2b: ld (uwx),bc ; >> v0.02 call movcur scf ret ; cursw: cp 0ffh ld de,curoff call z,printstr ld de,curon call nz,printstr scf ret ; curson: push de ld de,curon call printstr pop de ret ; cursoff: push de ld de,curoff call printstr pop de ret ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; snd1: ld e,7 ;Beep! ld c,2 call FDOS scf ret ; bleep: call pusha call snd1 jp popa ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; xlchar: ld a,l cp 128 ret c sub 128 push hl push bc ld l,a ld h,0 ld bc,xtab add hl,bc ld a,(hl) pop bc pop hl ld l,a ret ;Translate character in L ; xtab: defb 080h,081h,082h,083h,084h,085h,086h,087h defb 088h,089h,08Ah,08Bh,08Ch,08Dh,08Eh,08Fh defb 090h,091h,092h,093h,094h,095h,096h,097h defb 098h,099h,09Ah,0F0h,0F3h,0F4h,0D0h,0D3h defb 0D4h,0BAh,0ACh,0ABh,0F1h,0F2h,0EFh,0D1h defb 0D2h,0E0h,0E1h,0E2h,0E3h,0E4h,84h, 0C0h defb 0C1h,0C2h,0C3h,0C4h,85h, 0EAh,0EBh,0ECh defb 0EDh,0EEh,0CAh,0CBh,0CCh,0CDh,0CEh,0E5h defb 0E6h,0E7h,0E8h,0E9h,0C5h,0C6h,0C7h,0C8h defb 0C9h,0F7h,0D7h,0F8h,0D8h,0FAh,0F9h,0FBh defb 0DAh,0D9h,0DBh,0F6h,0D6h,0F5h,0D5h,080h defb 082h,081h,083h,0A3h,087h,086h,0AFh,0AEh defb 0E0h,0E1h,0E2h,0E3h,0E4h,0E5h,0E6h,0E7h defb 0E8h,0E9h,0EAh,0EBh,0ECh,0EDh,0EEh,0EFh defb 0F0h,0F1h,0F2h,0F3h,0F4h,0F5h,0F6h,0F7h defb 0F8h,0F9h,0FAh,0FBh,0FCh,0FDh,0FEh,0FFh ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; res_more: push hl ld a,(scrh) dec a ld hl,lwtop sub (hl) ;A = no. of scrolls to next [MORE] dec a ld (scrls),a pop hl inc a ret ; crlf: call pusha ld de,crlfstr call printstr jp popa ; more: push hl push de push bc ld a,(crtplus) or a ld de,morestr call z,printstr ld de,morestr1 call nz,printstr ld c,6 ld e,0FDh call FDOS ld de,morecstr call printstr pop bc pop de pop hl call res_more ret ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ;Print any character as a character (not as an escape code) ; anychar: call pusha ld l,e call xlchar ld e,l call anych1 jp popa ; anych1: ld a,e cp 20h jr nc,opc1 push de ld e,1Bh ld c,2 call FDOS pop de opc1: ld c,2 jp FDOS ;Print character ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ;Erase to end of line ; eral1: call movcur ld de,eralstr ld c,9 call FDOS scf ret