#define DEFB .BYTE
#define DEFW .WORD
#define EQU  .EQU
#define ORG  .ORG

; ========================
; UTILITY3 v1.3
; ========================
;
; UTILITY3 a disassembler for the ZX Spectrum which
; incorporates routine DIS-Z80 by John Kerr. 
; It also uses adaptations of scrolling routines
; and keyboard-reading routines from the Spectrum ROM.
; The pocket calculator style font looks a little familiar too.
; 
; ========================

; ========================
; 
; U3 is a self-contained, screen-resident disassembler written in
; 2K of memory. It is designed to load at address $4000 and re-locate 
; itself to $5000. The main subroutine, DIS-Z80 was written by John Kerr 
; and published in SUBSET 1987.
; The program will work on any Spectrum and doesn't make OS calls.
;
;========================


ORG $5000


; Note this section is
; normally executed at $4000


        LD      HL,$4000        ; begin by relocating the entire 2K
        LD      DE,$5000        ; block of instructions from the top 
        LD      BC,$0800        ; third of the screen to the lower
        LDIR                    ; third creating a print area in the 
                                ; top two thirds - 16 lines numbered
                                ; line 0 - line 15.

; Ensure that the attributes at end of line 15 are black ink on cyan paper.

        LD      HL,$59FC

ATTLP   LD      (HL),$28
        INC     L
        JR      NZ,ATTLP
        DEC     L

; copy the colour attributes on line 15 to preceding lines 0-14.
; Note a cyan column helps highlight the ASCII text.

        LD      DE,$59DF        ; end of line 14 attributes.
        LD      BC,$01E0        ; number of bytes to fill.
        LDDR

; Now check if Interface 1 is present and initialized
; If so allow ROM 4 to be paged. (Not relevant to +3).
; This cautious approach prevents a BASIC error code being generated
; should Interface 1 not be present. 


        LD      A,(23734)       ; shadow system variable FLAGS3 
                                ; or first byte of CHANS area.
        AND     A               ; test - will be zero if shadow ROM initialized.
        JR      NZ,NOSHDW       ; skip to NOSHDW if not  

        LD      HL,MAXROM       ; enable paging of Shadow ROM
        INC     (HL)            ; increase from 4 to 5.

NOSHDW  JP      MAIN            ; jump to main entry point now in lower screen 

; --------------------------------------------------------------------
; The above code although copied to lower screen is no longer required
; If space were at a premium it could be executed on another screen line.
; --------------------------------------------------------------------

; --------------
; The next routine pages in a ROM in the range 0-4
; --------------

ROM     DI                      ; disable interrupts until end.
        LD      A,'?'           ; prompt
        LD      ($50FF),A       ; "ROM?"
        CALL    ROMTXT          ; large

ROML    CALL    KEY             ; wait for a valid keypress.

        LD      HL,MAXROM       ; holds 4 or 5
        CP      (HL)            ; compare against upper limit 4/5 
        JR      NC,ROML         ; loop back if too high.

        LD      E,A             ; select ROM
        CALL    ROMSEL          ; and continue into disassembly loop.

; Note. the call to ROMSEL puts the address of MAIN on the stack.

;========================
;
; Initial Entry Point.
;
;========================

MAIN    LD      HL,0            ; start address is initially zero 
                                ; and reset to zero after a ROM switch.

MAINLP  LD      (DISADD),HL     ; update disassembly address.

        CALL    ROMTXT          ; large print in lower screen shows
                                ; the current ROM.


; The start of the main disassembly loop.

DLOOP   LD      B,8             ; disassemble 8 lines at a time.

BLOOP   PUSH    BC              ; save line count. 

        CALL    SCROLL          ; scroll top 16 lines upwards

        LD      DE,(DISADD)     ; pick up address in DE
        PUSH    DE              ; save it

;------------------------
        CALL    DISZ80          ; disassemble one instruction.
;------------------------

        LD      (DISADD),DE     ; save new disassembly address.

        LD      B,3             ; print

GAP     CALL    CHROP           ; a three
        DJNZ    GAP             ; space gap.

        POP     HL              ; pop the old disassembly address

TEXTLP  LD      A,(HL)          ; print
        INC     HL              ; ASCII
        CALL    CHROP           ; TEXT to help distinguish 
        AND     A               ; instructions
        SBC     HL,DE           ; from data.
        JR      NC,TXTEND       ; finished when addresses match. 

        ADD     HL,DE           ; loop back until HL matches DE
        JR      TEXTLP          ; could be 1 to 4 characters

TXTEND  POP     BC              ; restore the counter
        DJNZ    BLOOP           ; and loop back for 8 lines

        CALL    KEY             ; get a keypress.

        CP      16              ; is it Enter ?
        JR      Z,DLOOP         ; back if so for another 8 lines.

        CP      17              ; is it 'R' ?
        JR      Z,ROM           ; back to ROM routine if so.

        CP      18              ; is it 'Q' ?
        JR      Z,QUIT          ; forward if so to quit

; By elimination a  numeric key (0-F) has been pressed so continue to build
; up  a hex number. The HL pair will initially hold 0 from the KEY routine. 
; First time through the loop, the value will be $00 - $0F. Any key higher,
; usually ENTER, causes the hex input to be accepted. 


HEX_LP  CP      $10             ; is key higher than hex digit ?

        JR      NC,MAINLP       ; back to main loop with ENTER, etc.
                                ; and a new disassembly address.

        ADD     HL,HL           ; rotate the
        ADD     HL,HL           ; nibble to the left
        ADD     HL,HL           ; losing the
        ADD     HL,HL           ; 5'th character.

        OR      L
        LD      L,A             ; insert new hex digit.

        EX      DE,HL           ; transfer address to DE

        CALL    CLINE           ; clear line 15
        CALL    ADRSP           ; print DE as HEX then a space
        PUSH    DE              ; save the address.

        CALL    BIGPR           ; scan text to produce large text
                                ; in lower third.
        POP     HL              ; restore address to HL.

        LD      D,$A0           ; outer adjustable delay counter.

DELAY   PUSH    HL              ; 21-cycle
        POP     HL              ; delay.
        PUSH    HL              ; and again.
        POP     HL              ;
        DJNZ    DELAY           ; decrement inner counter and loop

        DEC     D               ; decrement outer counter.

        JR      NZ,DELAY        ; back to DELAY1 while D not zero.

        CALL    KEY             ; get a keypress.

        JR      HEX_LP          ; and back to hex input loop

; ------------------
;
; Select ROM.
;
; ------------------ 

ROM3    LD      E,3             ; Amstrad named the original ROM ROM3.

ROMSEL  LD      A,($700)        ; fetch the byte at location $700.
        CP      201             ; is it the RET instruction ?
        CALL    Z,$700          ; if it is then call it.

; Note. Interface 1 is un-paged by an instruction fetch on 0x700. The above 
; code would have no ill effects if another ROM had a RET at same address.

        LD      A,E             ; fetch desired ROM $00-$04
        OR      '0'             ; add to ascii base character.
        LD      ($50FF),A       ; place after letters "ROM"

        CP      '4'             ; is Interface 1 ROM required.
        JR      NZ,PLUS3        ; forward to consider 128K ROMS

        DEC     E               ; change 4 to 3.
        CALL    PLUS3           ; ensure main Basic ROM is active.

        LD      HL,0            ; a dummy address
        PUSH    HL              ; is pushed on the stack.

        JP      $0008           ; jump to location $0008.

; Note. An instruction fetch on address 8 pages in Interface 1.
; A routine in the Interface 1 ROM will drop the zero off the stack.
; It will then return to the Main Entry Point leaving the shadow ROM paged in.
; This method which tricks Interface 1 into thinking that the CALBAS routine 
; has been used, avoids writing to RAM areas outside the screen area.
; Use this program with an Interface 1 supporting emulator to see what happens
; on the other side. Or use with the real hardware.

; The varous 128K Spectrums page in the various ROMS using OUT instructions.
; These have no effect on Spectrums that have only a single ROM.

PLUS3   LD      A,(23399)
        AND     248
        BIT     1,E
        JR      Z,SW1

        OR      4

SW1     LD      BC,$1FFD
        OUT     (C),A

        LD      A,(23388)
        BIT     0,E
        JR      NZ,SW2

        RES     4,A

SW2     LD      BC,$7FFD
        OUT     (C),A
        RET

;====================
;
; Before returning to BASIC select the main ROM, discourage re-entry
; with an all-white display and enable interrupts.
;
;====================

QUIT    CALL ROM3

        LD      HL,$57FF
        LD      DE,$5800
        LD      BC,$0300
        LDIR

        EI

LABEND  RET                     ; Finish  >>>

; ---
; Note. there is a gap of approx 16 unused bytes here.
; ---

ORG $50F8

; Three Program Variables.

DISADD  DEFW    0               ; two byte disassembly address.
PRIPOS  DEFB    0               ; low byte of print position.
MAXROM  DEFB    4               ; one more than maximum ROM allowed.



; ---

ORG $50FC

; This message terminates at a page boundary and requires no end marker.

        DEFB "ROM3"

; ====================
; DIS-Z80 was published in the SUBSET column of Personal Computer World 1987.
; The routine disassembles a single Z80 instruction at address DE. 
; It is required to be followed by a routine called CHROP that outputs a 
; single ASCII character.
; It was originally developed for CP/M on an Amstrad CPC128.
; The original ORG was $0100. I have added $5000 to all addresses.
; The stated aim was to write a Z80 disassembly routine in as short a space
; as possible and, at just over 1K (1090 bytes), it is a rather incredible 
; program. 
; The SUBSET editor David Barrow was able to trim only one byte from John 
; Kerr's compact code. I've forgotten where so there's a challenge.
; ====================

ORG $5100


DISZ80  CALL    ADRSP
        LD      BC,$0900
        LD      HL,$2020

BUFFER  PUSH    HL
        DJNZ    BUFFER
        LD      H,B
        LD      L,C
        ADD     HL,SP

        PUSH    BC
        EX      (SP),IX
        PUSH    BC
        PUSH    BC
        ADD     IX,SP

        PUSH    HL
        LD      HL,GROUP3

TRYNDX  CALL    FETCH

        LD      B,C
        CP      $ED
        JR      Z,CONFLG

        INC     B
        CP      $DD
        JR      Z,CONFLG

        INC     B
        CP      $FD
        JR      NZ,NOTNDX

CONFLG  LD      (IX+1),B
        INC     B
        DJNZ    TRYNDX

        JR      NXBYTE

NOTNDX  LD      C,A
        LD      A,(IX+1)
        OR      A
        JR      Z,NODISP

        LD      A,C
        CP      $CB
        JR      Z,GETDIS

        AND     $44
        CP      4
        JR      Z,GETDIS

        LD      A,C
        AND     $C0
        CP      $40
        JR      NZ,NODISP

GETDIS  CALL    FETCH
        LD      (IX+2),A

NODISP  LD      HL,GROUP1
        LD      A,C
        CP      $CB
        JR      NZ,NEWMSK

        LD      HL,GROUP2

NXBYTE  CALL    FETCH
        LD      C,A

NEWMSK  LD      A,(HL)
        OR      A
        JR      Z,TABEND

        AND     C
        INC     HL

NEWMOD  LD      B,(HL)
        INC     HL
        INC     B
        JR      Z,NEWMSK

TRYMAT  CP      (HL)
        INC     HL
        JR      Z,GETNDX

        BIT     7,(HL)
        INC     HL
        JR      Z,TRYMAT

        JR      NEWMOD

GETNDX  LD      A,(HL)
        AND     $7F
        DEC     B

TABEND  POP     HL
        PUSH    DE
        PUSH    HL

        EX      DE,HL
        LD      HL,MONICS
        CALL    XTRACT

        POP     HL
        LD      DE,5
        ADD     HL,DE
        POP     DE

        LD      A,B
        AND     $F0
        JR      Z,SECOND

        RRA
        RRA
        RRA
        RRA
        PUSH    BC

        LD      B,A
        LD      A,C
        CALL    OPRND1

        POP     BC
        LD      A,B
        AND     $0F
        JR      Z,OPDONE

        LD      (HL),44                 ;,
        INC     HL

SECOND  LD      A,B
        AND     $0F

        LD      B,A
        LD      A,C
        CALL    NZ,OPRND2

OPDONE  LD      A,3
        SUB     (IX)

        POP     HL
        POP     HL
        POP     IX

        JR      C,OUTEXT

        INC     A
        LD      B,A
        ADD     A,B
        ADD     A,B
        LD      B,A

SPACES  LD      A,$20
        CALL    CHROP
        DJNZ    SPACES

OUTEXT  LD      B,18

PUTOUT  DEC     SP
        POP     HL
        LD      A,H
        CALL    CHROP
        DJNZ    PUTOUT

        RET

;***********************

GROUP2  DEFB    $C0,$36,$40
        DEFB    $04,$80,$2D,$C0,$BE
        DEFB    $FF,$F8,$06,$00,$33
        DEFB    $08,$38,$10,$35,$18
        DEFB    $3A,$20,$3F,$28,$40
        DEFB    $30,$00,$38,$C1


GROUP1  DEFB    $FF,$00,$00
        DEFB    $24,$07,$32,$0F,$37
        DEFB    $17,$31,$1F,$36,$27
        DEFB    $0D,$2F,$0B,$37,$3D
        DEFB    $3F,$06,$76,$14,$C9
        DEFB    $30,$D9,$12,$F3,$0F
        DEFB    $FB,$91,$72,$C6,$02
        DEFB    $CE,$01,$DE,$BC,$02
        DEFB    $D6,$42,$E6,$03,$EE
        DEFB    $43,$F6,$25,$FE,$8C
        DEFB    $04,$08,$93,$01,$10
        DEFB    $10,$18,$9D,$AF,$22
        DEFB    $A2,$FA,$2A,$A2,$A7
        DEFB    $32,$A2,$7A,$3A,$A2
        DEFB    $03,$C3,$1C,$CD,$85
        DEFB    $97,$D3,$AA,$79,$DB
        DEFB    $9B,$5F,$E3,$93,$0E
        DEFB    $E9,$9C,$05,$EB,$93
        DEFB    $DF,$F9,$A2,$FF,$C0
        DEFB    $B6,$40,$A2,$FF,$F8
        DEFB    $76,$80,$02,$88,$01
        DEFB    $98,$BC,$06,$90,$42
        DEFB    $A0,$03,$A8,$43,$B0
        DEFB    $25,$B8,$8C,$FF,$C7
        DEFB    $0B,$04,$16,$05,$8E
        DEFB    $B2,$06,$A2,$20,$C0
        DEFB    $B0,$23,$C2,$1C,$C4
        DEFB    $85,$10,$C7,$BB,$FF
        DEFB    $CF,$D3,$01,$A2,$0D
        DEFB    $03,$16,$0B,$8E,$FD
        DEFB    $09,$82,$60,$C1,$2B
        DEFB    $C5,$AC,$FF,$E7,$21
        DEFB    $20,$9D,$FF,$EF,$E7
        DEFB    $02,$A2,$7E,$0A,$A2


GROUP3  DEFB    $FF,$00,$44
        DEFB    $23,$45,$2F,$4D,$2E
        DEFB    $4E,$00,$67,$39,$6F
        DEFB    $34,$70,$00,$71,$00
        DEFB    $A0,$21,$A1,$0A,$A2
        DEFB    $1A,$A3,$29,$A8,$1F
        DEFB    $A9,$08,$AA,$18,$AB
        DEFB    $28,$B0,$20,$B1,$09
        DEFB    $B2,$19,$B3,$27,$B8
        DEFB    $1E,$B9,$07,$BA,$17
        DEFB    $BB,$A6,$FF,$C7,$B8
        DEFB    $40,$9B,$8B,$41,$AA
        DEFB    $FF,$CF,$FD,$42,$3C
        DEFB    $4A,$81,$AD,$43,$A2
        DEFB    $DA,$4B,$A2,$FF,$E7
        DEFB    $40,$46,$95,$FF,$F7
        DEFB    $C7,$47,$A2,$7C,$57
        DEFB    $A2,$FF,$00

;_______________

MONICS  DEFB    $BF
        DEFB    'A','D','C'+$80         ; ADC 
        DEFB    'A','D','D'+$80         ; ADD 
        DEFB    'A','N','D'+$80         ; AND 
        DEFB    'B','I','T'+$80         ; BIT 
        DEFB    'C','A','L','L'+$80     ; CALL 
        DEFB    'C','C','F'+$80         ; CCF
        DEFB    'C','P','D','R'+$80     ; CPDR
        DEFB    'C','P','D'+$80         ; CPD
        DEFB    'C','P','I','R'+$80     ; CPIR
        DEFB    'C','P','I'+$80         ; CPI
        DEFB    'C','P','L'+$80         ; CPL
        DEFB    'C','P'+$80             ; CP 
        DEFB    'D','A','A'+$80         ; DAA
        DEFB    'D','E','C'+$80         ; DEC 
        DEFB    'D','I'+$80             ; DI
        DEFB    'D','J','N','Z'+$80     ; DJNZ 
        DEFB    'E','I'+$80             ; EI
        DEFB    'E','X','X'+$80         ; EXX
        DEFB    'E','X'+$80             ; EX 
        DEFB    'H','A','L','T'+$80     ; HALT
        DEFB    'I','M'+$80             ; IM 
        DEFB    'I','N','C'+$80         ; INC 
        DEFB    'I','N','D','R'+$80     ; INDR
        DEFB    'I','N','D'+$80         ; IND
        DEFB    'I','N','I','R'+$80     ; INIR
        DEFB    'I','N','I'+$80         ; INI
        DEFB    'I','N'+$80             ; IN 
        DEFB    'J','P'+$80             ; JP 
        DEFB    'J','R'+$80             ; JR 
        DEFB    'L','D','D','R'+$80     ; LDDR
        DEFB    'L','D','D'+$80         ; LDD
        DEFB    'L','D','I','R'+$80     ; LDIR
        DEFB    'L','D','I'+$80         ; LDI
        DEFB    'L','D'+$80             ; LD 
        DEFB    'N','E','G'+$80         ; NEG
        DEFB    'N','O','P'+$80         ; NOP
        DEFB    'O','R'+$80             ; OR 
        DEFB    'O','T','D','R'+$80     ; OTDR
        DEFB    'O','T','I','R'+$80     ; OTIR
        DEFB    'O','U','T','D'+$80     ; OUTD
        DEFB    'O','U','T','I'+$80     ; OUTI
        DEFB    'O','U','T'+$80         ; OUT 
        DEFB    'P','O','P'+$80         ; POP 
        DEFB    'P','U','S','H'+$80     ; PUSH 
        DEFB    'R','E','S'+$80         ; RES 
        DEFB    'R','E','T','I'+$80     ; RETI
        DEFB    'R','E','T','N'+$80     ; RETN
        DEFB    'R','E','T'+$80         ; RET
        DEFB    'R','L','A'+$80         ; RLA
        DEFB    'R','L','C','A'+$80     ; RLCA
        DEFB    'R','L','C'+$80         ; RLC 
        DEFB    'R','L','D'+$80         ; RLD
        DEFB    'R','L'+$80             ; RL 
        DEFB    'R','R','A'+$80         ; RRA
        DEFB    'R','R','C','A'+$80     ; RA
        DEFB    'R','R','C'+$80         ; RRC 
        DEFB    'R','R','D'+$80         ; RRD
        DEFB    'R','R'+$80             ; RR 
        DEFB    'R','S','T'+$80         ; RST 
        DEFB    'S','B','C'+$80         ; SBC 
        DEFB    'S','C','F'+$80         ; SCF
        DEFB    'S','E','T'+$80         ; SET 
        DEFB    'S','L','A'+$80         ; SLA 
        DEFB    'S','R','A'+$80         ; SRA 
        DEFB    'S','R','L'+$80         ; SRL 
        DEFB    'S','U','B'+$80         ; SUB 
        DEFB    'X','O','R'+$80         ; XOR 



;*****************

OPRND1  DJNZ    CONDIT

RSTADR  AND     $38
        JR      DA

OPRND2  DJNZ    DAT8

RELADR  CALL    FETCH
        LD      C,A
        RLA
        SBC     A,A
        LD      B,A
        EX      DE,HL
        PUSH    HL
        ADD     HL,BC
        JR      DHL

CONDIT  RRA
        RRA
        RRA
        DJNZ    BITNUM

        BIT     4,A
        JR      NZ,ABS

        AND     3
        
ABS     AND     7
        ADD     A,$14
        JR      PS1

DAT8    DJNZ    DAT16

D8      CALL    FETCH
        JR      DA

BITNUM  DJNZ    INTMOD
        AND     7

DA      LD      C,A
        SUB     A
        JR      DAC

DAT16   DJNZ    EXAF
        
D16     CALL    FETCH
        LD      C,A
        CALL    FETCH

DAC     EX      DE,HL
        PUSH    HL
        LD      H,A
        LD      L,C

DHL     LD      C,$F8
        PUSH    HL
        CALL    CONVHL
        POP     HL
        LD      BC,$000A
        OR      A
        SBC     HL,BC
        POP     HL
        EX      DE,HL
        RET     C

        LD      (HL),'H'
        INC     HL
        RET


INTMOD  DJNZ    STKTOP
        AND     3
        ADD     A,$1C
        
PS1     JR      PS3

STKTOP  LD      C,$13
        DEC     B
        JR      Z,PS2

REG16P  DJNZ    COMMON
        RRA
        AND     3
        CP      3
        JR      NZ,RX

        DEC     A
        JR      RNX

EXAF    LD      C,$0A
        DEC     B
        JR      Z,PS2

EXDE    INC     C
        DEC     B
        JR      Z,PS2

REG8S   DJNZ    ACCUM

R8      AND     7
        CP      6
        JR      NZ,PS3

        LD      (HL),'('
        INC     HL
        CALL    REGX
        LD      A,(IX+2)
        OR      A
        JR      Z,RP

        LD      (HL),43                 ;+
        RLCA
        RRCA
        JR      NC,POS

        LD      (HL),45                 ;-
        NEG

POS     INC     HL
        EX      DE,HL
        PUSH    HL
        LD      H,B
        LD      L,A
        LD      C,$FB
        CALL    CONVHL
        POP     HL
        EX      DE,HL
        JR      RP

ACCUM   RRA
        RRA
        RRA

COMMON  LD      C,7
        DEC     B
        JR      Z,PS2

PORTC   DEC     C
        DJNZ    IDAT8

PS2     LD      A,C
PS3     JR      PS4

IDAT8   DJNZ    IDAT16
        LD      (HL),'('
        INC     HL
        CALL    D8
        JR      RP

IDAT16  DJNZ    REG8
        LD      (HL),'('
        INC     HL
        CALL    D16
        JR      RP

REG8    DEC     B
        JR      Z,R8

IPAREF  DJNZ    REG16
        AND     9
        JR      PS4

REG16   RRA
        DJNZ    IREG16

R16     AND     3
RX      CP      2
        JR      Z,REGX

RNX     ADD     A,$0C
        JR      PS4

IREG16  DJNZ    REGX
        LD      (HL),'('
        INC     HL
        CALL    R16

RP      LD      (HL),')'
        INC     HL
        RET

REGX    LD      A,(IX+1)
        ADD     A,$10

PS4     EX      DE,HL
        PUSH    HL
        LD      HL,RGSTRS
        CALL    XTRACT
        POP     HL
        EX      DE,HL
        RET

;*************

RGSTRS  DEFB    'B'                             +$80
        DEFB    'C'                             +$80
        DEFB    'D'                             +$80
        DEFB    'E'                             +$80
        DEFB    'H'                             +$80
        DEFB    'L'                             +$80
        DEFB    "(","C",')'                     +$80
        DEFB    'A'                             +$80
        DEFB    'I'                             +$80
        DEFB    'R'                             +$80
        DEFB    "A","F",",","A","F",'''         +$80
        DEFB    "D","E",",","H",'L'             +$80
        DEFB    "B",'C'                         +$80
        DEFB    "D",'E'                         +$80
        DEFB    "A",'F'                         +$80
        DEFB    "S",'P'                         +$80
        DEFB    "H",'L'                         +$80
        DEFB    "I",'X'                         +$80
        DEFB    "I",'Y'                         +$80
        DEFB    "(","S","P",')'                 +$80
        DEFB    "N",'Z'                         +$80
        DEFB    'Z'                             +$80
        DEFB    "N",'C'                         +$80
        DEFB    'C'                             +$80
        DEFB    "P",'O'                         +$80
        DEFB    "P",'E'                         +$80
        DEFB    'P'                             +$80
        DEFB    'M'                             +$80
        DEFB    '0'                             +$80
        DEFB    '?'                             +$80
        DEFB    '1'                             +$80
        DEFB    '2'                             +$80

;********************

CONVHL  SUB     A

CVHL1   PUSH    AF
        SUB     A
        LD      B,16

CVHL2   ADD     A,C
        JR      C,CVHL3
        SUB     C

CVHL3   ADC     HL,HL
        RLA
        DJNZ    CVHL2

        JR      NZ,CVHL1

        CP      10
        INC     B
        JR      NC,CVHL1

CVHL4   CP      10
        SBC     A,$69
        DAA
        LD      (DE),A
        INC     DE
        POP     AF
        JR      NZ,CVHL4

        RET

;****************

XTRACT  OR      A
        JR      Z,COPY

SKIP    BIT     7,(HL)
        INC     HL
        JR      Z,SKIP

        DEC     A
        JR      NZ,SKIP

COPY    LD      A,(HL)
        RLCA
        SRL     A
        LD      (DE),A

        INC     DE
        INC     HL
        JR      NC,COPY

        RET

;*******************

FETCH   LD      A,(DE)
        INC     DE
        INC     (IX+0)
        PUSH    AF
        CALL    BYTSP
        POP     AF
        RET

ADRSP   LD      A,D
        CALL    BYTOP
        LD      A,E

BYTSP   CALL    BYTOP
        LD      A,$20
        JR      CHROP

BYTOP   PUSH    AF
        RRA
        RRA
        RRA
        RRA
        CALL    HEXOP
        POP     AF

HEXOP   AND     $0F
        CP      10
        SBC     A,$69
        DAA

; -----------------------------------
;
; End of John Kerr's DIS-Z80 routine.
; 
; The next routine outputs a character.
;
; -------------------------------------

CHROP   PUSH    HL
        PUSH    DE
        PUSH    BC

        CALL    RITE

        POP     BC
        POP     DE
        POP     HL

        RET

;=======================
;
; This scrolling subroutine scrolls the lower 15 lines up by one character 
; position. Text line 8 is copied across a third of the screen to text 
; line 7. Text line 8 is also, for simplicity, copied to line 15 just before
; line 15 is cleared. This is purely to enable the DE register to advance 
; linearly while the source register is adjusted to pick up the appropriate
; text. 
;
;=======================


SCROLL  LD      HL,$4020                ; set source
        LD      DE,$4000                ; set destination

SLOOP   LD      BC,$00E0                ; 7 lines to move

        LDIR                            ; copy the lines 

        LD      C,$20                   ; prepare to copy one line - 32 bytes.

        LD      A,H                     ; fetch the high byte of source
        CP      $50                     ; is it in lower third ?
        JR      Z, CLINE                ; exit if so to clear 15'th
                                        ; line to accept new text..

        CP      $49                     ; if in middle third of screen
        JR      NC,SAME                 ; don't adjust source.

        ADD     A,7                     ; but if destination is text line 7
        LD      H,A                     ; then adjust to point across a third.

        LDIR                            ; copy 32 bytes across thirds.

        SUB     7                       ; restore source to top third again as
        LD      H,A                     ; there are more moves within third.
        
        JR      SLOOP                   ; back to scrolling loop.

SAME    LDIR                            ; this briefly copies line 8 to 15.
                                        ; copying line 16 to 15 is harmless 
                                        ; but looks awful.

        JR      SLOOP                   ; back to scrolling loop.


;====================
;
;This routine clears text line 15 which consists of eight pixel lines.
;
;====================

CLINE   LD      BC,$0800                ; set counter to 8, C to zero.
        LD      H,$48                   ; high byte of initial screen address.

CLOOP1  LD      L,$E0                   ; all pixel lines have $E0 as start,
                                        ; and $FF as end.

CLOOP2  LD      (HL),C                  ; insert a zero.
        INC     L                       ; address next horizontal byte
        JR      NZ,CLOOP2               ; loop until L is zero.   

        INC     H                       ; address next vertical byte.
        DJNZ    CLOOP1                  ; and repeat for all 8 lines, 
                                        ; continuing to set print position.

; Set up print plot mask in the alternate accumulator.

        LD      A,$80                   ; prepare plotting mask 10000000
        EX      AF,AF'                  ; save in alternate accumulator

        LD      A,$E0                   ; start of line 15 is $E0
        LD      (PRIPOS),A              ; set print position variable.

        RET                             ; return.

;======================
;
; This is the PRINT routine which prints on line 15 only. The line must have 
; been cleared previously. As the bitmaps are stored as vertical slices of
; a character and have to be laid down one pixel at a time, the 
; routine 'plots' the characters onto the screen.
;
;======================

RITE    LD      C,6                     ; prepare to advance 6 pixels for a 
                                        ; space


        CP      123                     ; is character higher than 'z' ? 
        JR      NC,UPD6                 ; forward to update by six pixels

        SUB     39                      ; reduce to range 0 - 83 decimal
        JR      C,UPD6                  ; forward to print as space if less
                                        ; than "'"

        LD      D,$56                   ; high byte of character set start.

        LD      B,A                     ; multiply
        RLC     B                       ; by
        ADD     A,B                     ; three  - max is 249 decimal.
        ADD     A,B                     ; five   - max is 415 decimal
        JR      NC,RITE1                ; if last addition produced no carry
                                        ; skip to use $56 as high byte.

        INC     D                       ; else increment high byte in D to $57.

RITE1   LD      E,A                     ; transfer low byte to E

        LD      B,$05                   ; count five source bytes.

RITE2   LD      HL,(PRIPOS)             ; fetch screen address low byte.

        LD      H,$48                   ; set high byte.

        LD      A,(DE)                  ; fetch a character bitmap.
        INC     DE                      ; address next source byte.

RITE3   RRCA                            ; is the bit set?
        JR      NC,RITE4                ; forward to leave corresponding 
                                        ; screen pixel unset if not so.

        EX      AF,AF'                  ; switch in the 'plot' mask.
        PUSH    AF                      ; preserve it on machine stack.
        OR      (HL)                    ; combine with what's on the screen.
        LD      (HL),A                  ; and update screen address
        POP     AF                      ; restore mask.
        EX      AF,AF'                  ; and save for future use.

RITE4   INC     H                       ; advance vertically down the screen.

        BIT     4,H                     ; when high byte reaches $50 then this
                                        ; program is about to be overwritten.

        JR      Z,RITE3                 ; otherwise loop for all 8 bits.

        CALL    UPD1                    ; update mask and possibly the low 
                                        ; byte of screen address.

        DJNZ    RITE2                   ; repeat for all 5 vertical slices.

; then continue into update routine to create a 1 pixel gap between characters.


;===================
;
; The update routine modifies the plotting mask rotating by a single pixel 
; if C holds 1 but by 6 pixels if C holds 6. The screen address may be 
; incremented.
; The routine is also entered at UPD6 with C holding 6 to print a space.
;
;===================


UPD1    LD      C,$01                   ; update by 1 pixel.

; -->

UPD6    EX      AF,AF'                  ; switch in mask.

ULOOP   RRCA                            ; rotate mask to right.

        JR      NC,NOCHNG               ; skip if no carry.
        
        LD      HL,PRIPOS               ; address print position low.
        INC     (HL)                    ; and move to right.

NOCHNG  DEC     C                       ; decrement pixel counter.
        JR      NZ,ULOOP                ; back to ULOOP for more.

        EX      AF,AF'                  ; save mask.

        RET                             ; return.

; ----------------------------
; Routine to validate a keypress 
; against a table of valid keys and 
; loop until a meaningful key is pressed. 
; ----------------------------

KEY     PUSH    HL                      ; save HL throughout.

KEY1    CALL    KSCAN                   ; scan the keyboard.

        LD      HL,TABLE                ; address keys table.

KLOOP   CP      (HL)                    ; compare key in A against an entry.
        JR      Z,FOUND                 ; forward with a match.

        INC     L                       ; address next table entry.
        JR      Z,KEY1                  ; end of table so back to read the 
                                        ; keyboard again

        JR      KLOOP                   ; loop back until table read.

; but a match is found so transfer low byte of address to A.

FOUND   LD      A,L                     ; transfer table location to A

        CPL                             ; entries were in reverse order so
                                        ; complement to give translated value.

        POP     HL                      ; restore initial value of HL.

L55DA   RET                             ; return.

; Note there is a gap of approx 18 unused bytes here.

; ---

; Of the 40 keys on the Spectrum keyboard, only nineteen are used by this 
; program.

ORG $55ED 

TABLE   DEFB    $25                     ; Q
        DEFB    $0D                     ; R
        DEFB    $21                     ; ENTER
        DEFB    $0E                     ; F
        DEFB    $15                     ; E
        DEFB    $16                     ; D
        DEFB    $0F                     ; C
        DEFB    $00                     ; B
        DEFB    $26                     ; A
        DEFB    $1B                     ; 9
        DEFB    $13                     ; 8
        DEFB    $0B                     ; 7
        DEFB    $03                     ; 6
        DEFB    $04                     ; 5
        DEFB    $0C                     ; 4
        DEFB    $14                     ; 3
        DEFB    $1C                     ; 2
        DEFB    $24                     ; 1
L55FF   DEFB    $23                     ; 0

; ---

ORG $5600 

; The character set consists of characters 39d(') to 122d(z)
; Characters outside this range are printed blank.
; Some characters within this range have been intentionally left blank as they 
; are not required by the disassembler and are unlikely to occur in text 
; messages.
; Characters are stored as vertical slices so the letter A is

; 01111110 = 126
; 00001001 = 9
; 00001001 = 9
; 00001001 = 9
; 01111110 = 126

;CSET
        DEFB    0,4,2,1,0                       ; '
        DEFB    0,28,34,65,0                    ; (
        DEFB    0,65,34,28,0                    ; )

        DEFB    0,0,0,0,0                       ; *

        DEFB    8,8,62,8,8                      ; +
        DEFB    0,80,48,0,0                     ; ,
        DEFB    8,8,8,8,8                       ; -
        DEFB    0,0,96,96,0                     ; =

        DEFB    0,0,0,0,0                       ; /

        DEFB    62,81,73,69,62                  ; 0
        DEFB    0,66,127,64,0                   ; 1
        DEFB    66,97,81,73,70                  ; 2
        DEFB    33,65,69,75,49                  ; 3
        DEFB    24,20,18,127,16                 ; 4
        DEFB    39,69,69,69,57                  ; 5
        DEFB    60,74,73,73,48                  ; 6
        DEFB    1,1,121,5,3                     ; 7
        DEFB    54,73,73,73,54                  ; 8
        DEFB    6,73,73,41,30                   ; 9
        DEFB    0,0,36,0,0                      ; :
        DEFB    0,64,50,0,0                     ; ; 

        DEFB    0,0,0,0,0                       ; <
        DEFB    0,0,0,0,0                       ; =
        DEFB    0,0,0,0,0                       ; >

        DEFB    2,1,81,9,6                      ; ?

        DEFB    0,0,0,0,0                       ; @

        DEFB    126,9,9,9,126                   ; A
        DEFB    127,73,73,73,54                 ; B
        DEFB    62,65,65,65,34                  ; C
        DEFB    127,65,65,34,28                 ; D
        DEFB    127,73,73,73,65                 ; E
        DEFB    127,9,9,9,1                     ; F
        DEFB    62,65,73,73,122                 ; G
        DEFB    127,8,8,8,127                   ; H
        DEFB    0,65,127,65,0                   ; I
        DEFB    32,64,65,63,1                   ; J
        DEFB    127,12,18,33,64                 ; K
        DEFB    127,64,64,64,64                 ; L
        DEFB    127,2,12,2,127                  ; M
        DEFB    127,4,8,16,127                  ; N
        DEFB    62,65,65,65,62                  ; O
        DEFB    127,9,9,9,6                     ; P
        DEFB    62,65,113,193,62                ; Q
        DEFB    127,9,25,41,70                  ; R
        DEFB    38,73,73,73,50                  ; S
        DEFB    1,1,127,1,1                     ; T
        DEFB    63,64,64,64,63                  ; U
        DEFB    7,24,96,24,7                    ; V
        DEFB    31,96,28,96,31                  ; W
        DEFB    99,20,8,20,99                   ; X
        DEFB    3,4,120,4,3                     ; Y
        DEFB    97,81,73,69,67                  ; Z

        DEFB    0,0,0,0,0                       ; [
        DEFB    0,0,0,0,0                       ; /
        DEFB    0,0,0,0,0                       ; ]
        DEFB    0,0,0,0,0                       ; ^
        DEFB    0,0,0,0,0                       ; _
        DEFB    0,0,0,0,0                       ; uk currency symbol

        DEFB    32,84,84,84,120                 ; a
        DEFB    126,72,72,72,48                 ; b
        DEFB    56,68,68,68,0                   ; c
        DEFB    48,72,72,72,126                 ; d
        DEFB    56,84,84,84,72                  ; e
        DEFB    0,124,10,2,0                    ; f
        DEFB    24,164,164,164,124              ; g
        DEFB    126,8,8,8,112                   ; h
        DEFB    0,72,122,64,0                   ; i
        DEFB    64,128,128,122,0                ; j
        DEFB    126,24,36,64,0                  ; k
        DEFB    0,62,64,64,0                    ; l
        DEFB    124,4,120,4,120                 ; m
        DEFB    124,4,4,4,120                   ; n
        DEFB    56,68,68,68,56                  ; o
        DEFB    252,36,36,36,24                 ; p
        DEFB    24,36,36,252,128                ; q
        DEFB    120,4,4,4,0                     ; r
        DEFB    72,84,84,84,32                  ; s
        DEFB    4,62,68,64,0                    ; t
        DEFB    60,64,64,64,60                  ; u
        DEFB    12,48,64,48,12                  ; v
        DEFB    60,64,56,64,60                  ; w
        DEFB    68,40,16,40,68                  ; x
        DEFB    28,160,160,160,124              ; y
        DEFB    68,100,84,76,68                 ; z

; End of font.

;=====================
;
; This routine first prints the message ROMx then continues into the
; big print routine.
;
;=====================

ROMTXT  LD      DE,$50FC                ; address ROMx
        CALL    CLINE                   ; clear line 15.

RLOOP   LD      A,(DE)                  ; fetch a character.
        CALL    CHROP                   ; print it on line 15.
        INC     E                       ; point to next character 
        JR      NZ,RLOOP                ; and loop until 4 characters printed.
                                        ; continue into big print routine.

; Routine to scan text at start of line 15 
; producing big text in lower third of screen 
; by updating colour attributes.

BIGPR   LD      HL,$5A20                ; attribute address for line 17 decimal
        LD      D,$48                   ; display file address of line 15 top.

        LD      BC,$0001                ; colour in B starts as black ink on
                                        ; black paper (0).
                                        ; C is used as an eight counter.

BIGPR2  LD      E,$E0                   ; low byte of address in display file.

BIGPR3  LD      A,(DE)                  ; fetch a display file byte.

BIGPR4  RLCA                            ; rotate bits to carry

        LD      (HL),63                 ; load attributes with white/white.

        JR      NC,BIGPR5               ; if no carry leave as it is.

        LD      (HL),B                  ; else overwrite attribute byte with 
                                        ; a colour.

BIGPR5  INC     L                       ; point to next attribute byte 
        RLC     C                       ; rotate the eight counter.
        JR      NC,BIGPR4               ; loop back for all eight bits

        INC     E                       ; bump the screen address
        BIT     2,E                     ; check that bytes don't exceed 4
        JR      Z,BIGPR3                ; loop back for bytes $E0-$E3.

        LD      A,9                     ; now manipulate the colour attribute
        ADD     A,B                     ; to give a rainbow gradation.
        AND     $1F                     ; but pure colours are hard to see as
        LD      B,A                     ; they get lighter so this compromise
                                        ; was arrived at by accident.

        INC     D                       ; move vertically down display file.    
        BIT     3,D                     ; check high byte to ensure still
        JR      NZ,BIGPR2               ; within middle third and continue.

        RET                             ; return when all 7 pixel lines have 
                                        ; been enlarged.

;=====================
;
; The keyboard scanning routine is similar to
; that in the Spectrum ROM at address $028E
;
; For each of the 40 keys  a number in the range
; 0-39 ($00-$27)  is returned.
;
;=====================

KSCAN   LD      DE,$FF2F                ; initialize D to receive key.
                                        ; E is starting key value + 8
                                        ; $27 is 39 decimal.

        LD      BC,$FEFE                ; commencing port address
                                        ; B is also an 8 counter.

KLINE   IN      A,(C)                   ; read a keyboard half row. 
        CPL                             ; pressed keys now equate to a set bit.
        AND     $1F                     ; isolate lower meaningful 5 bits.
        JR      Z,KDONE                 ; forward if no keys pressed in half-
                                        ; row.

        INC     D                       ; test D - result should be zero.

        JR      NZ,KSCAN                ; two keys are unacceptable so start
                                        ; from the beginning again.

        LD      H,A                     ; transfer bit to H
        LD      A,E                     ; key value to A.

KBITS   SUB     8                       ; subtract eight from value
        SRL     H                       ; shift bit to carry 
        JR      NC,KBITS                ; loop until carry is set

        JR      NZ,KSCAN                ; if two keys pressed in the same 
                                        ; row then start again.

        LD      D,A                     ; otherwise pass key value to D
                                        ; and continue to check that no other
                                        ; key is pressed in other rows.

KDONE   DEC     E                       ; decrease starting key value for 
                                        ; a new row. 
        RLC     B                       ; form next port address.
        JR      C,KLINE                 ; repeat for all 8 rows while a carry
                                        ; is set.

        LD      A,D                     ; transfer key value to A
        INC     D                       ; test for a valid key in D/A
        JR      Z,KSCAN                 ; repeat from beginning if was $FF.

L57FE   RET                             ; return with key value $00 - $27 in A.

; ---      

ORG $57FF

        DEFB    56                      ; white/white 63 but it helps to use 
                                        ; black/white 56 while testing.


; Note the above attribute
; which immediately
; precedes the attr.file
; is used to flood the
; screen with white/white before
; the return to BASIC.


; Normally this file builds a 2K object file 
; e.g. tasm -80 -b u3.asm
; This can be loaded into the Z80 emulator using the extra functions
; Load a Block of memory/screen 
; Set start to #4000, length to #0800 and name to u3.obj or whatever.
; execute with PRINT USR 16384.

; Since not all emulators can 
; load an arbitrary block of code, by removing the semicolon from 
; the start of the next two lines and building with 
; tasm -80 -b -f38 u3.asm
; then a binary file of 6912 bytes will be produced which can be 
; renamed to u3.scr which loads into most emulators.
; This will have black and white stripes in the lower two thirds of
; the display which may be cleared from basic. To highlight the ascii
; column in cyan, say, use PRINT AT 15,28; PAPER 5; "    " .
;
; This file is difficult to work on, but I have now removed the self-modifying
; code that was present in the original version.

; .org $5aff+$1000
; .byte $38

.END                                    ; cross-assembler directive