vnIIc/client/client.s

421 lines
9.8 KiB
ArmAsm

;;;-------------------------------------------------------------------
;;;
;;; vnIIc Client Application
;;;
;;;-------------------------------------------------------------------
PADDLE_SUPPORT = 0
MOUSE_SUPPORT = 0
.include "apple2.inc"
.include "macros.inc"
;;;---------------------------------------------------------
;;; Hi-res graphics constants/locations
;;;---------------------------------------------------------
PAGE := $E6 ; Active hires plotting page (Applesoft)
PAGE1 := $20
PAGE2 := $40
PAGESIZE := $20 ; Size of hi-res screen in pages
;;;---------------------------------------------------------
;;; I/O
;;;---------------------------------------------------------
PADDL0 := $C064
PTRIG := $C070
;;;---------------------------------------------------------
;;; ROM routines
;;;---------------------------------------------------------
HCLR := $F3F2 ; Clear current hires screen to black
;;;---------------------------------------------------------
;;; Other
;;;---------------------------------------------------------
MAX_SLOT := 7 ; Maximum slot # on an Apple II
;;;-------------------------------------------------------------------
;;; Protocol:
;;;-------------------------------------------------------------------
.proc Protocol
Keyboard := $00
Button0 := $10
Button1 := $11
Paddle0 := $20
Paddle1 := $21
MouseX := $30
MouseY := $31
MouseBtn := $32
Screen := $80
.endproc
;;;-------------------------------------------------------------------
;;;
;;; Client Code
;;;
;;;-------------------------------------------------------------------
.org $6000
jmp AppEntry
.include "ssc.inc"
.ifdef MOUSE_SUPPORT
.include "mouse.inc"
.endif
;;;-------------------------------------------------------------------
;;; Variables
;;;-------------------------------------------------------------------
;;; Application configuration
PSPEED: .byte SSC::BPS_115k ; Hardcoded for Apple IIc (TODO: Allow configuration)
PSLOT: .byte 2 ; Hardcoded for Apple IIc (TODO: Allow configuration)
PEXIT: .byte 0 ; Set when it's time to exit (Not Yet Implemented)
;;;---------------------------------------------------------
;;; Initialize the application, and enter the main loop
.proc AppEntry
lda PSLOT ; Use slot 2
jsr SSC::Init ; Initialize Super Serial Card
jsr InitHires ; Initialize Hi-Res graphics
jsr InitInput ; Initialize input devices
jsr MainLoop
;; fall through
.endproc
;;;---------------------------------------------------------
;;; Clean up and exit app
.proc AppExit
jsr SSC::Reset
sta LOWSCR
sta TXTSET
rts
.endproc
;;;-------------------------------------------------------------------
;;;
;;; Main loop functionality
;;;
;;;-------------------------------------------------------------------
;;;---------------------------------------------------------
.proc MainLoop
;; Handle splash image - sent without preamble
jsr ReceivePage
jsr FlipHires
;;; TODO: Sort out the protocol - should be able to send
;;; input state without receiving data
;;; jsr SSC::HasData ; Anything to read?
;;; bne :+ ; Nope
: jsr RequestPage
jsr FlipHires
jmp :- ; TODO: define an exit trigger
rts
.endproc
;;;---------------------------------------------------------
;;; Receive a hires page; no input sent.
;;;
.proc ReceivePage
ptr := $FA
lda #0 ; set up write pointer
sta ptr
lda PAGE
sta ptr+1
ldx #PAGESIZE ; plan to receive this many pages
ldy #0
: jsr SSC::Get
sta (ptr),Y
iny
bne :- ; Do a full page...
inc ptr+1
dex
bne :- ; ...as many pages as we need
rts
.endproc
;;;---------------------------------------------------------
;;; Request a hires page, sending input state along every
;;; 256 bytes.
;;;
.proc RequestPage
ptr := $FA
lda #Protocol::Screen
jsr SSC::Put
lda #0 ; data size
jsr SSC::Put
lda #0 ; set up write pointer
sta ptr
lda PAGE
sta ptr+1
ldx #PAGESIZE ; plan to receive this many pages
ldy #0
: jsr SSC::Get
sta (ptr),Y
iny
bne :- ; Do a full page...
;; Interleave to maintain responsiveness
.if 0
jsr SendInputState
.endif
inc ptr+1
dex
bne :- ; ...as many pages as we need
rts
.endproc
;;;-------------------------------------------------------------------
;;;
;;; Input device routines
;;;
;;;-------------------------------------------------------------------
;;;---------------------------------------------------------
;;; Initialize input devices and storage for detecting
;;; state transitions
.proc InitInput
.ifdef MOUSE_SUPPORT
jsr Mouse::FindMouse
.endif
rts
.endproc
;;;---------------------------------------------------------
;;; Send a full set of input state updates.
;;; Assumes time to transmit is roughly comparable to time
;;; to measure input state, therefore only sending changes is
;;; not worthwhile in most cases.
.proc SendInputState
jsr MaybeSendKeyboard
jsr SendButtons
.ifdef PADDLE_SUPPORT
jsr SendPaddles
.endif
.ifdef MOUSE_SUPPORT
jsr Mouse::SendMouse
.endif
.endproc
;;;------------------------------------------------------------
;;; Keyboard
;;; NOTE: Can't use KBDSTRB to detect key up -> key down transition
;;; since the msb can change before the key code. Instead, consider
;;; these cases:
;;;
;;; OLD STATE KBD KBDSTRB RESULT
;;; Up Up - No-op
;;; Up Down - Save and send key down
;;; Down - Up Save and send key up
;;; Down - Down Save and send key ONLY if different
;;;
last_kb: .byte 0
.proc MaybeSendKeyboard
lda last_kb
bne key_was_down
key_was_up:
;; Key was up - send only if now down.
lda KBD ; Read keyboard
bpl done ; Do nothing if it is still up.
jmp send ; Otherwise send.
key_was_down:
;; Key was down - strobe should match
;; unless the key changed or was released.
lda KBDSTRB
bmi kbdstrb_down
kbdstrb_up:
lda #0 ; Now released
jmp send
kbdstrb_down:
cmp last_kb ; Same key as last time?
beq done ; - no change, don't send.
jmp send
send: sta last_kb
lda Protocol::Keyboard
jsr SSC::Put
lda #1 ; Data size
jsr SSC::Put
lda last_kb
jsr SSC::Put
done: rts
.endproc
;;;------------------------------------------------------------
;;; Buttons
.proc SendButtons
lda Protocol::Button0
jsr SSC::Put
lda #1 ; Data size
jsr SSC::Put
lda BUTN0
jsr SSC::Put
lda Protocol::Button1
jsr SSC::Put
lda #1 ; Data size
jsr SSC::Put
lda BUTN1
jsr SSC::Put
rts
.endproc
;;;------------------------------------------------------------
;;; Paddles
.ifdef PADDLE_SUPPORT
.proc SendPaddles
lda Protocol::Paddle0
jsr SSC::Put
lda #1 ; Data size
jsr SSC::Put
ldx #0
jsr pread
tya
jsr SSC::Put
;; Need to wait 3ms between reads.
lda Protocol::Paddle1
jsr SSC::Put
lda #1 ; Data size
jsr SSC::Put
ldx #1
jsr pread
tya
jsr SSC::Put
rts
.proc pread
;; Let any previous timer reset
: lda PADDL0,x
bmi :-
;; Read paddle
lda PTRIG
ldy #0
nop
nop
: lda PADDL0,X
bpl done
iny
bne :-
done: rts
.endproc
.endproc
.endif
;;;-------------------------------------------------------------------
;;;
;;; Hi-res graphics routines
;;;
;;;-------------------------------------------------------------------
;;;---------------------------------------------------------
;;; Set up the graphics display and pointers
.proc InitHires
lda #PAGE1 ; clear page 1
sta PAGE
jsr HCLR
;; Show page 1
sta HIRES
sta TXTCLR
sta MIXCLR
sta LOWSCR
;; And set up writing to page 2
lda #PAGE2
sta PAGE
rts
.endproc
;;;---------------------------------------------------------
;;; Call when done with the current plotting page
;;; (selected in PAGE) and it will be shown and the
;;; other page will be shown.
.proc FlipHires
lda PAGE ; plotting on which page?
cmp #PAGE1
beq :+
sta HISCR ; page 2 - so show it
lda #PAGE1 ; and plot on page 1
sta PAGE
rts
: sta LOWSCR ; page 1 - so show it
lda #PAGE2 ; and plot on page 2
sta PAGE
rts
.endproc