; CCC3FST - Cromemco Cyclops CCC3 program, modified for CP/M
; Original functionality translated from the octal dump in the
; Cromemco-88 CCC manual.
;
; REVERSE
;
; Analyzed, documented and corrected by Bill Sudbrink in March
; of 2018.
;
; This translation includes several corrections to typos in
; the original.
;
; High speed Cyclops/Dazzler transfer written by Bill Sudbrink
; in April 2018

CPM EQU 5

CYPBASE EQU 3000H
; Cyclops port defaults (conflicts with 2SIO)
;CYPA EQU 010H
;CYPB EQU 011H
;CYPC EQU 012H


CYPA EQU 090H
CYPB EQU 091H
CYPC EQU 092H

   ORG 100H

   JMP START

; DATA
WELCOME:
   DB 'CCC3CPM - Interpreted and enhanced in 2018 by Bill Sudbrink',0DH,0AH,'$'
INSTRCTS:
   DB 'Press X or Q to exit.',0DH,0AH,'$'
NEEDPRT:
   DB 'Need print routine.',0DH,0AH,'$'
CYCWA:
   DB 80H    ; Cyclops, control word A, one frame, bias light off, 0 aux
CYCWB:
   DB 0BH    ; Cyclops, control word B, no 2 millisecond gapss, 11(B) fields...
DZMODE:
   DB 00H    ; Dazzler color mode


START:
   LXI SP,2000H   ; put our stack above the dazzler buffer
   LXI D,WELCOME
   MVI C,9
   CALL CPM
   LXI D,INSTRCTS
   MVI C,9
   CALL CPM

; set up Dazzler one time only, it's all that is required
;   MVI A, 00H    ; Dazzler, normal res, 512 byte frame, grey scale
   LDA DZMODE
   OUT 0FH
;   MVI A, 8BH    ; Dazzler, high bit turns on, starting address 00010110 0x1600
;   OUT 0EH

NXTFRM:
   MVI A, 60H    ; Cyclops, control word C, sets address to binary:x011 0000 0xxx xxxx or hex: 0x3000
   OUT CYPC
;   MVI A, 0BH    ; Cyclops, control word B, no 2 millisecond gapss, 11(B) fields...
   LDA CYCWB
   OUT CYPB

;CHKR:            ; fill Cyclops buffer with checkerboard for testing
;   MVI C,0C0H
;   LXI H, CYPBASE
;CHKLOOP:
;   MVI M,55H
;   INX H
;   MVI M,55H
;   INX H
;   MVI M,55H
;   INX H
;   MVI M,55H
;   INX H
;   MVI M,0AAH
;   INX H
;   MVI M,0AAH
;   INX H
;   MVI M,0AAH
;   INX H
;   MVI M,0AAH
;   INX H
;   DCR C
;   JNZ CHKLOOP

; make sure dazzler is off
   XRA A
   OUT 0EH
   NOP
   NOP
   NOP

;   MVI A, 80H    ; Cyclops, control word A, one frame, bias light off, 0 aux
   LDA CYCWA
   OUT CYPA

; loop until we have a frame from Cyclops
WAITFRM:
   IN CYPA
   ANI 80H
   JNZ WAITFRM   ; loop if high bit not clear

   MVI A, 8BH    ; Dazzler, high bit turns on, starting address 00010110 0x1600
   OUT 0EH

; Do a binary search of the Cyclops fields to find where the
; cell transitions from on to off.  Emit the correct pixel value
; based on the transition.
   LXI D,1600H     ; dazzler address
   MVI C,80H
NXTTEST:
   MOV A,C
   RLC
   MOV C,A
   LXI H,CYPBASE+300H
   MVI B,060H        ; test field 6

; given dazzler address in DE, cyclops field number in the high nibble of B,
; field pointer in HL and bit mask in C, AND the bit mask against the correct
; cyclops byte, then jump to the node function (org 200) for the field/result
TSTCYBIT:
   MVI A,0FCH ; adjust field address with dazzler address
   ANA E
   RRC
   RRC        ; E divided by 4
   ORA L      ; high bit from caller
   MOV L,A
   MOV A,D
   RRC
   JNC SKIP2
   MOV A,L
   ORI 40H
   MOV L,A
SKIP2:       ; HL now points to correct cyclops byte
   MOV A,M         ; load cyclops byte
   MVI H,02H
   ANA C           ; test bit
   JZ SKIP3
   MVI A,08H
   ORA B
   MOV L,A
   PCHL
SKIP3:
   MOV L,B
   PCHL

; Note: The code below consists of 16 byte nodes of a binary search tree.
;       Each node is composed of two 8 byte branches, the first branch
;       is used if the field bit is zero, the second branch is used if the
;       field bit is one.  The TSTCYBIT (test Cyclops bit) function will
;       calculate the correct address and jump to the node/branch.  The
;       labels in the code are not used, they are just included for
;       reference.  Each branch will either set up to test another field
;       or will emit a pixel value.
   ORG 200H
PIXB: ; field 0, bit is 0
   MVI B,0BH         ; pixel value 0BH
   JMP NXTPIX
   DB 0,0,0
PIXC: ; field 0, bit is 1
   MVI B,0CH         ; pixel value 0CH
   JMP NXTPIX
   DB 0,0,0
TST2: ; field 1, bit is 0
   LXI H,CYPBASE+100H
   MVI B,020H        ; test field 2
   JMP TSTCYBIT
TST0: ; field 1, bit is 1
   LXI H,CYPBASE
   MVI B,00H        ; test field 0
   JMP TSTCYBIT
PIX9: ; field 2, bit is 0
   MVI B,9H          ; pixel value 9
   JMP NXTPIX
   DB 0,0,0
PIXA: ; field 2, bit is 1
   MVI B,0AH         ; pixel value 0AH
   JMP NXTPIX
   DB 0,0,0
PIX8: ; field 3, bit is 0
   MVI B,8H          ; pixel value 8
   JMP NXTPIX
   DB 0,0,0
TST1: ; field 3, bit is 1
   LXI H,CYPBASE+80H
   MVI B,010H        ; test field 1
   JMP TSTCYBIT
TST5: ; field 4, bit is 0
   LXI H,CYPBASE+280H
   MVI B,050H        ; test field 5
   JMP TSTCYBIT
TST3: ; field 4, bit is 1
   LXI H,CYPBASE+180H
   MVI B,030H        ; test field 3
   JMP TSTCYBIT
PIX6: ; field 5, bit is 0
   MVI B,6H          ; pixel value 6
   JMP NXTPIX
   DB 0,0,0
PIX7: ; field 5, bit is 1
   MVI B,7H          ; pixel value 7
   JMP NXTPIX
   DB 0,0,0
TST8: ; field 6, bit is 0
   LXI H,CYPBASE+400H
   MVI B,080H        ; test field 8
   JMP TSTCYBIT
TST4: ; field 6, bit is 1
   LXI H,CYPBASE+200H
   MVI B,040H        ; test field 4
   JMP TSTCYBIT
PIX4: ; field 7, bit is 0
   MVI B,4H          ; pixel value 4
   JMP NXTPIX
   DB 0,0,0
PIX5: ; field 7, bit is 1
   MVI B,5H          ; pixel value 5
   JMP NXTPIX
   DB 0,0,0
TSTA: ; field 8, bit is 0
   LXI H,CYPBASE+500H
   MVI B,0A0H        ; test field A
   JMP TSTCYBIT
TST7: ; field 8, bit is 1
   LXI H,CYPBASE+380H
   MVI B,070H        ; test field 7
   JMP TSTCYBIT
PIX2: ; field 9, bit is 0
   MVI B,2H          ; pixel value 2
   JMP NXTPIX
   DB 0,0,0
PIX3: ; field 9, bit is 1
   MVI B,3H          ; pixel value 3
   JMP NXTPIX
   DB 0,0,0
TSTB: ; field A, bit is 0
   LXI H,CYPBASE+580H
   MVI B,0B0H        ; test field B
   JMP TSTCYBIT
TST9: ; field A, bit is 1
   LXI H,CYPBASE+480H
   MVI B,090H        ; test field 9
   JMP TSTCYBIT
PIX0: ; field B, bit is 0
   MVI B,0            ; pixel value 0
   JMP NXTPIX
   DB 0,0,0
PIX1: ; field B, bit is 1
   MVI B,1H          ; pixel value 1

NXTPIX:
   MOV A,C
   ANI 055H
   JZ PAIRDONE
   PUSH B           ; push pixel
   JMP NXTTEST
PAIRDONE:
   POP PSW          ; pop other pixel into A
   RLC
   RLC
   RLC
   RLC
   ORA B            ; combine pixels
   STAX D           ; and store in Dazzler frame buffer
   INX D            ; increment dazzler address every other pixel
   MOV A,D
   CPI 18H
   JNZ NXTTEST

; Done with the frame
FRMDONE:
   MVI C,0BH      ; Check for character available on console
   CALL CPM
   CPI 00H
   JZ NXTFRM
   MVI C,01H
   CALL CPM       ; get the character
   CPI 30H        ; 0-5, select palettes
   JZ PLT0
   CPI 31H
   JZ PLT1
   CPI 32H
   JZ PLT2
   CPI 33H
   JZ PLT3
   CPI 34H
   JZ PLT4
   CPI 35H
   JZ PLT5
   ANI 0DFH       ; upper case
   CPI 58H        ; X character (exit)
   JZ EXIT
   CPI 51H        ; Q character (quit, same as exit)
   JZ EXIT
   CPI 46H        ; F character (freeze frame)
   JZ FREEZE
   CPI 4CH        ; L character (toggle bias Light)
   JZ TGL1
   CPI 47H        ; G character (increment field Gap)
   JZ GAP1
   CPI 43H        ; C character (toggle Color)
   JZ TGC1
   JMP NXTFRM     ; otherwise, start over and get another frame

PLT0: ; palette zero (default)
   MVI A,0H
   STA PIX0+1
   MVI A,01H
   STA PIX1+1
   MVI A,02H
   STA PIX2+1
   MVI A,03H
   STA PIX3+1
   MVI A,04H
   STA PIX4+1
   MVI A,05H
   STA PIX5+1
   MVI A,06H
   STA PIX6+1
   MVI A,07H
   STA PIX7+1
   MVI A,08H
   STA PIX8+1
   MVI A,09H
   STA PIX9+1
   MVI A,0AH
   STA PIXA+1
   MVI A,0BH
   STA PIXB+1
   MVI A,0CH
   STA PIXC+1
   JMP NXTFRM

PLT1: ; palette one (four level, high contrast)
   MVI A,0H
   STA PIX0+1
   MVI A,0H
   STA PIX1+1
   MVI A,0H
   STA PIX2+1
   MVI A,00H
   STA PIX3+1
   MVI A,05H
   STA PIX4+1
   MVI A,05H
   STA PIX5+1
   MVI A,05H
   STA PIX6+1
   MVI A,0AH
   STA PIX7+1
   MVI A,0AH
   STA PIX8+1
   MVI A,0AH
   STA PIX9+1
   MVI A,0FH
   STA PIXA+1
   MVI A,0FH
   STA PIXB+1
   MVI A,0FH
   STA PIXC+1
   JMP NXTFRM

PLT2: ; palette two (reverse)
   MVI A,0FH
   STA PIX0+1
   MVI A,0EH
   STA PIX1+1
   MVI A,0DH
   STA PIX2+1
   MVI A,0CH
   STA PIX3+1
   MVI A,0BH
   STA PIX4+1
   MVI A,0AH
   STA PIX5+1
   MVI A,09H
   STA PIX6+1
   MVI A,08H
   STA PIX7+1
   MVI A,07H
   STA PIX8+1
   MVI A,06H
   STA PIX9+1
   MVI A,05H
   STA PIXA+1
   MVI A,04H
   STA PIXB+1
   MVI A,03H
   STA PIXC+1
   JMP NXTFRM

PLT3: ; palette three (stretch middle)
   MVI A,0H
   STA PIX0+1
   MVI A,01H
   STA PIX1+1
   MVI A,02H
   STA PIX2+1
   MVI A,03H
   STA PIX3+1
   MVI A,04H
   STA PIX4+1
   MVI A,05H
   STA PIX5+1
   MVI A,08H
   STA PIX6+1
   MVI A,0AH
   STA PIX7+1
   MVI A,0BH
   STA PIX8+1
   MVI A,0CH
   STA PIX9+1
   MVI A,0DH
   STA PIXA+1
   MVI A,0EH
   STA PIXB+1
   MVI A,0FH
   STA PIXC+1
   JMP NXTFRM

PLT4: ; palette four (color)
   MVI A,0H
   STA PIX0+1
   MVI A,00H
   STA PIX1+1
   MVI A,07H ; grey
   STA PIX2+1
   MVI A,04H ; dark blue
   STA PIX3+1
   MVI A,0CH ; bright blue
   STA PIX4+1
   MVI A,02H ; dark green
   STA PIX5+1
   MVI A,0AH ; bright green
   STA PIX6+1
   MVI A,05H ; dark blue/red
   STA PIX7+1
   MVI A,0DH ; bright blue/red
   STA PIX8+1
   MVI A,01H ; dark red
   STA PIX9+1
   MVI A,09H ; bright red
   STA PIXA+1
   MVI A,0BH ; bright green/red (yellow)
   STA PIXB+1
   MVI A,0FH ; white
   STA PIXC+1
   JMP NXTFRM

PLT5: ; palette five (monochrome)
   MVI A,0H
   STA PIX0+1
   MVI A,0H
   STA PIX1+1
   MVI A,0H
   STA PIX2+1
   MVI A,0H
   STA PIX3+1
   MVI A,0H
   STA PIX4+1
   MVI A,0H
   STA PIX5+1
   MVI A,0H
   STA PIX6+1
   MVI A,0H
   STA PIX7+1
   MVI A,0FH
   STA PIX8+1
   MVI A,0FH
   STA PIX9+1
   MVI A,0FH
   STA PIXA+1
   MVI A,0FH
   STA PIXB+1
   MVI A,0FH
   STA PIXC+1
   JMP NXTFRM

TGL1: ; toggle the bias light and keep going
   LDA CYCWA
   XRI 20H
   STA CYCWA
   JMP NXTFRM

TGC1: ; toggle the Dazzler color mode and keep going
   LDA DZMODE
   XRI 10H
   STA DZMODE
   OUT 0FH
   JMP NXTFRM

GAP1: ; increment the field gap and keep going
   LDA CYCWB
   ADI 10H
   ANI 3FH
   STA CYCWB
   JMP NXTFRM

FREEZE:
   MVI C,0BH      ; Check for character available on console
   CALL CPM
   CPI 00H
   JZ FREEZE
   MVI C,01H
   CALL CPM       ; get the character
   ANI 0DFH       ; upper case
   CPI 52H        ; R character (resume, unfreeze)
   JZ NXTFRM
   CPI 58H        ; X character (exit)
   JZ EXIT
   CPI 51H        ; Q character (quit, same as exit)
   JZ EXIT
   CPI 4CH        ; L character (toggle bias Light)
   JZ TGL2
   CPI 47H        ; G character (increment field Gap)
   JZ GAP2
   CPI 43H        ; C character (toggle Color)
   JZ TGC2
   CPI 42H        ; B character (make Brighter)
   JZ BRIGHT
   CPI 50H        ; P character (Print)
   JZ PRINT
   CPI 5AH        ; Z character (zero out the Dazzler buffer (for testing))
   JZ ZERO
   CPI 56H        ; V character (reverse Video)
   JZ REVERSE
   JMP FREEZE

TGL2: ; toggle the bias light and keep frozen
   LDA CYCWA
   XRI 20H
   STA CYCWA
   JMP FREEZE

TGC2: ; toggle the Dazzler color mode and keep frozen
   LDA DZMODE
   XRI 10H
   STA DZMODE
   OUT 0FH
   JMP FREEZE

GAP2: ; increment the field gap and keep frozen
   LDA CYCWB
   ADI 10H
   ANI 3FH
   STA CYCWB
   JMP FREEZE

ZERO:
   LXI H, 1600H
NXTZRO:
   XRA A
   MOV M,A
   INX H
   MOV A,H
   CPI 18H
   JNZ NXTZRO
   JMP FREEZE

REVERSE: ; XOR all of the Dazzler pixel values with 0xFF
   LXI H, 1600H
NXTREV:
   MOV A,M
   XRI 0FFH
   MOV M,A
   INX H
   MOV A,H
   CPI 18H
   JNZ NXTREV
   JMP FREEZE

BRIGHT: ; increment all of the Dazzler pixel values
   LXI H, 1600H
NXTBRT:
   MOV A,M
   ADI 10H
   MOV B,A
   INR A
   ANI 0FH
   MOV C,A
   MOV A,B
   ANI 0F0H
   ADD C
   MOV M,A
   INX H
   MOV A,H
   CPI 18H
   JNZ NXTBRT
   JMP FREEZE

RAMP:
;   DB ' .:;-=+*%O#$@XKH'
   DB 'HKX@$#O%*+=-;:. '
LFCR:
   DB 0DH,0AH,'$'

PRINT:
;   LXI D,NEEDPRT
;   MVI C,9
;   CALL CPM
; Three blank lines
   LXI D,LFCR
   MVI C,9
   CALL CPM
   LXI D,LFCR
   MVI C,9
   CALL CPM
   LXI D,LFCR
   MVI C,9
   CALL CPM
   LXI H, 1600H   ; address of Dazzler buffer
NXTPRT:
   MOV A,M
   MOV B,A
   ANI 0FH
   LXI D,RAMP
   ADD E
   MOV E,A
   JNC SKPINC4
   INR D
SKPINC4:
   LDAX D        ; ramp character in A
   MOV E,A
   MVI C,2
   PUSH H
   PUSH B
   CALL CPM
   POP B
   POP H
   MOV A,B
   ANI 0F0H
   RRC
   RRC
   RRC
   RRC
   LXI D,RAMP
   ADD E
   MOV E,A
   JNC SKPINC5
   INR D
SKPINC5:
   LDAX D        ; ramp character in A
   MOV E,A
   MVI C,2
   PUSH H
   PUSH B
   CALL CPM
   POP B
   POP H
   INX H
   MOV A,L
   ANI 0FH
   JNZ SKPLFCR
   LXI D,LFCR
   MVI C,9
   PUSH H
   CALL CPM
   POP H
SKPLFCR:
   MOV A,H
   CPI 18H
   JNZ NXTPRT
; Three blank lines
   LXI D,LFCR
   MVI C,9
   CALL CPM
   LXI D,LFCR
   MVI C,9
   CALL CPM
   LXI D,LFCR
   MVI C,9
   CALL CPM
   JMP FREEZE

EXIT:
   MVI A,00H      ; Dazzler, clear high bit turns off
   OUT 0EH
   JMP 0000H      ; return to CPM

