; ---------------------------------------------------------------------------
; CTC Tests: Cursor -> NMI
; (c) 2013 SyX
; ---------------------------------------------------------------------------

    include "firmware.i"
    include "hardware.i"
    include "supergrafx.i"
    include "macros.i"

; ---------------------------------------------------------------------------
    ORG  $4000
inicio
    ; Disable firmware and install NMI
    DI
    IM   1
    
    ; Set mode 1
    LD   BC,$7F8D
    OUT  (C),C

    ; Install VBlank Interrupt
    LD   A,$C3
    LD   HL,vblank_interrupt
    LD   ($0038),A
    LD   ($0039),HL
    
    ; Install NMI handler
;    LD   A,$C3
    LD   HL,nmi_interrupt
    LD   ($0066),A
    LD   ($0067),HL
    EI

    ; Copy font to $8000
    LD   HL,font
    LD   DE,$8000
    LD   BC,end_font - font
    LDIR

    ; Initialize timer
    LD   HL,(init_cursor_cnt)
    LD   (cursor_cnt),HL

    ; Intialize Cursor Height
    LD   HL,0 * 256 + 7             ; 8 scanlines (0-7)
    LD   BC,CRTC_SELECT + REG_0A
    OUT  (C),C
    INC  B
    OUT  (C),H
    DEC  B
    INC  C
    OUT  (C),C
    INC  B
    OUT  (C),L

    ; Print timer off
    LD   IY,txt_on
    CALL print_string
    
    ; Wait to no key is pressed
    CALL wait_keyboard_free
    
.main_loop
    CALL wait_vblank
    HALT
    CALL read_keyboard
    OR   A
    JR   Z,.main_loop
    ; Comprobar teclas
.check_up
    BIT  JOY_UP,A
    JR   Z,.check_down
    LD   A,(.sm_timer_enable + 1)
    OR   A
    JP   Z,.end_main_loop

    LD   HL,(cursor_cnt)
.sm_sub40
    LD   BC,40
    SBC  HL,BC
    LD   (cursor_cnt),HL

    ; Program new cursor in the CRTC
    LD   BC,CRTC_SELECT + REG_0E
    OUT  (C),C
    INC  B
    OUT  (C),H
    DEC  B
    INC  C
    OUT  (C),C
    INC  B
    OUT  (C),L

    ; Reenable CTC
    LD   BC,CTC_TIM1
    LD   A,(start_counter)  
    OUT  (C),A
    LD   A,1                        ; Only the first time happen
    OUT  (C),A
 
    ; Convert value
    LD   DE,txt_on_timer
    CALL bin2hex
    
    LD   IY,txt_on
    CALL print_string
    
    JP   .end_main_loop

.check_down
    BIT  JOY_DOWN,A
    JR   Z,.check_left
    LD   A,(.sm_timer_enable + 1)
    OR   A
    JP   Z,.end_main_loop

    LD   HL,(cursor_cnt)
.sm_add40
    LD   BC,40
    ADD  HL,BC
    LD   (cursor_cnt),HL

    ; Program new cursor in the CRTC
    LD   BC,CRTC_SELECT + REG_0E
    OUT  (C),C
    INC  B
    OUT  (C),H
    DEC  B
    INC  C
    OUT  (C),C
    INC  B
    OUT  (C),L

    ; Reenable CTC
    LD   BC,CTC_TIM1
    LD   A,(start_counter)  
    OUT  (C),A
    LD   A,1                        ; Only the first time happen
    OUT  (C),A

    ; Convert value
    LD   DE,txt_on_timer
    CALL bin2hex
    
    LD   IY,txt_on
    CALL print_string
    JP   .end_main_loop

.check_left
    BIT  JOY_LEFT,A
    JR   Z,.check_right
    LD   A,(.sm_timer_enable + 1)
    OR   A
    JP   Z,.end_main_loop

    LD   HL,(cursor_cnt)
.sm_sub1
    LD   BC,1
    SBC  HL,BC
    LD   (cursor_cnt),HL

    ; Program new cursor in the CRTC
    LD   BC,CRTC_SELECT + REG_0E
    OUT  (C),C
    INC  B
    OUT  (C),H
    DEC  B
    INC  C
    OUT  (C),C
    INC  B
    OUT  (C),L

    ; Reenable CTC
    LD   BC,CTC_TIM1
    LD   A,(start_counter)  
    OUT  (C),A
    LD   A,1                        ; Only the first time happen
    OUT  (C),A

    ; Convert value
    LD   DE,txt_on_timer
    CALL bin2hex
    
    LD   IY,txt_on
    CALL print_string
    JP   .end_main_loop

.check_right
    BIT  JOY_RIGHT,A
    JR   Z,.check_space
    LD   A,(.sm_timer_enable + 1)
    OR   A
    JP   Z,.end_main_loop

    LD   HL,(cursor_cnt)
.sm_add1
    LD   BC,1
    ADD  HL,BC
    LD   (cursor_cnt),HL

    ; Program new cursor in the CRTC
    LD   BC,CRTC_SELECT + REG_0E
    OUT  (C),C
    INC  B
    OUT  (C),H
    DEC  B
    INC  C
    OUT  (C),C
    INC  B
    OUT  (C),L

    ; Reenable CTC
    LD   BC,CTC_TIM1
    LD   A,(start_counter)  
    OUT  (C),A
    LD   A,1                        ; Only the first time happen
    OUT  (C),A
 
    ; Convert value
    LD   DE,txt_on_timer
    CALL bin2hex
   
    LD   IY,txt_on
    CALL print_string
    JR   .end_main_loop

.check_space
    BIT  JOY_FIRE2,A
    JP   Z,.main_loop
.sm_timer_enable
    LD   A,$00
    OR   A
    JR   Z,.start_timer
.stop_timer
    CPL
    LD   (.sm_timer_enable + 1),A

    ; Disable CTC
    LD   BC,CTC_TIM1
    LD   A,(stop_counter)
    OUT  (C),A  ; Disable Timer 
 
    LD   HL,$1010
    LD   (txt_on_timer),HL
    LD   (txt_on_timer + 2),HL
    LD   IY,txt_on
    CALL print_string
    JR   .end_main_loop

.start_timer
    CPL
    LD   (.sm_timer_enable + 1),A

    ; Program new cursor in the CRTC
    LD   HL,(cursor_cnt)
    LD   BC,CRTC_SELECT + REG_0E
    OUT  (C),C
    INC  B
    OUT  (C),H
    DEC  B
    INC  C
    OUT  (C),C
    INC  B
    OUT  (C),L

    ; Enable CTC
;    LD   A,(timer_cnt)
;    LD   H,0
    LD   BC,CTC_TIM1
    LD   A,(start_counter)  
    OUT  (C),A  ; Enable Timer 
    LD   A,1                        ; Only the first time happen
    OUT  (C),A  ; Set new time constant

    LD   DE,txt_on_timer
    CALL bin2hex
    
    LD   IY,txt_on
    CALL print_string

.end_main_loop
    CALL wait_keyboard_free
    JP   .main_loop

;counter_cnt
;    DEFB 0

start_counter
    DEFB CTC_START_ICOUNTER     ; %11110111

stop_counter
    DEFB CTC_STOP_CHANNEL       ; %00000011

;init_counter_cnt
;    DEFB 1

cursor_cnt
    DEFW 0

init_cursor_cnt
    DEFW $3000

; ---------------------------------------------------------------------------
nmi_interrupt
    PUSH BC
    PUSH AF
    LD   BC,GATE_ARRAY + PEN_00 ;BORDER
    LD   A,INK + ROJO_BRILLANTE
    OUT  (C),C
    OUT  (C),A

    ; Rearm NMI for the next frame
    LD   BC,CTC_TIM1
    LD   A,(start_counter)  
    OUT  (C),A  ; Enable Timer 
    LD   A,1                        ; Only happen the first time
    OUT  (C),A  ; Set new time constant
 
    POP  AF
    POP  BC
    EI
    RETN

; ---------------------------------------------------------------------------
vblank_interrupt
    PUSH AF
    ; Test for VBlank
    LD   A,>PPI_B
    IN   A,($00)
    RRA
    JR   NC,.exit_vblank_interrupt
    PUSH BC
    LD   BC,GATE_ARRAY + PEN_00 ;BORDER
    LD   A,INK + NEGRO
    OUT  (C),C
    OUT  (C),A
    POP  BC
.exit_vblank_interrupt
    POP  AF
    EI
    RETI

; ---------------------------------------------------------------------------
; Wait vblank
wait_vblank
    WAIT_VBL
    RET

; ---------------------------------------------------------------------------
; Wait until no key is pressed
wait_keyboard_free
.loop_keyboard_free
    CALL wait_vblank
    HALT
    CALL anykey_press
    JR   C,.loop_keyboard_free
    RET
; ---------------------------------------------------------------------------
; Test if any key is pressed and mark the Carry flag.
; ---------------------------------------------------------------------------
anykey_press
    INIT_PSG_FOR_SCANKEYS
    LD   C,%01000000            ; Key line 0 - 9
.loop_anykey_press
    LD   B,>PPI_C
    OUT  (C),C                  ; Send Key line
    LD   B,>PPI_A
    IN   A,(C)                  ; Read Key line
    CPL
    AND  A
    JR   NZ,.key_press
    INC  C
    LD   A,%01001010
    CP   C
    JR   NZ,.loop_anykey_press
    JR   .no_key_press
.key_press
    SCF
    JR   .end_anykey_press
.no_key_press
    AND  A
.end_anykey_press
    RESTORE_PSG_OF_SCANKEYS
    RET
    
; ---------------------------------------------------------------------------
; Read keyboard and joystick
; RESULT:
;    A : %s3chdrlu (SPACE, TAB, COPY, SHIFT + Right/Left, Down, Right, Left, Up)
; ---------------------------------------------------------------------------
read_keyboard
    ; Select the PSG IOPORT_A
    LD   BC,PPI_A + PSG_IOPORT_A
    OUT  (C),C

    ; Operation Select PSG Register and Joystick 0 key Line 
    LD   BC,PPI_C + PPI_PSG_SELECT              ; + JOY0 <--- Puto ERROR!!! xDDD
    OUT  (C),C

    ; PSG Inactive (Needed in CPC+, because the PPI emulation do not automatically
    ; resets the Ports A, B and C to $00, when PPI Control changes (bit 7 set)
    DEFB $ED,$71                                ; OUT (C),0 

    ; Set the PPI_A in input mode
    LD   BC,PPI_CONTROL + PPI_A_INPUT
    OUT  (C),C

    LD   A,$FF
.read_cursor_keys
    ; Check Cursors Up, Down and Right
    LD   BC,PPI_C + PPI_PSG_READ + KEY_LINE0
    OUT  (C),C                  
    LD   B,>PPI_A
    IN   B,(C)                                  ; B = byte received
.cursor_up
    BIT  CURSOR_UP,B
    JR   NZ,.cursor_right
    RES  JOY_UP,A
.cursor_right
    BIT  CURSOR_RIGHT,B
    JR   NZ,.cursor_down
    RES  JOY_RIGHT,A
.cursor_down
    BIT  CURSOR_DOWN,B
    JR   NZ,.cursor_left
    RES  JOY_DOWN,A
.cursor_left
    ; Check Cursor Left
    LD   BC,PPI_C + PPI_PSG_READ + KEY_LINE1
    OUT  (C),C                  
    LD   B,>PPI_A
    IN   B,(C)                                  ; B = byte received
    BIT  CURSOR_LEFT,B
    JR   NZ,.check_space
    RES  JOY_LEFT,A

.check_space
    LD   BC,PPI_C + PPI_PSG_READ + KEY_LINE5
    OUT  (C),C                  
    LD   B,>PPI_A
    IN   B,(C)                                  ; B = byte received
    BIT  KEY_SPACE,B                            ; NTSC/PAL
    JR   NZ,.end_check_keys
    RES  JOY_FIRE2,A
    
.end_check_keys
    ; Set the PPI_A in output mode
    LD   BC,PPI_CONTROL + PPI_A_OUTPUT
    OUT  (C),C

    ; PSG Inactive (Needed in CPC+)
    DEC  B                                      ; B = PPI_C
    DEFB $ED,$71                                ; OUT (C),0 

    CPL
    RET

; ---------------------------------------------------------------------------
; Convierte de binario a ascii en hexadecimal una cadena de bytes (Baze)
; Entradas:
;    HL: Valor a convertir
;    DE: Puntero a la cadena
; ---------------------------------------------------------------------------
bin2hex
    LD   A,H
    CALL .bin2hex1
    LD   A,H
    CALL .bin2hex2
    LD   A,L
    CALL .bin2hex1
    LD   A,L
    JR   .bin2hex2
.bin2hex1
    RRA
    RRA
    RRA
    RRA
.bin2hex2
    OR   $F0
    DAA
    ADD  A,$A0
    ADC  A,$40
    SUB  A,$30
    CP   $0A
    JR   C,.continue_bin2hex
    SUB  A,7
.continue_bin2hex
    ; Guarda caracter
    LD   (DE),A
    INC  DE
    RET

; ---------------------------------------------------------------------------
; Print a string finished in 255
; IY : String
print_string
    LD   DE,$C000
.loop_print_string
    LD   A,(IY + 0)                         ; Get Tile number
    CP   255
    RET  Z                                  ; Finish???
    
    INC  IY                                 ; Tilemap++

    ; Print tile
    PUSH DE
    CALL draw_tilenumber
    POP  DE
    
    ; Next screen address
    INC  DE
    INC  DE
    
    JR   .loop_print_string

; --------------------------------------------------------------------------- 
;  A : Tile number
; DE : Screen address
draw_tilenumber
    ; Get the tile address ($8000 + tile_number << 4) 
    LD   H,%00001000
    SLA  A
    RL   H
    RLA
    RL   H
    RLA
    RL   H
    RLA
    RL   H
    LD   L,A                ; HL hold tile address

; HL : Tile address
; DE : Screen address
draw_tile
    ; 1º Scanline
    LDI
    LDI
    DEC  E
    DEC  E
    SET  3,D
    ; 2º Scanline
    LDI
    LDI
    DEC  E
    DEC  E
    SET  4,D
    ; 4º Scanline
    LDI
    LDI
    DEC  E
    DEC  E
    RES  3,D
    ; 3º Scanline
    LDI
    LDI
    DEC  E
    DEC  E
    SET  5,D
    ; 7º Scanline
    LDI
    LDI
    DEC  E
    DEC  E
    RES  4,D
    ; 5º Scanline
    LDI
    LDI
    DEC  E
    DEC  E
    SET  3,D
    ; 6º Scanline
    LDI
    LDI
    DEC  E
    DEC  E
    SET  4,D
    ; 8º Scanline
    LDI
    LDI

    RET
    
; ---------------------------------------------------------------------------
txt_on
txt_on_timer
    DEFB $10,$10,$10,$10
    DEFB 255

font
    incbin 'recursos/font.bin'
    DEFB 0,0,0,0,1,8,0,0,0,0,1,8,0,0,0,0
    DEFB 3,$c,7,$e,$e,0,$f,8,3,$c,$f,8,7,$e,0,0
end_font

; ---------------------------------------------------------------------------
    END
