;
; ppolis.txt
;
; V. Crisafulli 01/04/2014
;
; 
;=============================================================
;
; Porkpolis
;
; Overview: Use you light gun to protect the flying pig from
;           predators. But don't hit the pig!
;
;           Player 1 - Light phaser. Shoot the foes.
;
; Title Screen - A flying pig! Pull trigger to start.
; Instructions - Score replaced by text giving instructions.
;                Cycle through the backgrounds.
; Level 1      - White Moon and Green Fields               
; Level 2      - The Snowman's Graveyard
; Level 3      - Castle of Golden Faces
; Level 4      - Porkpolis
;        
;              
; Screen: Score P1 (4 rows)
;         Background
;         Pig is flying in center.
;         Player shoots foes before they hit the pig.   
;         Place last shot is marked.
;
; Page 0: Interrupts, Utility Routines, Title Screen "ppolis.txt"
; Page 1: Level 1 "level1code.txt"
; Page 2: Level 2 "level2code.txt"
; Page 3: Level 3 "level3code.txt"
; Page 4: Level 4, Ending  "level4code.txt"
; Page 7: Game Over "gameovercode.txt"
;
;=============================================================
;

.sdsctag 1.01, "Porkpolis", "Version 1.00", "VAC2014"

;---------------------------------------
; Constants 
;---------------------------------------
; This file defines constants used in the program.
; MEMORY --------------------------------------------------------------------
.DEFINE         PAGE_SIZE               $4000
.DEFINE         PAGE_0                  $0000
.DEFINE         PAGE_1                  (PAGE_0 + PAGE_SIZE)
.DEFINE         PAGE_2                  (PAGE_1 + PAGE_SIZE)
.DEFINE         PAGE_3                  (PAGE_2 + PAGE_SIZE)
.DEFINE         RAM                     $C000
.DEFINE         RAM_LEN                 $1FF8
.DEFINE         RAM_MIRROR              $E000
.DEFINE         REG_MAP_SRAM            $FFFC
.DEFINE         REG_MAP_0               $FFFD
.DEFINE         REG_MAP_1               $FFFE
.DEFINE         REG_MAP_2               $FFFF
;----------------------------------------------------------------------------

; VIDEO ---------------------------------------------------------------------
.DEFINE         VDP_DATA                $BE
.DEFINE         VDP_ADDR                $BF
.DEFINE         VDP_STATUS              $BF
.DEFINE         VRAM_TILES              $0000
.DEFINE         VRAM_BG_MAP             $3800
.DEFINE         VRAM_SPR_MAP            $3F00
.DEFINE         VRAM_SPR_LAST           208
;----------------------------------------------------------------------------
.DEFINE         VRAM_SIZE               $4000
.DEFINE         VRAM_TILE_SIZE          32            ; (8 * 8) * 4 bits = 32 bytes
;----------------------------------------------------------------------------
.DEFINE         VREG_CONFIG0            $80
.DEFINE         VREG_CONFIG1            $81
; ..
.DEFINE         VREG_BORDER_COL         $87
.DEFINE         VREG_HSCROLL            $88
.DEFINE         VREG_VSCROLL            $89
.DEFINE         VREG_LINES_CNT          $8A
;----------------------------------------------------------------------------

; INPUTS --------------------------------------------------------------------
.DEFINE         PORT_INPUT1             $DC
.DEFINE         P1_UP                   $01
.DEFINE         P1_DOWN                 $02
.DEFINE         P1_LEFT                 $04
.DEFINE         P1_RIGHT                $08
.DEFINE         P1_BUTTON1              $10
.DEFINE         P1_BUTTON2              $20
.DEFINE         P2_UP                   $40
.DEFINE         P2_DOWN                 $80
;----------------------------------------------------------------------------
.DEFINE         PORT_INPUT2             $DD
.DEFINE         P2_LEFT                 $01
.DEFINE         P2_RIGHT                $02
.DEFINE         P2_BUTTON1              $04
.DEFINE         P2_BUTTON2              $08
.DEFINE         RESET_BUTTON            $10
; Unused                                $20
.DEFINE         LIGHTGUN1               $40
.DEFINE         LIGHTGUN2               $80
;----------------------------------------------------------------------------
.DEFINE         PORT_INPUTGG            $00
.DEFINE         START_BUTTON            $80
;----------------------------------------------------------------------------

; SOUND ---------------------------------------------------------------------
.DEFINE         PORT_PSG                $7F
.DEFINE         PORT_FM_ADDR            $F0
.DEFINE         PORT_FM_DATA            $F1
.DEFINE         PORT_FM_LATCH           $F2
;----------------------------------------------------------------------------

; MISCELLANEOUS -------------------------------------------------------------
.DEFINE         PORT_NATION             $3F
.DEFINE         PORT_VLINE              $7E
.DEFINE         PORT_HLINE              $7F
;----------------------------------------------------------------------------

; HEADER --------------------------------------------------------------------
.DEFINE         HEADER                  $7FF0
.DEFINE         HEADER_ID               $7FF0 ; TMR SEGA
; ..
.DEFINE         HEADER_CHECKSUM         $7FFA
; ..
.DEFINE         HEADER_SYS_SIZE         $7FFF
;----------------------------------------------------------------------------

;----------------------------------------------------------------------------
; WORKING RAM
; 
.DEFINE         RAM_FRAME_CTR               $C000 ; Frame counter
.DEFINE         RAM_SECOND_CTR              $C001 ; Second counter
.DEFINE         RAM_GAME_STATE              $C002 ; Game state
.DEFINE         RAM_INTRO_STATE             $C003 ; Introduction state
.DEFINE         RAM_RASTER_LINE             $C004
.DEFINE         RAM_LIGHT_GUN_READ          $C005
.DEFINE         RAM_VPOS_READ               $C007 ; Target for phaser 1
.DEFINE         RAM_HPOS_READ               $C008 ; Target for phaser 1

.DEFINE         RAM_COLOR_INC               $C009 ; Color index for title screen
.DEFINE         RAM_FLOOR_WIGGLE            $C00A ; Floor wiggle index

.DEFINE         RAM_SCORE_1                 $C00B ;
.DEFINE         RAM_SCORE_2                 $C00C ;
.DEFINE         RAM_SCORE_3                 $C00D ;
.DEFINE         RAM_SCORE_4                 $C00E ;
.DEFINE         RAM_SCORE_5                 $C00F ;
.DEFINE         RAM_SCORE_6                 $C010 ;
.DEFINE         RAM_SCORE_7                 $C011 ;
.DEFINE         RAM_HEALTH_1                $C012 ;
.DEFINE         RAM_TIME_1                  $C013 ;
.DEFINE         RAM_TIME_2                  $C014 ;

.DEFINE         RAM_PREV_TRIGGER            $C015 ; 
.DEFINE         RAM_COLOR_TO_BLINK          $C016 ;

.DEFINE         RAM_FOE_1_COUNT             $C020 ;
.DEFINE         RAM_FOE_1_STATE             $C021 ;
.DEFINE         RAM_FOE_2_COUNT             $C022 ;
.DEFINE         RAM_FOE_2_STATE             $C023 ;
.DEFINE         RAM_FOE_3_COUNT             $C024 ;
.DEFINE         RAM_FOE_3_STATE             $C025 ;
.DEFINE         RAM_FOE_4_COUNT             $C026 ;
.DEFINE         RAM_FOE_4_STATE             $C027 ;
.DEFINE         RAM_FOE_5_COUNT             $C028 ;
.DEFINE         RAM_FOE_5_STATE             $C029 ;
.DEFINE         RAM_FOE_6_COUNT             $C02A ;
.DEFINE         RAM_FOE_6_STATE             $C02B ;
.DEFINE         RAM_FOE_7_COUNT             $C02C ;
.DEFINE         RAM_FOE_7_STATE             $C02D ;
.DEFINE         RAM_FOE_8_COUNT             $C02E ;
.DEFINE         RAM_FOE_8_STATE             $C02F ;

.DEFINE         RAM_COLL_X1                 $C030 ; = x1
.DEFINE         RAM_COLL_Y1                 $C031 ; = y1
.DEFINE         RAM_COLL_X2                 $C032 ; = x1
.DEFINE         RAM_COLL_Y2                 $C033 ; = y1
.DEFINE         RAM_COLL_RESULT             $C034 ; = result, $00 => miss, $01 => hit

.DEFINE         RAM_PIG_BLINK               $C035 ; Make pig blink
.DEFINE         RAM_END_TRIGGER_READY       $C036 ; Ending timer expired
.DEFINE         RAM_ENABLE_SCROLLING        $C037 ; Enable scrolling?
.DEFINE         RAM_SCROLL_CTR              $C038 ; Scroll counter
.DEFINE         RAM_COLOR_INDEX             $C039 ; Scroll counter

.DEFINE         RAM_PAUSE                   $C040 ; Paused? even=>no pause, odd=>paused

.DEFINE         RAM_SOUND_ADDR              $C042 ; Sound state
.DEFINE         RAM_SOUND_DELAY             $C044 ;

.DEFINE         RAM_VPOS                    $C100 ; Sprite data, horizontal position
.DEFINE         RAM_HPOS                    $C200 ; Sprite data, vertical position
.DEFINE         RAM_SCORE_BG                $C300 ; Score data
.DEFINE         RAM_RESTORE_COLOR           $C400 ;
.DEFINE         RAM_SOUND                   $C420 ; Sound data

;----------------------------------------------------------------------------
; CONSTANTS
.DEFINE         STATE_INTRO                 0     ; Title Screen
.DEFINE         STATE_LEVEL_1_LOAD          1     ; Level 1    
.DEFINE         STATE_LEVEL_1_RUN           2     ; Level 1    
.DEFINE         STATE_LEVEL_2_LOAD          3     ; Level 2    
.DEFINE         STATE_LEVEL_2_RUN           4     ; Level 2  
.DEFINE         STATE_LEVEL_3_LOAD          5     ; Level 3    
.DEFINE         STATE_LEVEL_3_RUN           6     ; Level 3  
.DEFINE         STATE_LEVEL_4_LOAD          7     ; Level 4    
.DEFINE         STATE_LEVEL_4_RUN           8     ; Level 4 
.DEFINE         STATE_END_LOAD              9     ; Ending    
.DEFINE         STATE_END_RUN              10     ; Ending   
.DEFINE         STATE_GAME_OVER           $7E     ; Game Over screen  
.DEFINE         STATE_GAME_OVER_WAIT      $7F     ; Game Over screen 

.DEFINE         LEVEL_TIMER_START         $09     ; Level timer
.DEFINE         LEVEL_1_HEALTH            $09     ;
.DEFINE         LEVEL_2_HEALTH            $08     ;
.DEFINE         LEVEL_3_HEALTH            $07     ;
.DEFINE         LEVEL_4_HEALTH            $06     ;

;------------------------------------------
; Assembler Defines
;------------------------------------------
.EMPTYFILL $00


; $0000 - $3FFF = bank 0
; $4000 - $7FFF = bank 1
; $8000 - $BFFF = bank 2 (not mapped for 32kb)
; $C000 - $FFFF = bank 3 (working RAM, not mapped)
.MEMORYMAP
   SLOTSIZE $4000
   SLOT 0 START $0000 
   SLOT 1 START $4000 
   SLOT 2 START $8000 
   DEFAULTSLOT 0
.ENDME

; 1 Megabit Game
.ROMBANKMAP
   BANKSTOTAL 8
   BANKSIZE $4000
   BANKS 8
.ENDRO


;----------------------------------------
; Vectors at $0000
.BANK 0 SLOT 0
.org $0000

;----------------------------------------
; Power up vector
.org $0000
   JP start_sms                ; Jump to start of program

;---------------------------------------
; H blank/ V blank
.org $0038
   IN a,(VDP_STATUS)           ; Acknowledge the IRQ
   CALL h_blank                ; v_blank routine (no raster line interrupts)
   EI                          ; Enable interrupts
   RETN                        ; End interrupt

;---------------------------------------
; Pause interrupt vector 
.org $0066
   CALL pause_routine          ; Jump to pause routine
   RETN                        ; End interrupt


;---------------------------------------
; The program starts here.
start_sms:
   DI                          ; Disable interrupts
   IM 1                        ; Set SMS interrupt mode
   LD sp,$dff0                 ; Set the stack pointer

; Initialize registers
   LD c,VDP_ADDR               ; Set c <- VDP_ADDR
   LD b,$80                    ; VDP Register
   LD hl, vdp_init_data        ; Set initialization data.
  
vdp_init_loop:
   LD a,(hl)                   ; Load data
   OUT (c), a                  ; Write to VDP
   LD a, b                     ; Load register number
   OUT (c), a                  ;

   INC hl                      ; Increment data pointer
   INC a                       ; Increment B
   LD b, a                     ;
   CP $8B                      ; Stop before 11
   JP NZ, vdp_init_loop        ; Loop

; 0 = enable, ON
; 1 = disable, OFF
; 7654 3210
; +----------- Expansion slot OFF
;  +---------- Cart slot      ON
;   +--------- Card slot      OFF
;    +-------- Work RAM       ON
;      +------ BIOS ROM       OFF
;       +----- I/O Chip       ON
;        +---- Unknown        ON
;         +--- Unknown        ON
;
; BIOS ROM leaves this data in $C000
; But Genesis does not have BIOS ROM so don't rely on it.
; $A8 is the default for SMS carts.
;
   LD a, $A8                   ; Set memory enables
   OUT ($3E), a                ;

; Set up the title screen.
; Clear RAM! SMS BIOS clears RAM, but Genesis does not.
   LD hl, $C000                ;
   LD b, 255                   ;
   CALL tool_clear_RAM_loop    ; Clear working RAM
   CALL clear_vram             ; Clear VRAM
   CALL clear_palette_data     ; Clear palette data

   CALL intro_load_sprites     ; Load intro sprites

   CALL load_tile_data_000     ; Load tile data
   CALL load_bg_data_000       ;
   CALL load_palette_data_000  ; Load colors

   CALL copy_song_to_RAM       ; Load sound

; Turn on the screen
   CALL VDP_on                 ; Screen on

   EI                          ; Enable interrupts

main_loop:
   NOP
   NOP
   NOP
   JP main_loop

; VDP Register initialization data.
; This is kinda important.
vdp_init_data:
 .db $76 $00 $FF $FF $FF $FF $FB $00 $00 $00 $00
; .db $76 $00 $FF $FF $FF $FF $FB $00 $00 $00 $FF - no hline interrupts. Scan line register is $C2 in MEKA.

;---------------------------------------
; reset_check (16 lines)
;
; Reset button pressed?
;---------------------------------------
reset_check:
   IN a, ($DD)                 ; Reset active?
   AND $10                     ;
   CP $00                      ;
   JP NZ, reset_check_end      ;

   CALL VDP_off                ; Screen off

   LD hl, $C000                ;
   LD b, 255                   ;
   CALL tool_clear_RAM_loop    ; Clear working RAM
   CALL clear_vram             ; Clear VRAM
   CALL clear_palette_data     ; Clear palette data

   CALL intro_load_sprites     ; Load intro sprites

   CALL load_tile_data_000     ; Load tile data
   CALL load_bg_data_000       ;
   CALL load_palette_data_000  ; Load colors

   CALL VDP_on                 ; Screen on

reset_check_end:
   RET                         ; End subroutine
;---------------------------------------

;----------------------------------------------------------
; sound_on (9 lines)
;
; Turn the sound on
;----------------------------------------------------------
sound_off:
   LD a, $9F                   ; PSG1 off
   OUT (PORT_PSG), a           ;

   LD a, $BF                   ; PSG2 off
   OUT (PORT_PSG), a           ;

   LD a, $DF                   ; PSG3 off
   OUT (PORT_PSG), a           ;

   LD a, $FF                   ; Noise off
   OUT (PORT_PSG), a           ;
   
   RET                         ; End subroutine
;---------------------------------------


;----------------------------------------------------------
; VDP_off (6 lines)
;
; Turn the screen off
;----------------------------------------------------------
VDP_off:
   LD c,VDP_ADDR               ; Write to VDP

   LD a,$80                    ; Set VDP 1 <- $82
   OUT (c),a                   ; IE  = 0
   LD a,$81                    ;
   OUT (c),a                   ;

   RET                         ;
;---------------------------------------

;----------------------------------------------------------
; VDP_on (6 lines)
;
; Turn the screen on
;
; NOTE: Interrupt occurs almost as 
;       soon as you write to register 1.
;----------------------------------------------------------
VDP_on:
   LD c,VDP_ADDR               ; Write to VDP 

; Turn on the screen
   LD a, $E0                   ; Set VDP 1 <- $E2
   OUT (c), a                  ; IE  = 1  
   LD a, $81                   ; 
   OUT (c), a                  ; 

   RET                         ;
;---------------------------------------

;------------------------------------------
; h_blank (15 lines)
;
; This interrupt occurs each horizontal line drawn.
; On line 191, vertical blank interrupt.
;
; v_blank - Most of game takes place here
;------------------------------------------
h_blank:
; Read and increment raster line
   IN a, ($7E)                ; Load
   LD (RAM_RASTER_LINE), a    ; Store
   LD hl, $00B0               ; before 191 ($BF) -
   LD de, $0000               ; RAM_RASTER_LINE =
   LD e, a                    ;
   SBC hl, de                 ; Z => line last
                              ; P => line above last
   JP Z, line_last            ;
   JP P, valid_h_blank        ;
   JP h_blank_end             ;

; Scan line interrupt
valid_h_blank:
   LD a, (RAM_LIGHT_GUN_READ) ; Reading from light gun?
   CP $7F                     ; $7F => ON, $00 => OFF
   CALL Z, h_light_gun_read   ; 

   LD a, (RAM_ENABLE_SCROLLING) ; Perform level 1 background scanline effects
   CP $7F                     ; 
   CALL Z, level_1_bgfx_001   ;

   JP h_blank_end             ;

; Vertical blank period
line_last:
   CALL v_blank               ; Perform v_blank operations

h_blank_end:
   RET                        ; End subroutine


;---------------------------------------
; v_blank (41 lines)
;
; Vertical blank interrupt
; 
v_blank:
   LD a, (RAM_PAUSE)          ; Paused?
   AND $01                    ;
   CP $01                     ; 0 is active
   JP Z, vblank_end2          ; Skip all if paused

   LD a, (RAM_FRAME_CTR)      ; Frame counter
   INC a                      ;
   LD (RAM_FRAME_CTR), a      ;
 
   CP 60                      ; 60 frames per second
   JP NZ, vblank_sec_skip     ;

   LD a, $00                  ; Clear A
   LD (RAM_FRAME_CTR), a      ;  
   
   LD a, (RAM_SECOND_CTR)     ; Seconds counter
   INC a                      ;
   LD (RAM_SECOND_CTR), a     ;

vblank_sec_skip:
   LD a, (RAM_GAME_STATE)     ; Load the game state
   CP STATE_INTRO             ; Is this the introduction?
   CALL Z, title_screen_000   ; Title Screen

   LD a, (RAM_GAME_STATE)     ; Load the game state
   CP STATE_LEVEL_1_LOAD      ; 
   CALL Z, level_1_load_001   ;

   LD a, (RAM_GAME_STATE)     ; Load the game state
   CP STATE_LEVEL_1_RUN       ; 
   CALL Z, level_1_run_001    ;

   LD a, (RAM_GAME_STATE)     ; Load the game state
   CP STATE_LEVEL_2_LOAD      ; 
   CALL Z, wrapper_level_2_load ;

   LD a, (RAM_GAME_STATE)     ; Load the game state
   CP STATE_LEVEL_2_RUN      ; 
   CALL Z, wrapper_level_2_run ;

   LD a, (RAM_GAME_STATE)     ; Load the game state
   CP STATE_LEVEL_3_LOAD      ; 
   CALL Z, wrapper_level_3_load ;

   LD a, (RAM_GAME_STATE)     ; Load the game state
   CP STATE_LEVEL_3_RUN      ; 
   CALL Z, wrapper_level_3_run ;

   LD a, (RAM_GAME_STATE)     ; Load the game state
   CP STATE_LEVEL_4_LOAD      ; 
   CALL Z, wrapper_level_4_load ;

   LD a, (RAM_GAME_STATE)     ; Load the game state
   CP STATE_LEVEL_4_RUN      ; 
   CALL Z, wrapper_level_4_run ;

   LD a, (RAM_GAME_STATE)     ; Load the game state
   CP STATE_GAME_OVER         ; 
   CALL Z, wrapper_gameover_load ;

   LD a, (RAM_GAME_STATE)     ; Load the game state
   CP STATE_GAME_OVER_WAIT    ; 
   CALL Z, wrapper_gameover_run ;

   LD a, (RAM_GAME_STATE)     ; Load the game state
   CP STATE_END_LOAD          ; 
   CALL Z, wrapper_end_load   ;

   LD a, (RAM_GAME_STATE)     ; Load the game state
   CP STATE_END_RUN           ; 
   CALL Z, wrapper_end_run    ;

vblank_end:
   CALL get_dpad_input        ; Use d-pad if not light phaser.
   CALL light_gun_restore     ; Restore if light phaser was previously in use.
   CALL check_light_phaser    ; Is the light phaser in use?
   CALL increment_scroll      ;

vblank_end2:
   CALL reset_scroll          ; Reset scroll
   CALL reset_check           ; Reset pressed?
   CALL sound_play_RAM        ;

   RET                        ; End subroutine

;---------------------------------------
; pause_routine (4 lines)
;
;---------------------------------------
pause_routine:
   LD a, (RAM_PAUSE)          ; Load
   INC a                      ; Increment
   LD (RAM_PAUSE), a          ; Store
 
   RET                        ; End subroutine
;---------------------------------------

;----------------------------------------
; Wrapper Routines - Set page then call
; (40 lines)
;----------------------------------------
wrapper_level_2_load:
   LD a, $02                  ; Set page   
   LD ($FFFF), a              ;
   CALL level_2_load_002      ; Call subroutine
   RET                        ; End subroutine

wrapper_level_2_run:
   LD a, $02                  ; Set page   
   LD ($FFFF), a              ;
   CALL level_2_run_002       ; Call subroutine
   RET                        ; End subroutine

wrapper_level_3_load:
   LD a, $03                  ; Set page   
   LD ($FFFF), a              ;
   CALL level_3_load_003      ; Call subroutine
   RET                        ; End subroutine

wrapper_level_3_run:
   LD a, $03                  ; Set page   
   LD ($FFFF), a              ;
   CALL level_3_run_003       ; Call subroutine
   RET                        ; End subroutine

wrapper_level_4_load:
   LD a, $04                  ; Set page   
   LD ($FFFF), a              ;
   CALL level_4_load_004      ; Call subroutine
   RET                        ; End subroutine

wrapper_level_4_run:
   LD a, $04                  ; Set page   
   LD ($FFFF), a              ;
   CALL level_4_run_004       ; Call subroutine
   RET                        ; End subroutine

wrapper_gameover_load:
   LD a, $07                  ; Set page   
   LD ($FFFF), a              ;
   CALL level_X_load_00X      ; Call subroutine
   RET                        ; End subroutine

wrapper_gameover_run:
   LD a, $07                  ; Set page   
   LD ($FFFF), a              ;
   CALL level_X_run_00X       ; Call subroutine
   RET                        ; End subroutine

wrapper_end_load:
   LD a, $04                  ; Set page   
   LD ($FFFF), a              ;
   CALL level_4_load_002      ; Call subroutine
   RET                        ; End subroutine

wrapper_end_run:
   LD a, $04                  ; Set page   
   LD ($FFFF), a              ;
   CALL level_4_run_end       ; Call subroutine
   RET                        ; End subroutine

;----------------------------------------
; Utility Routines
;----------------------------------------

;----------------------------------------
; reset_scroll (5 lines)
; 
;----------------------------------------
reset_scroll:
   LD a, $00                  ; No scroll
   OUT (VDP_ADDR),a           ; 
   LD a,$88                   ; R8 is horizontal scroll
   OUT (VDP_ADDR),a           ;

   RET                        ; End subroutine
;----------------------------------------

;----------------------------------------
; increment_scroll (12 lines)
;
;----------------------------------------
increment_scroll:
   LD a, (RAM_GAME_STATE)     ; Load the game state
   CP STATE_LEVEL_1_RUN       ; 
   JP Z, incscr_level001      ;

   LD a, (RAM_SCROLL_CTR)     ; Update scroll counter
   DEC a                      ;
   LD (RAM_SCROLL_CTR), a     ;

   RET                        ; End subroutine

incscr_level001:
   LD a, (RAM_SECOND_CTR)     ; Update scroll counter
   DEC a                      ;
   LD (RAM_SCROLL_CTR), a     ;
   RET                        ; End subroutine
;----------------------------------------

;----------------------------------------
; check_light_phaser (14 lines)
;
; During vblank, set up the light phaser.
;----------------------------------------
; When the light phaser trigger is pressed,
;    Turn the screen white 
;    Start reading light gun info
check_light_phaser:
   LD a, (RAM_PREV_TRIGGER)    ; Load previous value
   AND $10                     ; Mask unwanted bits
   CP $10                      ; Detect 1 -> 0
   JP NZ, vb_skip2             ; Skip if trigger was depressed

   IN a, ($DC)                 ; Read light gun 1
   AND $10                     ; Mask unwanted bits
   CP $10                      ; Detect 1 -> 0
   JP Z, vb_skip2              ; Skip if trigger is off

   CALL tool_white_screen      ; Turn screen white
   LD a, $7F                   ; Turn on read routine
   LD (RAM_LIGHT_GUN_READ), a  ;

vb_skip2:
   IN a, ($DC)                 ; Read light gun 1
   LD (RAM_PREV_TRIGGER), a    ; Store previous value

   RET                          ; End subroutine

;------------------------------------------
; light_gun_restore (8 lines)
;
; At vblank, restore screen if needed.
;------------------------------------------
light_gun_restore:
   LD a, (RAM_LIGHT_GUN_READ)   ;
   CP $7F                       ;
   JP NZ, lgr_skip_000          ;

   CALL tool_restore_screen     ;
   LD a, $00                    ; Clear A
   LD (RAM_LIGHT_GUN_READ), a   ; Reading for light gun is done

   CALL copy_song_to_RAM002     ; Bang!

lgr_skip_000:
   RET                         ; End subroutine
;------------------------------------------

;------------------------------------------
; h_light_gun_read (24 lines) 
;
; Color 1 -> 0? Light phaser sees white.
; If so,
;    Read hpos
;    Read vpos
; If raster line 191, 
;    Restore color
;    Calculate hpos
;
; NOTE: For MEKA emulator, 
;       HPOS = (most recent HPOS * 2) - 32
;       VPOS = most recent VPOS
;------------------------------------------
h_light_gun_read:
; Color 1 -> 0? Light phaser sees white.
   IN a, ($DD)                 ; Read I/O
   CP $BF                      ; 1->0 for bit 6?
   JP NZ, hlgr_next2           ; Skip if no light seen

; If so,
;    Read hpos
;    Read vpos
   IN a, ($7E)                  ; vertical position
   SUB 4                        ; a = a - 4 (center)
   LD (RAM_VPOS_READ), a        ;
   IN a, ($7F)                  ; horizontal postion  
   RLA                          ; a <- a << 1  <- a * 2
   SUB 32                       ; a <= a - 32
   SUB 4                        ; a = a - 4 (center)
   LD (RAM_HPOS_READ), a        ;
  
; Place light gun coordinates to target sprite.
   LD a, (RAM_VPOS_READ)       ; Setup vpos
   LD (RAM_VPOS + 8), a        ;
   LD (RAM_VPOS + 9), a        ;
   ADD a, 8                    ; A = A + 8
   LD (RAM_VPOS + 10), a       ;
   LD (RAM_VPOS + 11), a       ;

   LD a, (RAM_HPOS_READ)       ; Setup hpos
   LD (RAM_HPOS + 16), a       ;
   LD (RAM_HPOS + 20), a       ;
   ADD a, 8                    ; A = A + 8
   LD (RAM_HPOS + 18), a       ;
   LD (RAM_HPOS + 22), a       ;

hlgr_next2:
   RET                          ; End subroutine

;------------------------------------------
; tool_white_screen (12 lines) 
;
; Set all colors to white ($3F)
;------------------------------------------
tool_white_screen:
   NOP                        ;
   NOP                        ;

   LD a, $00                  ; XX00
   OUT (VDP_ADDR), a          ;
   LD a, $C0                  ; C0XX 
   OUT (VDP_ADDR), a          ;

   LD b, 16                   ;

tws_loop:
   LD a, $3F                  ; Set A for white color
   OUT (VDP_DATA), a          ; Write to color RAM

   DEC b                      ; Loop until all color RAM is written.
   JP NZ, tws_loop            ; 
 
   RET                        ; End subroutine

;------------------------------------------
; tool_restore_screen (14 lines) 
;
; Restore colors to contents from RAM_RESTORE_COLOR
;------------------------------------------
tool_restore_screen:
   NOP                        ;
   NOP                        ;

   LD a, $00                  ; XX00
   OUT (VDP_ADDR), a          ;
   LD a, $C0                  ; C0XX 
   OUT (VDP_ADDR), a          ;

   LD b, 16                   ;
   LD hl, RAM_RESTORE_COLOR   ;

trs_loop:
   LD a, (hl)                 ; Read a color
   INC hl                     ; Increment for next read
   OUT (VDP_DATA), a          ; Write to color RAM

   DEC b                      ; Loop until all color RAM is written.
   JP NZ, trs_loop            ; 
 
   RET                        ; End subroutine

;-----------------------------------------
; get_dpad_input (48 lines)
;
; If the D-pad is connected, 
; move the target.
;-----------------------------------------
get_dpad_input:
; If directional pad is pressed, change coordinates.
   IN a, ($DC)                 ; Read light gun 1 
   AND $01                     ; Mask unwanted bits
   CP $00                      ;
   JP NZ, vb_skip_up           ;

   LD a, (RAM_VPOS + 8)        ; Go up with d-pad
   DEC a                       ;
   DEC a                       ;
   LD (RAM_VPOS + 8), a        ;
   LD (RAM_VPOS + 9), a        ;
   ADD a, 8                    ; A = A + 8
   LD (RAM_VPOS + 10), a       ;
   LD (RAM_VPOS + 11), a       ;

vb_skip_up:
   IN a, ($DC)                 ; Read light gun 1 
   AND $02                     ; Mask unwanted bits
   CP $00                      ;
   JP NZ, vb_skip_down         ;

   LD a, (RAM_VPOS + 8)        ; Go down with d-pad
   INC a                       ;
   INC a                       ;
   LD (RAM_VPOS + 8), a        ;
   LD (RAM_VPOS + 9), a        ;
   ADD a, 8                    ; A = A + 8
   LD (RAM_VPOS + 10), a       ;
   LD (RAM_VPOS + 11), a       ;

vb_skip_down:
   IN a, ($DC)                 ; Read light gun 1 
   AND $04                     ; Mask unwanted bits
   CP $00                      ;
   JP NZ, vb_skip_left         ;

   LD a, (RAM_HPOS + 16)       ; Go left with d-pad
   DEC a                       ;
   DEC a                       ;
   LD (RAM_HPOS + 16), a       ;
   LD (RAM_HPOS + 20), a       ;
   ADD a, 8                    ; A = A + 8
   LD (RAM_HPOS + 18), a       ;
   LD (RAM_HPOS + 22), a       ;

vb_skip_left:
   IN a, ($DC)                 ; Read light gun 1 
   AND $08                     ; Mask unwanted bits
   CP $00                      ;
   JP NZ, vb_skip_right        ;

   LD a, (RAM_HPOS + 16)       ; Go left with d-pad
   INC a                       ;
   INC a                       ;
   LD (RAM_HPOS + 16), a       ;
   LD (RAM_HPOS + 20), a       ;
   ADD a, 8                    ; A = A + 8
   LD (RAM_HPOS + 18), a       ;
   LD (RAM_HPOS + 22), a       ; 

vb_skip_right:
   RET                         ; End subroutine

;-----------------------------------------------------
; tool_clear_RAM_loop (13 lines)
;
; Set hl before calling this routine
; Set b before calling this routine
;-----------------------------------------------------
tool_clear_RAM_loop:
   LD a, $00                   ; Clear register
   LD (hl), a                  ; Clear
   INC hl                      ;
   DEC b                       ;
   CP b                        ; a - b
   JP NZ, tool_clear_RAM_loop  ;

   RET                         ; End subroutine 
;---------------------------------------

;----------------------------------------------------------
; clear_vram (16 lines)
;
; Set all of VRAM to $00
;----------------------------------------------------------
clear_vram:
; Clear VDP RAM
   LD a, $00                   ; Low address byte
   OUT (VDP_ADDR),a            ;
   LD a, $40                   ; High address byte
   OUT (VDP_ADDR),a            ;

   LD de, $4000                ; Set up counter
   
clear_vram_loop:
   LD a, $00                   ; Write $00 to all items
   OUT (VDP_DATA), a           ;
   DEC de                      ; Decrement counter
   LD a, d                     ; A <- D
   OR e                        ; A OR E
   JP NZ, clear_vram_loop      ; Loop until de is empty.

   RET                         ; End subroutine  
;---------------------------------------

;----------------------------------------------------------
; clear_palette_data (9 lines)
;
; Clear palette data to $C000.
;----------------------------------------------------------
clear_palette_data:
   LD a, $00                   ; Low address byte
   OUT (VDP_ADDR),a            ;
   LD a, $C0                   ; High address byte
   OUT (VDP_ADDR),a            ;

   LD b, 32                    ; 32 bytes for color

cpd32_loop:
   LD a, $00                   ; Copy $00 to VDP
   OUT (VDP_DATA), a           ;
   DEC b                       ; Decrement counter
   LD a, b                     ; a <- b 
   CP $00                      ; is b zero?
   JP NZ, cpd32_loop           ; If not zero jump

   RET                         ; End subroutine  
;---------------------------------------

;----------------------------------------------------------
; load_tile_data_000 (20 lines)
;
; Load tile data to $0000.
;----------------------------------------------------------
load_tile_data_000: 
   LD a, $00                   ; Low address byte
   OUT (VDP_ADDR),a            ;
   LD a, $00                   ; High address byte
   OUT (VDP_ADDR),a            ;

   LD hl, tiles_000 + 1        ; tile_data is defined below
   LD c, VDP_DATA              ; C <- $BE;   LD b, 128  

   LD e, 113                   ; Set up counter (226 tiles, 113 sets of 2 tiles)

tile_load_loopt:
   LD b, 64                    ; Write 2 tiles (64 characters)               
   CALL copy_to_vdp_loop       ; HL -> VDP, until B goes to 0

   DEC e                       ;
   JR NZ, tile_load_loopt      ; Loop until de is empty.

   RET                         ; End subroutine 
;---------------------------------------

;----------------------------------------------------------
; load_bg_data_000 (20 lines)
;
; Load background data
;----------------------------------------------------------
load_bg_data_000: 
   LD a, $FF                   ; Low address byte
   OUT (VDP_ADDR),a            ;
   LD a, $37                   ; High address byte
   OUT (VDP_ADDR),a            ;

   LD hl, background_000       ; background_000 is defined below
   LD c, VDP_DATA              ; C <- $BE;   LD b, 128  

   LD e, 24                   ; Set up counter (24 rows)

tile_load_loopbg:
   LD b, 64                    ; Write 1 row (64 characters)               
   CALL copy_to_vdp_loop       ; HL -> VDP, until B goes to 0

   DEC e                       ;
   JR NZ, tile_load_loopbg     ; Loop until de is empty.

   RET                         ; End subroutine 
;---------------------------------------

;-----------------------------------------
; intro_load_sprites (27 lines)
;
; Load sprites for this screen.
;-----------------------------------------
intro_load_sprites:
   LD a, $D0                   ; Copy sprite data to RAM
   LD (RAM_VPOS), a            ; vpos
   LD (RAM_HPOS), a            ; Hpos

;---------------------------------------

sprites_to_VRAM:
   NOP                         ; Let VRAM rest
   NOP                         ; Let VRAM rest

   LD a, $FF                   ; Low address byte
   OUT (VDP_ADDR),a            ;
   LD a, $3E                   ; High address byte
   OUT (VDP_ADDR),a            ;

   LD hl, RAM_VPOS             ; tile_data is defined below 
   LD b, 45                    ; Set up counter (vpos)
   CALL copy_to_vdp_loop       ; Copy data from HL to C 

   LD a, $7F                   ; Low address byte
   OUT (VDP_ADDR),a            ;
   LD a, $3F                   ; High address byte
   OUT (VDP_ADDR),a            ;

   LD hl, RAM_HPOS             ; tile_data is defined below 
   LD b, 89                    ; Set up counter (hpos, tile)
   CALL copy_to_vdp_loop       ; Copy data from HL to C 

   RET                         ; End subroutine
;---------------------------------------

;----------------------------------------------------------
; load_palette_data_000 (12 lines)
;
; Load palette data.
;----------------------------------------------------------
load_palette_data_000:
   LD a, $00                   ; Low address byte
   OUT (VDP_ADDR),a            ;
   LD a, $C0                   ; High address byte
   OUT (VDP_ADDR),a            ;

   LD hl, palette_000          ; 
   LD b, 32                    ;
   CALL copy_to_vdp_loop       ; HL -> VDP, until B goes to 0

   LD hl, palette_000          ; Copy restore color
   LD de, RAM_RESTORE_COLOR    ;
   LD bc, 32                   ;
   LDIR                        ;

   RET                         ; End subroutine  
;---------------------------------------

;---------------------------------------
; copy_to_vdp_loop (7 lines)
;
; Copy data from HL to VDP
;---------------------------------------
copy_to_vdp_loop:              
   LD a, (hl)                  ;
   NOP                         ;
   OUT (VDP_DATA), a           ; Write to VDP

   INC hl                      ; Increment HL
   DEC b                       ; Decrement B
   JP NZ, copy_to_vdp_loop     ;
   
   RET                         ;
;---------------------------------------

;-----------------------------------------
; xy_collision_detect (21 lines)
;
; RAM_COLL_X1     = x1
; RAM_COLL_Y1     = y1
; RAM_COLL_X2     = x1
; RAM_COLL_Y2     = y1
; RAM_COLL_RESULT = result, $00 => miss, $01 => hit
;
; Is (x2,y2) within 32x32 box with
; top corner at (x1,y1)?
;
;-----------------------------------------
xy_collision_detect:
   LD a, $00                   ; Assume miss until hit
   LD (RAM_COLL_RESULT), a     ;

   LD a, (RAM_COLL_Y1)         ; y1
   SUB 12                      ; center is 12 pixels from top corner
   LD b, a                     ;
   LD a, (RAM_COLL_Y2)         ; y2
   SUB b                       ; a = y2 - y1
   AND $E0                     ; mask unwanted bits
   CP $00                      ; If difference is less than 16 pixels, declare hit
   JP NZ, xycds_end            ;

   LD a, (RAM_COLL_X1)         ; x1
   SUB 12                      ; center is 12 pixels from top corner
   LD b, a                     ;
   LD a, (RAM_COLL_X2)         ; x2
   SUB b                       ; a = x2 - x1
   AND $E0                     ; mask unwanted bits
   CP $00                      ; If difference is less than 16 pixels, declare hit
   JP NZ, xycds_end            ;

   LD a, $7F                   ; Proven hit
   LD (RAM_COLL_RESULT), a     ;

xycds_end: 
   RET                         ; End subroutine

;-----------------------------------------
; xy_collision_detect16 (21 lines)
;
; RAM_COLL_X1     = x1
; RAM_COLL_Y1     = y1
; RAM_COLL_X2     = x1
; RAM_COLL_Y2     = y1
; RAM_COLL_RESULT = result, $00 => miss, $01 => hit
;
; Is (x2,y2) within 16x16 box with
; top corner at (x1,y1)?
;
;-----------------------------------------
xy_collision_detect16:
   LD a, $00                   ; Assume miss until hit
   LD (RAM_COLL_RESULT), a     ;

   LD a, (RAM_COLL_Y1)         ; y1
   SUB 4                       ; center is 4 pixels from top corner
   LD b, a                     ;
   LD a, (RAM_COLL_Y2)         ; y2
   SUB b                       ; a = y2 - y1
   AND $F0                     ; mask unwanted bits
   CP $00                      ; If difference is less than 16 pixels, declare hit
   JP NZ, xycds_end16          ;

   LD a, (RAM_COLL_X1)         ; x1
   SUB 4                       ; center is 4 pixels from top corner
   LD b, a                     ;
   LD a, (RAM_COLL_X2)         ; x2
   SUB b                       ; a = x2 - x1
   AND $F0                     ; mask unwanted bits
   CP $00                      ; If difference is less than 16 pixels, declare hit
   JP NZ, xycds_end16          ;

   LD a, $7F                   ; Proven hit
   LD (RAM_COLL_RESULT), a     ;

xycds_end16: 
   RET                         ; End subroutine

;---------------------------------------
; Title Screen
;---------------------------------------

;---------------------------------------
; title_screen_000 (45 lines)
;
; Make the letters blink every 1/2 second.
; Wait for a trigger press.
title_screen_000:
   PUSH AF                     ;

; Blink colors until trigger is pressed.
   IN a, ($DC)                 ; Bit 4 is trigger
   AND $10                     ;
   JP NZ, skip_next_stage      ;

   LD a, STATE_LEVEL_1_LOAD    ; Go to next state
   LD (RAM_GAME_STATE), a      ;

skip_next_stage:
   LD a, (RAM_FRAME_CTR)       ; Load frame count
   CP 30                       ; Change colors every 30 frames
   JP NZ, title_screen_000_end ;

   LD a, $00                   ; Clear A
   LD (RAM_FRAME_CTR), a       ; Clear frame counter

   LD hl, title_color_cycle    ; Base
   LD d, $00                   ;
   LD a, (RAM_COLOR_INC)       ; Offset
   LD e, a                     ;
   ADD hl, de                  ; Base + offset

; Color 1
   LD a, $02                   ; Low address byte
   OUT (VDP_ADDR),a            ;
   LD a, $C0                   ; High address byte
   OUT (VDP_ADDR),a            ;
   LD a, (hl)                  ; Read color
   OUT (VDP_DATA), a           ; Write color

; Color 2
   LD a, $0A                   ; Low address byte
   OUT (VDP_ADDR),a            ;
   LD a, $C0                   ; High address byte
   OUT (VDP_ADDR),a            ;
   INC hl                      ;
   LD a, (hl)                  ; Read color
   OUT (VDP_DATA), a           ; Write color

; Color 3
   LD a, $0B                   ; Low address byte
   OUT (VDP_ADDR),a            ;
   LD a, $C0                   ; High address byte
   OUT (VDP_ADDR),a            ;
   INC hl                      ;
   LD a, (hl)                  ; Read color
   OUT (VDP_DATA), a           ; Write color

; Handle offset
   LD hl, RAM_COLOR_INC        ;
   INC (hl)                    ; Increment
   LD a, (hl)                  ;
   CP 3                        ; Above max?
   JP NZ, title_screen_000_end ;
   LD a, $00                   ; 
   LD (RAM_COLOR_INC), a       ; Reset 

title_screen_000_end:
   POP AF                      ;
   RET                         ;

title_color_cycle:
.db $08, $0C, $0F, $08, $0C, $0F,

background_000:
.include "ppolis_title_BG.inc"

tiles_000:
.include "ppolis_title_T.inc"

palette_000:
.include "ppolis_title_P.inc"


; Sound code and data
.include "soundcode.txt"

;--------------------------------------------------------------




;----------------------------------------
; Include Files
.BANK 1 SLOT 1
.org $0000

.include "level1code.txt"

;----------------------------------------
; Include Files
.BANK 2 SLOT 2
.org $0000

.include "level2code.txt"

;----------------------------------------
; Include Files
.BANK 3 SLOT 2
.org $0000

.include "level3code.txt"

;----------------------------------------
; Include Files
.BANK 4 SLOT 2
.org $0000

.include "level4code.txt"

;----------------------------------------
; Include Files
.BANK 7 SLOT 2
.org $0000

.include "gameovercode.txt"




