ca65 V2.16 - Git f5e9b401 Main file : client.s Current file: client.s 000000r 1 ;;;------------------------------------------------------------------- 000000r 1 ;;; 000000r 1 ;;; vnIIc Client Application 000000r 1 ;;; 000000r 1 ;;;------------------------------------------------------------------- 000000r 1 000000r 1 PADDLE_SUPPORT = 1 000000r 1 ;;; MOUSE_SUPPORT = 1 000000r 1 000000r 1 .include "apple2.inc" 000000r 2 000000r 2 ;----------------------------------------------------------------------------- 000000r 2 ; Zero page stuff 000000r 2 000000r 2 WNDLFT := $20 ; Text window left 000000r 2 WNDWDTH := $21 ; Text window width 000000r 2 WNDTOP := $22 ; Text window top 000000r 2 WNDBTM := $23 ; Text window bottom+1 000000r 2 CH := $24 ; Cursor horizontal position 000000r 2 CV := $25 ; Cursor vertical position 000000r 2 BASL := $28 ; Text base address low 000000r 2 BASH := $29 ; Text base address high 000000r 2 INVFLG := $32 ; Normal/inverse(/flash) 000000r 2 PROMPT := $33 ; Used by GETLN 000000r 2 RNDL := $4E ; Random counter low 000000r 2 RNDH := $4F ; Random counter high 000000r 2 HIMEM := $73 ; Highest available memory address+1 000000r 2 000000r 2 ;----------------------------------------------------------------------------- 000000r 2 ; Vectors 000000r 2 000000r 2 DOSWARM := $03D0 ; DOS warmstart vector 000000r 2 BRKVec := $03F0 ; Break vector 000000r 2 SOFTEV := $03F2 ; Vector for warm start 000000r 2 PWREDUP := $03F4 ; This must be = EOR #$A5 of SOFTEV+1 000000r 2 000000r 2 ;----------------------------------------------------------------------------- 000000r 2 ; Hardware 000000r 2 000000r 2 ; Keyboard input 000000r 2 KBD := $C000 ; Read keyboard 000000r 2 KBDSTRB := $C010 ; Clear keyboard strobe 000000r 2 000000r 2 ; 80 column video switches 000000r 2 CLR80COL:= $C000 ; Disable 80 column store 000000r 2 SET80COL:= $C001 ; Enable 80 column store 000000r 2 RD80COL := $C018 ; >127 if 80 column store enabled 000000r 2 RD80VID := $C01F ; >127 if 80 column video enabled 000000r 2 000000r 2 ; Character set switches 000000r 2 CLRALTCHAR := $C00E ; Normal Apple II char set 000000r 2 SETALTCHAR := $C00F ; Norm/inv LC, no flash 000000r 2 ALTCHARSET := $C01E ; >127 if alt charset switched in 000000r 2 000000r 2 ; Language card switches 000000r 2 RDLCBNK2:= $C011 ; >127 if LC bank 2 in use 000000r 2 RDLCRAM := $C012 ; >127 if LC is read enabled 000000r 2 ROMIN := $C081 ; Swap in D000-FFFF ROM 000000r 2 LCBANK2 := $C083 ; Swap in LC bank 2 000000r 2 LCBANK1 := $C08B ; Swap in LC bank 1 000000r 2 000000r 2 ; Video mode switches 000000r 2 TXTCLR := $C050 ; Display graphics 000000r 2 TXTSET := $C051 ; Display text 000000r 2 MIXCLR := $C052 ; Disable 4 lines of text 000000r 2 MIXSET := $C053 ; Enable 4 lines of text 000000r 2 LOWSCR := $C054 ; Page 1 000000r 2 HISCR := $C055 ; Page 2 000000r 2 LORES := $C056 ; Lores graphics 000000r 2 HIRES := $C057 ; Hires graphics 000000r 2 000000r 2 ; Game controller 000000r 2 BUTN0 := $C061 ; Open-Apple Key 000000r 2 BUTN1 := $C062 ; Closed-Apple Key 000000r 2 000000r 1 000000r 1 .include "macros.inc" 000000r 2 ;;;--------------------------------------------------------- 000000r 2 ;;; 000000r 2 ;;; Generic Macros 000000r 2 ;;; 000000r 2 ;;;--------------------------------------------------------- 000000r 2 000000r 2 .macro SaveRegisters 000000r 2 pha 000000r 2 txa 000000r 2 pha 000000r 2 tya 000000r 2 pha 000000r 2 .endmacro 000000r 2 000000r 2 .macro RestoreRegisters 000000r 2 pla 000000r 2 tay 000000r 2 pla 000000r 2 tax 000000r 2 pla 000000r 2 .endmacro 000000r 2 000000r 1 000000r 1 ;;;--------------------------------------------------------- 000000r 1 ;;; Hi-res graphics constants/locations 000000r 1 ;;;--------------------------------------------------------- 000000r 1 000000r 1 PAGE := $E6 ; Active hires plotting page (Applesoft) 000000r 1 PAGE1 := $20 000000r 1 PAGE2 := $40 000000r 1 000000r 1 PAGESIZE := $20 ; Size of hi-res screen in pages 000000r 1 000000r 1 ;;;--------------------------------------------------------- 000000r 1 ;;; ROM routines 000000r 1 ;;;--------------------------------------------------------- 000000r 1 000000r 1 PREAD := $FB1E ; Monitor paddle reading routine, call 000000r 1 ; with paddle # in X, returns value in Y 000000r 1 000000r 1 HCLR := $F3F2 ; Clear current hires screen to black 000000r 1 000000r 1 ;;;--------------------------------------------------------- 000000r 1 ;;; Other 000000r 1 ;;;--------------------------------------------------------- 000000r 1 000000r 1 MAX_SLOT := 7 ; Maximum slot # on an Apple II 000000r 1 000000r 1 ZP_PTR := $FA ; Write cursor location on zero page 000000r 1 000000r 1 ;;;------------------------------------------------------------------- 000000r 1 ;;; Protocol: 000000r 1 ;;;------------------------------------------------------------------- 000000r 1 000000r 1 .proc Protocol 000000r 1 Keyboard := $00 000000r 1 000000r 1 Button0 := $10 000000r 1 Button1 := $11 000000r 1 000000r 1 Paddle0 := $20 000000r 1 Paddle1 := $21 000000r 1 000000r 1 MouseX := $30 000000r 1 MouseY := $31 000000r 1 MouseBtn := $32 000000r 1 000000r 1 Screen := $80 000000r 1 .endproc 000000r 1 000000r 1 000000r 1 ;;;------------------------------------------------------------------- 000000r 1 ;;; 000000r 1 ;;; Client Code 000000r 1 ;;; 000000r 1 ;;;------------------------------------------------------------------- 000000r 1 000000r 1 .org $6000 006000 1 4C 5B 60 jmp AppEntry 006003 1 006003 1 .include "ssc.inc" 006003 2 ;;;------------------------------------------------------------------- 006003 2 ;;; 006003 2 ;;; Serial port routines 006003 2 ;;; 006003 2 ;;; (based on ADTPro) 006003 2 ;;; 006003 2 ;;;------------------------------------------------------------------- 006003 2 006003 2 .proc SSC 006003 2 006003 2 ;;;--------------------------------------------------------- 006003 2 ;;; Super Serial constants/locations 006003 2 ;;;--------------------------------------------------------- 006003 2 006003 2 ;;; These get incremented by the slot where they appear 006003 2 UACTRL = $C08B ; Control Register 006003 2 UACMND = $C08A ; Command Register 006003 2 UASTAT = $C089 ; Status Register 006003 2 UADATA = $C088 ; Data Register - incoming and outgoing data 006003 2 006003 2 ;;; Lookup table for UACTRL register, by baud rate 006003 2 006003 2 16 1E 1F 10 BPSCTRL: .byte $16,$1E,$1F,$10 ; 300, 9600, 19200, 115k (with 8 data bits, 1 stop bit, no echo) 006007 2 .enum 006007 2 BPS_300 006007 2 BPS_9600 006007 2 BPS_19200 006007 2 BPS_115k 006007 2 .endenum 006007 2 006007 2 CMND_NRDI = $0B ; Command: no parity, RTS on, DTR on, no interrupts 006007 2 006007 2 006007 2 ;;;--------------------------------------------------------- 006007 2 ;;; Initialize the SSC; slot passed in A 006007 2 006007 2 .proc Init 006007 2 0A asl ; Slot passed in A 006008 2 0A asl 006009 2 0A asl 00600A 2 0A asl ; Now $S0 00600B 2 69 88 adc #$88 ; Low byte of UADATA 00600D 2 AA tax 00600E 2 A9 0B lda #CMND_NRDI ; Command register: no parity, RTS on, DTR on, no interrupts 006010 2 9D 02 C0 sta $C002,X 006013 2 AC 58 60 ldy PSPEED ; Control register: look up by baud rate (8 data bits, 1 stop bit) 006016 2 B9 03 60 lda BPSCTRL,Y 006019 2 9D 03 C0 sta $C003,X 00601C 2 8E 3C 60 stx MOD_UADATA_1 ; Modify references to 00601F 2 8E 49 60 stx MOD_UADATA_2 ; UADATA to point at 006022 2 8E 55 60 stx MOD_UADATA_3 ; correct slot (UADATA+S0) 006025 2 E8 inx 006026 2 8E 32 60 stx MOD_UASTAT_1 ; Modify reference to 006029 2 8E 40 60 stx MOD_UASTAT_2 ; UASTAT to point at 00602C 2 8E 4D 60 stx MOD_UASTAT_3 ; correct slot (UASTAT+S0) 00602F 2 60 rts 006030 2 .endproc 006030 2 006030 2 006030 2 ;;;--------------------------------------------------------- 006030 2 ;;; Send accumulator out the serial port 006030 2 006030 2 .proc Put 006030 2 48 pha ; Push A onto the stack 006031 2 MOD_UASTAT_1 := *+1 006031 2 AD 89 C0 : lda UASTAT ; Check status bits 006034 2 29 70 and #$70 006036 2 C9 10 cmp #$10 006038 2 D0 F7 bne :- ; Output register is full, so loop 00603A 2 68 pla 00603B 2 MOD_UADATA_1 := *+1 00603B 2 8D 88 C0 sta UADATA ; Put character 00603E 2 60 rts 00603F 2 .endproc 00603F 2 MOD_UASTAT_1 := Put::MOD_UASTAT_1 00603F 2 MOD_UADATA_1 := Put::MOD_UADATA_1 00603F 2 00603F 2 ;;;--------------------------------------------------------- 00603F 2 ;;; Read a character from the serial port to the accumulator 00603F 2 00603F 2 .proc Get 00603F 2 MOD_UASTAT_2 := *+1 00603F 2 AD 89 C0 lda UASTAT ; Check status bits 006042 2 29 68 and #$68 006044 2 C9 08 cmp #$8 006046 2 D0 F7 bne Get ; Input register empty, loop 006048 2 MOD_UADATA_2 := *+1 006048 2 AD 88 C0 lda UADATA ; Get character 00604B 2 60 rts 00604C 2 .endproc 00604C 2 MOD_UASTAT_2 := Get::MOD_UASTAT_2 00604C 2 MOD_UADATA_2 := Get::MOD_UADATA_2 00604C 2 00604C 2 ;;;--------------------------------------------------------- 00604C 2 ;;; Check if the serial port has pending data 00604C 2 00604C 2 .proc HasData 00604C 2 MOD_UASTAT_3 := *+1 00604C 2 AD 89 C0 lda UASTAT ; Check status bits 00604F 2 29 68 and #$68 006051 2 C9 08 cmp #$8 006053 2 60 rts 006054 2 .endproc 006054 2 MOD_UASTAT_3 := HasData::MOD_UASTAT_3 006054 2 006054 2 006054 2 ;;;--------------------------------------------------------- 006054 2 ;;; Clean up serial port 006054 2 006054 2 .proc Reset 006054 2 MOD_UADATA_3 := *+1 006054 2 2C 88 C0 bit UADATA 006057 2 60 rts 006058 2 .endproc 006058 2 MOD_UADATA_3 := Reset::MOD_UADATA_3 006058 2 006058 2 .endproc 006058 2 006058 1 006058 1 .ifdef MOUSE_SUPPORT 006058 1 .include "mouse.inc" 006058 1 .endif 006058 1 006058 1 006058 1 ;;;------------------------------------------------------------------- 006058 1 ;;; Variables 006058 1 ;;;------------------------------------------------------------------- 006058 1 006058 1 ;;; Application configuration 006058 1 03 PSPEED: .byte SSC::BPS_115k ; Hardcoded for Apple IIc (TODO: Allow configuration) 006059 1 02 PSLOT: .byte 2 ; Hardcoded for Apple IIc (TODO: Allow configuration) 00605A 1 00 PEXIT: .byte 0 ; Set when it's time to exit (Not Yet Implemented) 00605B 1 00605B 1 00605B 1 ;;;--------------------------------------------------------- 00605B 1 ;;; Initialize the application, and enter the main loop 00605B 1 00605B 1 .proc AppEntry 00605B 1 AD 59 60 lda PSLOT ; Use slot 2 00605E 1 20 07 60 jsr SSC::Init ; Initialize Super Serial Card 006061 1 20 2B 61 jsr InitHires ; Initialize Hi-Res graphics 006064 1 20 A5 60 jsr InitInput ; Initialize input devices 006067 1 20 74 60 jsr MainLoop 00606A 1 ;; fall through 00606A 1 .endproc 00606A 1 00606A 1 ;;;--------------------------------------------------------- 00606A 1 ;;; Clean up and exit app 00606A 1 00606A 1 .proc AppExit 00606A 1 20 54 60 jsr SSC::Reset 00606D 1 8D 54 C0 sta LOWSCR 006070 1 8D 51 C0 sta TXTSET 006073 1 60 rts 006074 1 .endproc 006074 1 006074 1 ;;;------------------------------------------------------------------- 006074 1 ;;; 006074 1 ;;; Main loop functionality 006074 1 ;;; 006074 1 ;;;------------------------------------------------------------------- 006074 1 006074 1 006074 1 ;;;--------------------------------------------------------- 006074 1 .proc MainLoop 006074 1 006074 1 ;;; TODO: Sort out the protocol - should be able to send 006074 1 ;;; input state without receiving data 006074 1 ;;; jsr SSC::HasData ; Anything to read? 006074 1 ;;; bne :+ ; Nope 006074 1 006074 1 20 7E 60 : jsr ReceivePage 006077 1 ;; Input is sent every 256 bytes (32 times per page) 006077 1 20 42 61 jsr FlipHires 00607A 1 00607A 1 4C 74 60 jmp :- ; TODO: define an exit trigger 00607D 1 60 rts 00607E 1 .endproc 00607E 1 00607E 1 00607E 1 ;;;--------------------------------------------------------- 00607E 1 ;;; Pull a hi-res page down over serial 00607E 1 ;;; 00607E 1 ;;; Protocol is: 00607E 1 ;;; * Recieve 256 bytes (graphic data) 00607E 1 ;;; * Send 1 byte (input state) 00607E 1 00607E 1 .proc ReceivePage 00607E 1 A9 80 lda #Protocol::Screen 006080 1 20 30 60 jsr SSC::Put 006083 1 A9 00 lda #0 ; data size 006085 1 20 30 60 jsr SSC::Put 006088 1 006088 1 006088 1 A9 00 lda #0 ; set up write pointer 00608A 1 85 FA sta ZP_PTR 00608C 1 A5 E6 lda PAGE 00608E 1 85 FB sta ZP_PTR+1 006090 1 A2 20 ldx #PAGESIZE ; plan to receive this many pages 006092 1 A0 00 ldy #0 006094 1 006094 1 20 3F 60 : jsr SSC::Get 006097 1 91 FA sta (ZP_PTR),Y 006099 1 C8 iny 00609A 1 D0 F8 bne :- ; Do a full page... 00609C 1 00609C 1 ;; Interleave to maintain responsiveness 00609C 1 20 A6 60 jsr SendInputState 00609F 1 00609F 1 E6 FB inc ZP_PTR+1 0060A1 1 CA dex 0060A2 1 D0 F0 bne :- ; ...as many pages as we need 0060A4 1 60 rts 0060A5 1 .endproc 0060A5 1 0060A5 1 0060A5 1 ;;;------------------------------------------------------------------- 0060A5 1 ;;; 0060A5 1 ;;; Input device routines 0060A5 1 ;;; 0060A5 1 ;;;------------------------------------------------------------------- 0060A5 1 0060A5 1 ;;;--------------------------------------------------------- 0060A5 1 ;;; Initialize input devices and storage for detecting 0060A5 1 ;;; state transitions 0060A5 1 0060A5 1 .proc InitInput 0060A5 1 0060A5 1 .ifdef MOUSE_SUPPORT 0060A5 1 jsr Mouse::FindMouse 0060A5 1 .endif 0060A5 1 0060A5 1 60 rts 0060A6 1 .endproc 0060A6 1 0060A6 1 0060A6 1 ;;;--------------------------------------------------------- 0060A6 1 ;;; Send a full set of input state updates. 0060A6 1 0060A6 1 ;;; Assumes time to transmit is roughly comparable to time 0060A6 1 ;;; to measure input state, therefore only sending changes is 0060A6 1 ;;; not worthwhile in most cases. 0060A6 1 0060A6 1 .proc SendInputState 0060A6 1 20 B0 60 jsr MaybeSendKeyboard 0060A9 1 20 E3 60 jsr SendButtons 0060AC 1 0060AC 1 .ifdef PADDLE_SUPPORT 0060AC 1 20 04 61 jsr SendPaddles 0060AF 1 .endif 0060AF 1 0060AF 1 .ifdef MOUSE_SUPPORT 0060AF 1 jsr SendMouse 0060AF 1 .endif 0060AF 1 0060AF 1 .endproc 0060AF 1 0060AF 1 0060AF 1 ;;;------------------------------------------------------------ 0060AF 1 ;;; Keyboard 0060AF 1 0060AF 1 ;;; NOTE: Can't use KBDSTRB to detect key up -> key down transition 0060AF 1 ;;; since the msb can change before the key code. Instead, consider 0060AF 1 ;;; these cases: 0060AF 1 ;;; 0060AF 1 ;;; OLD STATE KBD KBDSTRB RESULT 0060AF 1 ;;; Up Up - No-op 0060AF 1 ;;; Up Down - Save and send key down 0060AF 1 ;;; Down - Up Save and send key up 0060AF 1 ;;; Down - Down Save and send key ONLY if different 0060AF 1 ;;; 0060AF 1 0060AF 1 00 last_kb: .byte 0 0060B0 1 0060B0 1 .proc MaybeSendKeyboard 0060B0 1 AD AF 60 lda last_kb 0060B3 1 D0 08 bne key_was_down 0060B5 1 0060B5 1 key_was_up: 0060B5 1 ;; Key was up - send only if now down. 0060B5 1 AD 00 C0 lda KBD ; Read keyboard 0060B8 1 10 28 bpl done ; Do nothing if it is still up. 0060BA 1 4C CF 60 jmp send ; Otherwise send. 0060BD 1 0060BD 1 key_was_down: 0060BD 1 ;; Key was down - strobe should match 0060BD 1 ;; unless the key changed or was released. 0060BD 1 AD 10 C0 lda KBDSTRB 0060C0 1 30 05 bmi kbdstrb_down 0060C2 1 0060C2 1 kbdstrb_up: 0060C2 1 A9 00 lda #0 ; Now released 0060C4 1 4C CF 60 jmp send 0060C7 1 0060C7 1 kbdstrb_down: 0060C7 1 CD AF 60 cmp last_kb ; Same key as last time? 0060CA 1 F0 16 beq done ; - no change, don't send. 0060CC 1 4C CF 60 jmp send 0060CF 1 0060CF 1 8D AF 60 send: sta last_kb 0060D2 1 A5 00 lda Protocol::Keyboard 0060D4 1 20 30 60 jsr SSC::Put 0060D7 1 A9 01 lda #1 ; Data size 0060D9 1 20 30 60 jsr SSC::Put 0060DC 1 AD AF 60 lda last_kb 0060DF 1 20 30 60 jsr SSC::Put 0060E2 1 0060E2 1 60 done: rts 0060E3 1 .endproc 0060E3 1 0060E3 1 ;;;------------------------------------------------------------ 0060E3 1 ;;; Buttons 0060E3 1 0060E3 1 .proc SendButtons 0060E3 1 0060E3 1 A5 10 lda Protocol::Button0 0060E5 1 20 30 60 jsr SSC::Put 0060E8 1 A9 01 lda #1 ; Data size 0060EA 1 20 30 60 jsr SSC::Put 0060ED 1 AD 61 C0 lda BUTN0 0060F0 1 20 30 60 jsr SSC::Put 0060F3 1 0060F3 1 A5 11 lda Protocol::Button1 0060F5 1 20 30 60 jsr SSC::Put 0060F8 1 A9 01 lda #1 ; Data size 0060FA 1 20 30 60 jsr SSC::Put 0060FD 1 AD 62 C0 lda BUTN1 006100 1 20 30 60 jsr SSC::Put 006103 1 006103 1 60 rts 006104 1 .endproc 006104 1 006104 1 ;;;------------------------------------------------------------ 006104 1 ;;; Paddles 006104 1 006104 1 .ifdef PADDLE_SUPPORT 006104 1 .proc SendPaddles 006104 1 006104 1 A5 20 lda Protocol::Paddle0 006106 1 20 30 60 jsr SSC::Put 006109 1 A9 01 lda #1 ; Data size 00610B 1 20 30 60 jsr SSC::Put 00610E 1 00610E 1 A2 00 ldx #0 006110 1 20 1E FB jsr PREAD 006113 1 98 tya 006114 1 20 30 60 jsr SSC::Put 006117 1 006117 1 ;; Assumes at least 11 cycles to send, so 006117 1 ;; timer has a chance to reset. 006117 1 006117 1 A5 21 lda Protocol::Paddle1 006119 1 20 30 60 jsr SSC::Put 00611C 1 A9 01 lda #1 ; Data size 00611E 1 20 30 60 jsr SSC::Put 006121 1 006121 1 A2 01 ldx #1 006123 1 20 1E FB jsr PREAD 006126 1 98 tya 006127 1 20 30 60 jsr SSC::Put 00612A 1 00612A 1 60 rts 00612B 1 .endproc 00612B 1 .endif 00612B 1 00612B 1 ;;;------------------------------------------------------------------- 00612B 1 ;;; 00612B 1 ;;; Hi-res graphics routines 00612B 1 ;;; 00612B 1 ;;;------------------------------------------------------------------- 00612B 1 00612B 1 ;;;--------------------------------------------------------- 00612B 1 ;;; Set up the graphics display and pointers 00612B 1 00612B 1 .proc InitHires 00612B 1 A9 20 lda #PAGE1 ; clear page 1 00612D 1 85 E6 sta PAGE 00612F 1 20 F2 F3 jsr HCLR 006132 1 006132 1 20 42 61 jsr FlipHires ; then show it and flip to 2 006135 1 8D 57 C0 sta HIRES 006138 1 8D 50 C0 sta TXTCLR 00613B 1 8D 52 C0 sta MIXCLR 00613E 1 8D 54 C0 sta LOWSCR 006141 1 006141 1 60 rts 006142 1 .endproc 006142 1 006142 1 006142 1 ;;;--------------------------------------------------------- 006142 1 ;;; Call when done with the current plotting page 006142 1 ;;; (selected in PAGE) and it will be shown and the 006142 1 ;;; other page will be shown. 006142 1 006142 1 .proc FlipHires 006142 1 A5 E6 lda PAGE ; plotting on which page? 006144 1 C9 20 cmp #PAGE1 006146 1 F0 08 beq :+ 006148 1 006148 1 8D 55 C0 sta HISCR ; page 2 - so show it 00614B 1 A9 20 lda #PAGE1 ; and plot on page 1 00614D 1 85 E6 sta PAGE 00614F 1 60 rts 006150 1 006150 1 8D 54 C0 : sta LOWSCR ; page 1 - so show it 006153 1 A9 40 lda #PAGE2 ; and plot on page 2 006155 1 85 E6 sta PAGE 006157 1 60 rts 006158 1 .endproc 006158 1