;;--------------------------------------------- ;; ;; screen is split into 2 sections ;; ;; ;; screen is 39 char lines tall, 25 for main area, rest for "border". ;; we need 1 char line effectively for the scroll, (and we take this away from the border area). ;; this is split into 2 areas, one added at the end of the "border" region, and one added at the end ;; of the main region, setup so they always both add to 8 scanlines so that the vsync in the bottom ;; section is always at the same position. ;; these 2 areas are defined using CRTC register R5. ;; ;; top and bottom are the same section wrapped around. ;; bottom section wraps to the top, and has a vsync triggered in the middle of it. ;; 8 lines per char ;; ;; top section is: 6 char lines tall (6*8 = 48 scan lines) ;; R5!=0 here ;; ;; - interrupt occurs at 2nd line within this block. ;; Next one happens 52 lines later at scanline 54. Potential that 7 additional lines are added. ;; main section is 25 char lines tall (25 * 8 = 200 scanlines) ;; R5!=0 here ;; bottom section is: 7 char lines tall (7*8 = 56 scan lines) ;; R5 = 0 here org &4000 nolist scr_set_mode equ &bc0e txt_output equ &bb5a start: ld a,2 call scr_set_mode ld bc,24*80 ld d,' ' l2: inc d ld a,d cp &7f jr nz,no_char_reset ld d,' ' no_char_reset: ld a,d call txt_output dec bc ld a,b or c jr nz,l2 di im 1 ld a,&c3 ld hl,int_handler1 ld (&0038),a ld (&0039),hl ld bc,&bc07 out (c),c ld bc,&bd00+7 out (c),c call vsync_sync ld bc,&bc04 out (c),c ld bc,&bd00+14-1-1 out (c),c ei ;;--------------------------------------------- loop1: ld b,&f5 l1: in a,(c) rra jr nc,l1 call check_keys ld a,(scroll_adjustment) add a,&f5 ld (hsync2),a ld a,1 ld (fetch_values),a loop5: loop2: halt halt halt jp loop1 ;; check if a key has been pressed and perform action if it has check_keys: ;; we scan the keyboard using the hardware directly ;; ;; we do this because we have turned off firmware interrupts (so firmware ;; can't scan it for us) ;; ;; we turned off the firmware interrupts because our effect is updated every frame ;; update keyboard state call scan_keyboard ;; f8 ld a,(matrix_buffer+1) bit 3,a jp nz,scroll_up ;; f2 ld a,(matrix_buffer+1) bit 6,a jp nz,scroll_down ;; f4 ld a,(matrix_buffer+2) bit 4,a jp nz,scroll_left ;; f6 ld a,(matrix_buffer+0) bit 4,a jp nz,scroll_right ;; f7 ld a,(matrix_buffer+1) bit 2,a jp nz,scroll_up_left ;; f9 ld a,(matrix_buffer+0) bit 3,a jp nz,scroll_up_right ;; f1 ld a,(matrix_buffer+1) bit 5,a jp nz,scroll_down_left ;; f3 ld a,(matrix_buffer+0) bit 5,a jp nz,scroll_down_right ret scroll_down_right: ld c,1 call scroll_down call scroll_right ret scroll_down_left: ld c,1 call scroll_down call scroll_left ret scroll_up_left: ld c,1 call scroll_up call scroll_left ret scroll_up_right: ld c,1 call scroll_up call scroll_right ret scroll_adjustment: defb 0 scroll_right: ld a,(scroll_adjustment) xor 1 ld (scroll_adjustment),a cp 1 ret z ld hl,(scrl2) inc hl ld a,h and &3 or &30 ld h,a ld (scrl2),hl ret scroll_left: ld a,(scroll_adjustment) xor 1 ld (scroll_adjustment),a or a ret z ld hl,(scrl2) dec hl ld a,h and &3 or &30 ld h,a ld (scrl2),hl ret ;;--------------------------------------------- ;; update these seperate of display scroll_down: ld a,(vscrl_line2) inc a and &7 ld (vscrl_line2),a cp 7 ret nz ld hl,(scrl2) ld bc,40 add hl,bc ld a,h and &3 or &30 ld h,a ld (scrl2),hl ret scroll_down2: and &7 ld (vscrl_line2),a ret ;; update these seperate of display scroll_up: ld a,(vscrl_line2) dec a and &7 ld (vscrl_line2),a or a ret nz ld hl,(scrl2) or a ld bc,40 sbc hl,bc ld a,h and &3 or &30 ld h,a ld (scrl2),hl ret vsync_sync: ld b,&f5 vs1: in a,(c) rra jr nc,vs1 vs2: in a,(c) rra jr c,vs2 ret ;;----------------------------------------------------------------------------- ;; this is called 2 lines after the vsync starts. ;; vsync occurs on char line 7 ;; ;; +2 ;; +52 ;; +52 -> 106 ;; 7 lines until end of end of block = 56 lines ;; + 8 max. = 64 ;; 106-64 = 42 lines/8 = 5 lines ;; next int is 10 lines before end ;; next after that is int_handler1: push bc push hl push af ;; make vsync position no longer reachable ld bc,&bc07 out (c),c ld bc,&bdff out (c),c ld bc,&bc03 out (c),c inc b ld a,(hsync) out (c),a ;; add some lines after to do vertical scroll ld bc,&bc05 out (c),c inc b ld a,(vscrl_line) neg add a,8 out (c),a ld hl,int_handler2 ld (&0039),hl pop af pop hl pop bc ei ret ;; not used, still in top part int_handler2: push hl ld bc,&bc0c out (c),c ld hl,(scrl) inc b out (c),h ld bc,&bc0d out (c),c inc b out (c),l ld bc,&bc01 out (c),c ld bc,&bd00+40 out (c),c ld hl,int_handler3 ld (&0039),hl pop hl ei ret ;; this is int_handler3: push bc push hl push af ;; set height of this area ld bc,&bc04 out (c),c ld bc,&bd00+25-1 out (c),c ;; set scroll after ld bc,&bc05 out (c),c inc b ld a,(vscrl_line) out (c),a ld hl,int_handler4 ld (&0039),hl pop af pop hl pop bc ei ret int_handler4: push hl ld hl,int_handler5 ld (&0039),hl pop hl ei ret int_handler5: push hl ld hl,int_handler6 ld (&0039),hl pop hl ei ret int_handler6: push bc push hl push af ;; turn off border for bottom bit... ld bc,&bc01 out (c),c ld bc,&bd00 out (c),c ;; 39-25 = 14 ;; set height of this area ld bc,&bc04 out (c),c ld bc,&bd00+14-1-1 out (c),c ;; this can't be on first char line ld bc,&bc07 out (c),c ld bc,&bd00+7 out (c),c ld hl,int_handler1 ld (&0039),hl ld a,(fetch_values) or a jr z,int_handler1a ;; refresh display versions ld a,(vscrl_line2) ld (vscrl_line),a ld hl,(scrl2) ld (scrl),hl ld a,(hsync2) ld (hsync),a xor a ld (fetch_values),a int_handler1a: pop af pop hl pop bc ei ret fetch_values: defb 0 hsync: defb &85 hsync2: defb &85 scrl2: defw &3000 vscrl_line2: defb 0 vscrl_line: defb 0 scrl: defw &3000 ;;---------------------------------------------------------------------- scan_keyboard: ;; store old keyboard state ld hl,matrix_buffer ld de,old_matrix_buffer ld bc,10 ldir ;; get new keyboard state ld hl,matrix_buffer ; buffer to store matrix data ld bc,&f40e ; write PSG register index (14) to PPI port A (databus to PSG) out (c),c ld b,&f6 in a,(c) and &30 ld c,a or &C0 ; bit 7=bit 6=1 (PSG operation: write register index) out (c),a ; set PSG operation -> select PSG register 14 ;; at this point PSG will have register 14 selected. ;; any read/write operation to the PSG will act on this register. out (c),c ; bit 7=bit 6=0 (PSG operation: inactive) inc b ld a,&92 out (c),a ; write PPI control: port A: input, port B: input, port C upper: output ; port C lower: output push bc set 6,c ; bit 7=0, bit 6=1 (PSG operation: read register data) scan_key: ld b,&f6 out (c),c ;set matrix line & set PSG operation ld b,&f4 ;PPI port A (databus to/from PSG) in a,(c) ;get matrix line data from PSG register 14 cpl ;invert data: 1->0, 0->1 ;if a key/joystick button is pressed bit will be "1" ;keys that are not pressed will be "0" ld (hl),a ;write line data to buffer inc hl ;update position in buffer inc c ;update line ld a,c and &0f cp &0a ;scanned all rows? jr nz,scan_key ;no loop and get next row ;; scanned all rows pop bc ld a,&82 ;write PPI Control: Port A: Output, Port B: Input, Port C upper: output, Port C lower: output. out (c),a dec b out (c),c ;set PSG operation: bit7=0, bit 6=0 (PSG operation: inactive) ret ;;---------------------------------------------------------------------- ;; This buffer has one byte per keyboard line. ;; Each byte defines a single keyboard line, which defines ;; the state for up to 8 keys. ;; ;; A bit in a byte will be '1' if the corresponding key ;; is pressed, '0' if the key is not pressed. matrix_buffer: defs 10 ;;---------------------------------------------------------------------- old_matrix_buffer: defs 10 end start