diff --git a/client/Makefile b/client/Makefile new file mode 100644 index 0000000..57c2a19 --- /dev/null +++ b/client/Makefile @@ -0,0 +1,23 @@ + +CC65 = ~/dev/cc65/bin +CAFLAGS = --target apple2enh --list-bytes 0 +CCFLAGS = --config apple2-asm.cfg + +TARGETS = \ + client.bin + +.PHONY: clean all +all: $(TARGETS) + +HEADERS = $(wildcard *.inc) + +clean: + rm -f *.o + rm -f *.list + rm -f $(TARGETS) + +%.o: %.s $(HEADERS) + $(CC65)/ca65 $(CAFLAGS) $(DEFINES) --listing $(basename $@).list -o $@ $< + +%.bin: %.o + $(CC65)/ld65 $(CCFLAGS) -o $@ $< diff --git a/client/client.bin b/client/client.bin new file mode 100644 index 0000000..c74512b Binary files /dev/null and b/client/client.bin differ diff --git a/client/client.list b/client/client.list new file mode 100644 index 0000000..9344f50 --- /dev/null +++ b/client/client.list @@ -0,0 +1,933 @@ +ca65 V2.16 - Git f5e9b401 +Main file : client.s +Current file: client.s + +000000r 1 +000000r 1 PADDLE_SUPPORT = 1 +000000r 1 ;MOUSE_SUPPORT = 1 ; NOTE: tests for ifdef +000000r 1 +000000r 1 ;--------------------------------------------------------- +000000r 1 ; Super Serial constants/locations +000000r 1 ;--------------------------------------------------------- +000000r 1 +000000r 1 ; These get incremented by the slot where they appear +000000r 1 UACTRL = $C08B ; Control Register +000000r 1 UACMND = $C08A ; Command Register +000000r 1 UASTAT = $C089 ; Status Register +000000r 1 UADATA = $C088 ; Data Register - incoming and outgoing data +000000r 1 +000000r 1 +000000r 1 ;--------------------------------------------------------- +000000r 1 ; Hi-res graphics constants/locations +000000r 1 ;--------------------------------------------------------- +000000r 1 +000000r 1 PLOTPAGE = $E6 ; Active hires plotting page (Applesoft) +000000r 1 PLOTPAGE1 = $20 +000000r 1 PLOTPAGE2 = $40 +000000r 1 PAGESIZE = $20 ; Size of hi-res screen in pages +000000r 1 +000000r 1 +000000r 1 CLRTEXT = $C050 ;display graphics +000000r 1 SETTEXT = $C051 ;display text +000000r 1 CLRMIXED = $C052 ;clear mixed mode- enable full graphics +000000r 1 SETMIXED = $C053 ;enable graphics/text mixed mode +000000r 1 PAGE1 = $C054 ;select text/graphics page1 +000000r 1 PAGE2 = $C055 ;select text/graphics page2 +000000r 1 CLRHIRES = $C056 ;select Lo-res +000000r 1 SETHIRES = $C057 ;select Hi-res +000000r 1 +000000r 1 +000000r 1 ;--------------------------------------------------------- +000000r 1 ; Keyboard input constants/locations +000000r 1 ;--------------------------------------------------------- +000000r 1 +000000r 1 KEYBD = $C000 ; key down in bit 7; key code in lower bits +000000r 1 STROBE = $C010 ; write to clear key down state +000000r 1 OPNAPPLE = $C061 ; open apple (command) key data (read) +000000r 1 CLSAPPLE = $C062 ; closed apple (option) key data (read) +000000r 1 PB2 = $C063 ; Paddle button 2 (read) +000000r 1 PB3 = $C060 ; Paddle button 3 (read) +000000r 1 +000000r 1 +000000r 1 ;--------------------------------------------------------- +000000r 1 ; Paddle/Joystick constants/locations/routines +000000r 1 ;--------------------------------------------------------- +000000r 1 +000000r 1 PADDLE0 = $C064 ; bit 7 = status of pdl-0 timer (read) +000000r 1 PADDLE1 = $C065 ; bit 7 = status of pdl-1 timer (read) +000000r 1 PADDLE2 = $C066 ; bit 7 = status of pdl-2 timer (read) +000000r 1 PADDLE3 = $C067 ; bit 7 = status of pdl-3 timer (read) +000000r 1 PDLTRIG = $C070 ; trigger paddles +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 +000000r 1 ;-------------------------------------------------- +000000r 1 ; Mouse locations and constants +000000r 1 ;-------------------------------------------------- +000000r 1 +000000r 1 ; For READMOUSE and POSMOUSE +000000r 1 +000000r 1 MOUSE_X_LSB = $0478 ; + slot Low byte of absolute X position +000000r 1 MOUSE_X_MSB = $0578 ; + slot High byte of absolute X position +000000r 1 MOUSE_Y_LSB = $04F8 ; + slot Low byte of absolute Y position +000000r 1 MOUSE_Y_MSB = $05F8 ; + slot High byte of absolute Y position +000000r 1 MOUSE_RSV1 = $0678 ; + slot Reserved and used by the firmware +000000r 1 MOUSE_RSV2 = $06F8 ; + slot Reserved and used by the firmware +000000r 1 MOUSE_BTN = $0778 ; + slot Button 0/1 interrupt status byte +000000r 1 MOUSE_MODE = $07F8 ; + slot Mode byte +000000r 1 +000000r 1 ; For CLAMPMOUSE: +000000r 1 +000000r 1 MOUSE_CMIN_LSB = $0478 ; low byte of low clamp +000000r 1 MOUSE_CMIN_MSB = $0578 ; high byte of low clamp +000000r 1 MOUSE_CMAX_LSB = $04F8 ; low byte of high clamp +000000r 1 MOUSE_CMAX_MSB = $05F8 ; high byte of high clamp +000000r 1 +000000r 1 MOUSE_CLAMP_X = 0 ; Value for A when setting X clamp with CLAMPMOUSE +000000r 1 MOUSE_CLAMP_Y = 1 ; Value for A when setting X clamp with CLAMPMOUSE +000000r 1 +000000r 1 +000000r 1 ;--------------------------------------------------------- +000000r 1 ; Other +000000r 1 ;--------------------------------------------------------- +000000r 1 +000000r 1 SLOT_CASE = $c000 ; Firmware for slots are at $cx00 +000000r 1 MAX_SLOT = 7 ; Maximum slot # on an Apple II +000000r 1 +000000r 1 ZP = $FA ; Write cursor location on zero page +000000r 1 ESCAPE = $80 ; Unused image data byte (all black2) +000000r 1 ESCAPE2 = $FF ; Unused image data byte (all white2) +000000r 1 +000000r 1 +000000r 1 ;--------------------------------------------------------- +000000r 1 ; Generic Macros +000000r 1 ;--------------------------------------------------------- +000000r 1 +000000r 1 ;---------------------------------------- +000000r 1 .macro SaveRegisters +000000r 1 ;---------------------------------------- +000000r 1 pha +000000r 1 txa +000000r 1 pha +000000r 1 tya +000000r 1 pha +000000r 1 .endmacro +000000r 1 +000000r 1 ;---------------------------------------- +000000r 1 .macro RestoreRegisters +000000r 1 ;---------------------------------------- +000000r 1 pla +000000r 1 tay +000000r 1 pla +000000r 1 tax +000000r 1 pla +000000r 1 .endmacro +000000r 1 +000000r 1 +000000r 1 ;------------------------------------------------------------------- +000000r 1 ; +000000r 1 ; Application-level logic +000000r 1 ; +000000r 1 ;------------------------------------------------------------------- +000000r 1 +000000r 1 +000000r 1 .ORG $6000 +006000 1 +006000 1 ;--------------------------------------------------------- +006000 1 .proc APP_ENTRY +006000 1 ;--------------------------------------------------------- +006000 1 ; Initialize the application, and enter the main loop +006000 1 ;--------------------------------------------------------- +006000 1 AD C7 61 lda PSLOT ; Use slot 2 +006003 1 20 71 61 jsr INITSSC ; Initialize Super Serial Card +006006 1 20 2C 61 jsr INITHIRES ; Initialize Hi-Res graphics +006009 1 20 40 60 jsr INITINPUT ; Initialize input devices +00600C 1 20 19 60 jsr MAINLOOP +00600F 1 +00600F 1 ; fall through +00600F 1 .endproc +00600F 1 +00600F 1 ;--------------------------------------------------------- +00600F 1 .proc APP_EXIT +00600F 1 ;--------------------------------------------------------- +00600F 1 ; Clean up and exit app +00600F 1 ;--------------------------------------------------------- +00600F 1 20 BE 61 jsr RESETSSC +006012 1 8D 54 C0 sta PAGE1 +006015 1 8D 51 C0 sta SETTEXT +006018 1 60 rts +006019 1 .endproc +006019 1 +006019 1 +006019 1 ;------------------------------------------------------------------- +006019 1 ; +006019 1 ; Main loop functionality +006019 1 ; +006019 1 ;------------------------------------------------------------------- +006019 1 +006019 1 +006019 1 ;--------------------------------------------------------- +006019 1 .proc MAINLOOP +006019 1 ;--------------------------------------------------------- +006019 1 +006019 1 ; TODO: Sort out the protocol - should be able to send +006019 1 ; input state without receiving data +006019 1 ; jsr SSCHASDATA ; Anything to read? +006019 1 ; bne :+ ; Nope +006019 1 +006019 1 20 23 60 : jsr RECEIVEPAGE +00601C 1 20 43 61 jsr FLIPHIRES +00601F 1 4C 19 60 jmp :- ; TODO: define an exit trigger +006022 1 60 rts +006023 1 .endproc +006023 1 +006023 1 +006023 1 ;--------------------------------------------------------- +006023 1 .proc RECEIVEPAGE +006023 1 ;--------------------------------------------------------- +006023 1 ; Pull a hi-res page down over serial +006023 1 ; +006023 1 ; Protocol is: +006023 1 ; * Recieve 256 bytes (graphic data) +006023 1 ; * Send 1 byte (input state) +006023 1 ;--------------------------------------------------------- +006023 1 +006023 1 A9 00 lda #0 ; set up write pointer +006025 1 85 FA sta ZP +006027 1 A5 E6 lda PLOTPAGE +006029 1 85 FB sta ZP+1 +00602B 1 A2 20 ldx #PAGESIZE ; plan to receive this many pages +00602D 1 A0 00 ldy #0 +00602F 1 +00602F 1 20 A9 61 : jsr SSCGET ; TODO: look for escape codes in the sequence +006032 1 91 FA sta (ZP),Y +006034 1 C8 iny +006035 1 D0 F8 bne :- ; Do a full page... +006037 1 +006037 1 20 5E 60 jsr SENDINPUTSTATE ; brief moment to send data back upstream +00603A 1 +00603A 1 E6 FB inc ZP+1 +00603C 1 CA dex +00603D 1 D0 F0 bne :- ; ...as many pages as we need +00603F 1 60 rts +006040 1 .endproc +006040 1 +006040 1 +006040 1 ;------------------------------------------------------------------- +006040 1 ; +006040 1 ; Input device routines +006040 1 ; +006040 1 ;------------------------------------------------------------------- +006040 1 ; Protocol: +006040 1 ; $7f - $ff - key down, ASCII code + $80 +006040 1 ; otherwise, a transition: +006040 1 ; +006040 1 SIS_KBUP = $00 ; Key up +006040 1 SIS_OADOWN = $01 ; Open Apple transitioned to down +006040 1 SIS_OAUP = $02 ; Open Apple transitioned to up +006040 1 SIS_CADOWN = $03 ; Closed Apple transitioned to down +006040 1 SIS_CAUP = $04 ; Closed Apple transitioned to up +006040 1 ; +006040 1 ; $05 - $0f : reserved +006040 1 ; +006040 1 SIS_MX = $10 ; Mouse X high nibble +006040 1 SIS_MY = $20 ; Mouse Y high nibble +006040 1 SIS_PDL0 = $30 ; Paddle 0 high nibble +006040 1 SIS_PDL1 = $40 ; Paddle 1 high nibble +006040 1 ; +006040 1 ; $50 - $7e : reserved +006040 1 ; +006040 1 SIS_SYNC = $7f +006040 1 +006040 1 ;--------------------------------------------------------- +006040 1 .proc INITINPUT +006040 1 ;--------------------------------------------------------- +006040 1 ; Initialize input devices and storage for detecting +006040 1 ; state transitions +006040 1 ;--------------------------------------------------------- +006040 1 +006040 1 ; Init keyboard state +006040 1 A9 00 lda #SIS_KBUP +006042 1 8D C9 61 sta LASTKB +006045 1 +006045 1 ; Init Open/Closed Apple states +006045 1 A9 02 lda #SIS_OAUP ; NOTE: Don't store OA state as it fluctuates +006047 1 8D CA 61 sta LASTOA +00604A 1 A9 04 lda #SIS_CAUP ; NOTE: Don't store CA state as it fluctuates +00604C 1 8D CB 61 sta LASTCA +00604F 1 +00604F 1 .ifdef PADDLE_SUPPORT +00604F 1 ; Init Paddle state +00604F 1 A9 30 lda #SIS_PDL0 +006051 1 09 08 ora #8 ; Middle of range 0...15 +006053 1 8D CC 61 sta LASTP0 +006056 1 A9 40 lda #SIS_PDL1 +006058 1 09 08 ora #8 ; Middle of range 0...15 +00605A 1 8D CD 61 sta LASTP1 +00605D 1 .endif +00605D 1 +00605D 1 .ifdef MOUSE_SUPPORT +00605D 1 jsr FINDMOUSE +00605D 1 .endif +00605D 1 +00605D 1 60 rts +00605E 1 .endproc +00605E 1 +00605E 1 +00605E 1 ;--------------------------------------------------------- +00605E 1 .proc SENDINPUTSTATE +00605E 1 ;--------------------------------------------------------- +00605E 1 ; Send keyboard joystick and/or mouse state over the +00605E 1 ; serial port +00605E 1 ; +00605E 1 ; Algorithm: +00605E 1 ; - Send key state (if it changed) +00605E 1 ; - otherwise send open-apple state (if it changed) +00605E 1 ; - otherwise send closed-apple state (if it changed) +00605E 1 ; - otherwise send paddle 0 state (if it changed) +00605E 1 ; - (TODO: Mouse state) +00605E 1 ; - otherwise send sync byte +00605E 1 ;--------------------------------------------------------- +00605E 1 +00605E 1 48 8A 48 98 SaveRegisters ; Store registers +006062 1 48 +006063 1 18 clc +006064 1 +006064 1 ;-------------------------------------- +006064 1 ; Send key state, if it changed +006064 1 +006064 1 ; NOTE: Can't use STROBE to detect key up -> key down transition +006064 1 ; since the msb can change before the key code. Instead, consider +006064 1 ; these cases: +006064 1 ; +006064 1 ; OLD STATE KEYBD STROBE RESULT +006064 1 ; Up Up - No-op +006064 1 ; Up Down - Save and send key down +006064 1 ; Down - Up Save and send key up +006064 1 ; Down - Down Save and send key ONLY if different +006064 1 ; +006064 1 +006064 1 AD C9 61 lda LASTKB +006067 1 D0 0E bne KEY_WAS_DOWN +006069 1 +006069 1 KEY_WAS_UP: +006069 1 AD 00 C0 lda KEYBD ; Read keyboard +00606C 1 10 27 bpl END_KEY ; - still up +00606E 1 8D C9 61 sta LASTKB ; Down, so save it +006071 1 20 9A 61 jsr SSCPUT ; and send it +006074 1 4C 26 61 jmp DONE +006077 1 +006077 1 KEY_WAS_DOWN: +006077 1 ; key was down - strobe should match +006077 1 ; unless the key changed or was released +006077 1 AD 10 C0 lda STROBE +00607A 1 30 0B bmi STROBE_DOWN +00607C 1 STROBE_UP: +00607C 1 A9 00 lda #SIS_KBUP ; Key was released +00607E 1 8D C9 61 sta LASTKB ; so save it +006081 1 20 9A 61 jsr SSCPUT ; and send it +006084 1 4C 26 61 jmp DONE +006087 1 STROBE_DOWN: +006087 1 CD C9 61 cmp LASTKB ; Same key as last time? +00608A 1 F0 09 beq END_KEY ; - no change +00608C 1 8D C9 61 sta LASTKB ; New key, so save it +00608F 1 20 9A 61 jsr SSCPUT ; and send it +006092 1 4C 26 61 jmp DONE +006095 1 +006095 1 END_KEY: +006095 1 +006095 1 ;-------------------------------------- +006095 1 ; Send Open Apple state, if it changed +006095 1 +006095 1 ; TODO: Can simplify this code if we make the high bits the same +006095 1 ; for both OA states and bit = 0 down: lda OPNAPPLE ; ROL ; LDA #0 ; ROL ; ORA #signature +006095 1 +006095 1 TEST_OA: +006095 1 AD 61 C0 lda OPNAPPLE ; Test Open Apple state +006098 1 30 10 bmi OA_IS_DOWN +00609A 1 OA_IS_UP: +00609A 1 A9 02 lda #SIS_OAUP +00609C 1 CD CA 61 cmp LASTOA ; Changed? +00609F 1 F0 19 beq END_OA ; Nope +0060A1 1 8D CA 61 sta LASTOA ; Yes, save it / send it! +0060A4 1 20 9A 61 jsr SSCPUT +0060A7 1 4C 26 61 jmp DONE +0060AA 1 OA_IS_DOWN: +0060AA 1 A9 01 lda #SIS_OADOWN +0060AC 1 CD CA 61 cmp LASTOA ; Changed? +0060AF 1 F0 09 beq END_OA ; Nope +0060B1 1 8D CA 61 sta LASTOA ; Yes, save it / send it! +0060B4 1 20 9A 61 jsr SSCPUT +0060B7 1 4C 26 61 jmp DONE +0060BA 1 +0060BA 1 END_OA: +0060BA 1 +0060BA 1 ;-------------------------------------- +0060BA 1 ; Send Closed Apple state, if it changed +0060BA 1 +0060BA 1 TEST_CA: +0060BA 1 AD 62 C0 lda CLSAPPLE ; Has the Open Apple/Button 1 value changed? +0060BD 1 30 10 bmi CA_IS_DOWN +0060BF 1 CA_IS_UP: +0060BF 1 A9 04 lda #SIS_CAUP +0060C1 1 CD CB 61 cmp LASTCA ; Changed? +0060C4 1 F0 19 beq END_CA ; Nope +0060C6 1 8D CB 61 sta LASTCA ; Yes, save it +0060C9 1 20 9A 61 jsr SSCPUT ; and send it +0060CC 1 4C 26 61 jmp DONE +0060CF 1 CA_IS_DOWN: +0060CF 1 A9 03 lda #SIS_CADOWN +0060D1 1 CD CB 61 cmp LASTCA ; Changed? +0060D4 1 F0 09 beq END_CA ; Nope +0060D6 1 8D CB 61 sta LASTCA ; Yes, save it +0060D9 1 20 9A 61 jsr SSCPUT ; and send it +0060DC 1 4C 26 61 jmp DONE +0060DF 1 +0060DF 1 END_CA: +0060DF 1 +0060DF 1 .ifdef PADDLE_SUPPORT +0060DF 1 +0060DF 1 ;-------------------------------------- +0060DF 1 ; Send Paddle 0 state, if it changed +0060DF 1 TEST_PDL0: +0060DF 1 A2 00 ldx #0 +0060E1 1 20 1E FB jsr PREAD +0060E4 1 98 tya +0060E5 1 4A lsr ; Shift to low nibble +0060E6 1 4A lsr +0060E7 1 4A lsr +0060E8 1 4A lsr +0060E9 1 09 30 ora #SIS_PDL0 ; And mark it with the signature +0060EB 1 CD CC 61 cmp LASTP0 ; Change? +0060EE 1 F0 09 beq END_PDL0 ; Nope +0060F0 1 8D CC 61 sta LASTP0 ; Yes, save it +0060F3 1 20 9A 61 jsr SSCPUT ; and send it +0060F6 1 4C 26 61 jmp DONE +0060F9 1 END_PDL0: +0060F9 1 ; Chew up time so next paddle read will be correct +0060F9 1 ; TODO: Replace this with a "read both" strobes +0060F9 1 ; routine +0060F9 1 EA EA EA EA : .repeat 11 ; By experiment, need 11 NOPs. +0060FD 1 EA EA EA EA +006101 1 EA EA EA +006104 1 nop +006104 1 .endrep +006104 1 C8 iny +006105 1 D0 F2 bne :- +006107 1 +006107 1 ;-------------------------------------- +006107 1 ; Send Paddle 1 state, if it changed +006107 1 TEST_PDL1: +006107 1 A2 01 ldx #1 +006109 1 20 1E FB jsr PREAD +00610C 1 98 tya +00610D 1 4A lsr ; Shift to low nibble +00610E 1 4A lsr +00610F 1 4A lsr +006110 1 4A lsr +006111 1 09 40 ora #SIS_PDL1 ; And mark it with the signature +006113 1 CD CD 61 cmp LASTP1 ; Change? +006116 1 F0 09 beq END_PDL1 ; Nope +006118 1 8D CD 61 sta LASTP1 ; Yes, save it +00611B 1 20 9A 61 jsr SSCPUT ; and send it +00611E 1 4C 26 61 jmp DONE +006121 1 END_PDL1: +006121 1 ; NOTE: No need to chew time like PDL0 +006121 1 ; since data receive will make up for it; if we +006121 1 ; loop in SENDINPUTSTATE need to add it here +006121 1 +006121 1 .endif +006121 1 +006121 1 +006121 1 ;-------------------------------------- +006121 1 .ifdef MOUSE_SUPPORT +006121 1 .error "Mouse support not fully implemented" +006121 1 .endif +006121 1 +006121 1 ;-------------------------------------- +006121 1 ; No state changes so send sync byte +006121 1 +006121 1 A9 7F lda #SIS_SYNC +006123 1 20 9A 61 jsr SSCPUT +006126 1 +006126 1 DONE: +006126 1 68 A8 68 AA RestoreRegisters +00612A 1 68 +00612B 1 60 rts +00612C 1 +00612C 1 .endproc +00612C 1 +00612C 1 +00612C 1 ;------------------------------------------------------------------- +00612C 1 ; +00612C 1 ; Hi-res graphics routines +00612C 1 ; +00612C 1 ;------------------------------------------------------------------- +00612C 1 +00612C 1 ;--------------------------------------------------------- +00612C 1 .proc INITHIRES +00612C 1 ;--------------------------------------------------------- +00612C 1 ; Set up the graphics display and pointers +00612C 1 ;--------------------------------------------------------- +00612C 1 A9 20 lda #PLOTPAGE1 ; clear page 1 +00612E 1 85 E6 sta PLOTPAGE +006130 1 20 59 61 jsr CLEARHIRES +006133 1 +006133 1 20 43 61 jsr FLIPHIRES ; then show it and flip to 2 +006136 1 8D 57 C0 sta SETHIRES +006139 1 8D 50 C0 sta CLRTEXT +00613C 1 8D 52 C0 sta CLRMIXED +00613F 1 8D 54 C0 sta PAGE1 +006142 1 +006142 1 60 rts +006143 1 .endproc +006143 1 +006143 1 +006143 1 ;--------------------------------------------------------- +006143 1 .proc FLIPHIRES +006143 1 ;--------------------------------------------------------- +006143 1 ; Call when done with the current plotting page +006143 1 ; (selected in PLOTPAGE) and it will be shown and the +006143 1 ; other page will be shown. +006143 1 ;--------------------------------------------------------- +006143 1 A5 E6 lda PLOTPAGE ; plotting on which page? +006145 1 C9 20 cmp #PLOTPAGE1 +006147 1 F0 08 beq :+ +006149 1 +006149 1 8D 55 C0 sta PAGE2 ; page 2 - so show it +00614C 1 A9 20 lda #PLOTPAGE1 ; and plot on page 1 +00614E 1 85 E6 sta PLOTPAGE +006150 1 60 rts +006151 1 +006151 1 8D 54 C0 : sta PAGE1 ; page 1 - so show it +006154 1 A9 40 lda #PLOTPAGE2 ; and plot on page 2 +006156 1 85 E6 sta PLOTPAGE +006158 1 60 rts +006159 1 .endproc +006159 1 +006159 1 +006159 1 ;--------------------------------------------------------- +006159 1 .proc CLEARHIRES +006159 1 ;--------------------------------------------------------- +006159 1 ; Clear hires plotting page (selected in PLOTPAGE) to +006159 1 ; black uses ZP; not terribly efficient +006159 1 ;--------------------------------------------------------- +006159 1 A9 00 lda #0 ; Set up ZP as a pointer into the hires page +00615B 1 85 FA sta ZP +00615D 1 A5 E6 lda PLOTPAGE +00615F 1 85 FB sta ZP+1 +006161 1 A2 20 ldx #PAGESIZE ; Clear this many pages +006163 1 A9 00 lda #0 ; with black! +006165 1 A8 tay +006166 1 91 FA : sta (ZP),Y +006168 1 C8 iny +006169 1 D0 FB bne :- +00616B 1 E6 FB inc ZP+1 +00616D 1 CA dex +00616E 1 D0 F6 bne :- +006170 1 60 rts +006171 1 .endproc +006171 1 +006171 1 +006171 1 +006171 1 ;------------------------------------------------------------------- +006171 1 ; +006171 1 ; Serial port routines +006171 1 ; +006171 1 ;------------------------------------------------------------------- +006171 1 +006171 1 +006171 1 ;--------------------------------------------------------- +006171 1 .proc INITSSC +006171 1 ;--------------------------------------------------------- +006171 1 ; Initialize the SSC; slot passed in A +006171 1 ; [based on ADTPro] +006171 1 ;--------------------------------------------------------- +006171 1 0A asl ; Slot passed in A +006172 1 0A asl +006173 1 0A asl +006174 1 0A asl ; Now $S0 +006175 1 69 88 adc #$88 ; Low byte of UADATA +006177 1 AA tax +006178 1 A9 0B lda #CMND_NRDI ; Command register: no parity, RTS on, DTR on, no interrupts +00617A 1 9D 02 C0 sta $C002,X +00617D 1 AC C6 61 ldy PSPEED ; Control register: look up by baud rate (8 data bits, 1 stop bit) +006180 1 B9 C2 61 lda BPSCTRL,Y +006183 1 9D 03 C0 sta $C003,X +006186 1 8E A6 61 stx MOD_UADATA_1+1 ; Modify references to +006189 1 8E B3 61 stx MOD_UADATA_2+1 ; UADATA to point at +00618C 1 8E BF 61 stx MOD_UADATA_3+1 ; correct slot (UADATA+S0) +00618F 1 E8 inx +006190 1 8E 9C 61 stx MOD_UASTAT_1+1 ; Modify reference to +006193 1 8E AA 61 stx MOD_UASTAT_2+1 ; UASTAT to point at +006196 1 8E B7 61 stx MOD_UASTAT_3+1 ; correct slot (UASTAT+S0) +006199 1 60 rts +00619A 1 +00619A 1 .endproc +00619A 1 +00619A 1 +00619A 1 ;--------------------------------------------------------- +00619A 1 SSCPUT: +00619A 1 ;--------------------------------------------------------- +00619A 1 ; Send accumulator out the serial port +00619A 1 ; (this is a blocking call) +00619A 1 ; [based on ADTPro] +00619A 1 ;--------------------------------------------------------- +00619A 1 48 pha ; Push A onto the stack +00619B 1 MOD_UASTAT_1: +00619B 1 AD 89 C0 : lda UASTAT ; Check status bits +00619E 1 29 70 and #$70 +0061A0 1 C9 10 cmp #$10 +0061A2 1 D0 F7 bne :- ; Output register is full, so loop +0061A4 1 68 pla +0061A5 1 MOD_UADATA_1: +0061A5 1 8D 88 C0 sta UADATA ; Put character +0061A8 1 60 rts +0061A9 1 +0061A9 1 +0061A9 1 ;--------------------------------------------------------- +0061A9 1 SSCGET: +0061A9 1 ;--------------------------------------------------------- +0061A9 1 ; Read a character from the serial port to the accumulator +0061A9 1 ; (this is a blocking call) +0061A9 1 ; [based on ADTPro] +0061A9 1 ;--------------------------------------------------------- +0061A9 1 MOD_UASTAT_2: +0061A9 1 AD 89 C0 lda UASTAT ; Check status bits +0061AC 1 29 68 and #$68 +0061AE 1 C9 08 cmp #$8 +0061B0 1 D0 F7 bne SSCGET ; Input register empty, loop +0061B2 1 MOD_UADATA_2: +0061B2 1 AD 88 C0 lda UADATA ; Get character +0061B5 1 60 rts +0061B6 1 +0061B6 1 +0061B6 1 ;--------------------------------------------------------- +0061B6 1 SSCHASDATA: +0061B6 1 ;--------------------------------------------------------- +0061B6 1 ; Read a character from the serial port to the accumulator +0061B6 1 ; (this is a blocking call) +0061B6 1 ; [based on ADTPro] +0061B6 1 ;--------------------------------------------------------- +0061B6 1 MOD_UASTAT_3: +0061B6 1 AD 89 C0 lda UASTAT ; Check status bits +0061B9 1 29 68 and #$68 +0061BB 1 C9 08 cmp #$8 +0061BD 1 60 rts +0061BE 1 +0061BE 1 +0061BE 1 ;--------------------------------------------------------- +0061BE 1 RESETSSC: +0061BE 1 ;--------------------------------------------------------- +0061BE 1 ; Clean up serial port +0061BE 1 ; [based on ADTPro] +0061BE 1 ;--------------------------------------------------------- +0061BE 1 MOD_UADATA_3: +0061BE 1 2C 88 C0 bit UADATA +0061C1 1 60 rts +0061C2 1 +0061C2 1 +0061C2 1 +0061C2 1 .ifdef MOUSE_SUPPORT +0061C2 1 ;------------------------------------------------------------------- +0061C2 1 ; +0061C2 1 ; Mouse routines +0061C2 1 ; +0061C2 1 ;------------------------------------------------------------------- +0061C2 1 +0061C2 1 MOUSEPTR = $EB ; Zero page location +0061C2 1 +0061C2 1 MOUSE_MIN_X = $10 +0061C2 1 MOUSE_MAX_X = $1f +0061C2 1 MOUSE_CENTER_X = $17 +0061C2 1 MOUSE_MIN_Y = $20 +0061C2 1 MOUSE_MAX_Y = $2f +0061C2 1 MOUSE_CENTER_Y = $2f +0061C2 1 +0061C2 1 +0061C2 1 ;-------------------------------------------------- +0061C2 1 ; Macros for common mouse operations +0061C2 1 ;-------------------------------------------------- +0061C2 1 +0061C2 1 ;---------------------------------------- +0061C2 1 .macro ClampMouse axis, min, max +0061C2 1 ;---------------------------------------- +0061C2 1 ; axis: MOUSE_CLAMP_X or MOUSE_CLAMP_Y +0061C2 1 ; min: minimum value (2 byte) +0061C2 1 ; max: maximum value (2 byte) +0061C2 1 ;---------------------------------------- +0061C2 1 ; Clamp X to 0...255 +0061C2 1 lda #min +0061C2 1 sta MOUSE_CMIN_MSB +0061C2 1 lda #max +0061C2 1 sta MOUSE_CMAX_MSB +0061C2 1 lda #axis +0061C2 1 jsr CLAMPMOUSE +0061C2 1 .endmacro +0061C2 1 +0061C2 1 ;---------------------------------------- +0061C2 1 .macro PosMouse px, py +0061C2 1 ;---------------------------------------- +0061C2 1 ldx MOUSE_SLOT +0061C2 1 lda #px +0061C2 1 sta MOUSE_X_MSB,X +0061C2 1 lda #py +0061C2 1 sta MOUSE_Y_MSB,X +0061C2 1 jsr POSMOUSE +0061C2 1 .endmacro +0061C2 1 +0061C2 1 +0061C2 1 ;--------------------------------------------------------- +0061C2 1 .proc FINDMOUSE +0061C2 1 ;--------------------------------------------------------- +0061C2 1 ; Find and initialize the mouse port +0061C2 1 ;--------------------------------------------------------- +0061C2 1 +0061C2 1 ; Reference: http://home.swbell.net/rubywand/R034MOUSEPRG.TXT +0061C2 1 +0061C2 1 sei ; No interrupts while we're getting set up +0061C2 1 ; +0061C2 1 ; Step 1: Find the mouse card by scanning slots for ID bytes +0061C2 1 ; +0061C2 1 +0061C2 1 ldy #MAX_SLOT ; Start search in slot 7 +0061C2 1 +0061C2 1 TESTSLOT: +0061C2 1 sty MOUSE_SLOT ; Save for later +0061C2 1 tya +0061C2 1 clc +0061C2 1 adc #>SLOT_BASE ; Firmware is $c0 + slot +0061C2 1 sta MOD_MOUSE_ID + 2 ; Update msb of signature test +0061C2 1 ldx #MOUSEID_MAX ; This many signature bytes +0061C2 1 +0061C2 1 TESTID: +0061C2 1 lda MOUSEID_ADDR,x +0061C2 1 sta MOD_MOUSE_ID + 1 ; Update lsb of signature test +0061C2 1 MOD_MOUSE_ID: +0061C2 1 lda SLOT_BASE +0061C2 1 cmp MOUSEID_VAL,x ; Does it match the signature? +0061C2 1 bne NOMATCH ; Nope - try the next slot +0061C2 1 dex ; Yes! Keep testing +0061C2 1 bpl TESTID ; Fall through if all done +0061C2 1 jmp FOUND_MOUSE +0061C2 1 +0061C2 1 NOMATCH: +0061C2 1 dey ; Didn't match +0061C2 1 bne TESTSLOT ; Keep looking until slot 0 +0061C2 1 sty MOUSE_SLOT ; Oops, no mouse - make a note +0061C2 1 rts ; and bail +0061C2 1 +0061C2 1 ; +0061C2 1 ; Step 2: Set up indirect calling routines +0061C2 1 ; +0061C2 1 +0061C2 1 FOUND_MOUSE: +0061C2 1 ; Slot is in y +0061C2 1 +0061C2 1 tya +0061C2 1 ora #>SLOT_BASE ; Compute $Cn - needed for +0061C2 1 sta MOUSEPTR+1 ; MSB of MOUSEPTR ($Cn00) +0061C2 1 sta TOMOUSE_Cn ; X register before firmware calls +0061C2 1 sta TOMOUSE_msb ; MSB of firmware calls +0061C2 1 +0061C2 1 lda #0 +0061C2 1 sta MOUSEPTR ; LSB of MOUSEPTR ($Cn00) +0061C2 1 +0061C2 1 tya +0061C2 1 asl ; Compute $n0 - needed for +0061C2 1 asl +0061C2 1 asl +0061C2 1 asl +0061C2 1 sta TOMOUSE_n0 ; Y register before firmware calls +0061C2 1 +0061C2 1 ; +0061C2 1 ; Step 3: Configure the mouse card +0061C2 1 ; +0061C2 1 +0061C2 1 ; Initialize the mouse for use +0061C2 1 jsr INITMOUSE ; reset, clamp to 0-1023 x/y +0061C2 1 lda #1 ; mouse on, no interrupts +0061C2 1 jsr SETMOUSE ; TODO: test carry bit result (set = error) +0061C2 1 ; +0061C2 1 ; Since we want deltas, clamp and center +0061C2 1 ; +0061C2 1 ClampMouse MOUSE_CLAMP_X, MOUSE_MIN_X, MOUSE_MAX_X +0061C2 1 ClampMouse MOUSE_CLAMP_Y, MOUSE_MIN_Y, MOUSE_MAX_Y +0061C2 1 PosMouse MOUSE_CENTER_X, MOUSE_CENTER_Y +0061C2 1 +0061C2 1 cli ; Enable interrupts so mouse can function +0061C2 1 +0061C2 1 rts +0061C2 1 +0061C2 1 .endproc +0061C2 1 +0061C2 1 +0061C2 1 ;-------------------------------------------------- +0061C2 1 ; Indirect jump table for mouse firmware routines +0061C2 1 ;-------------------------------------------------- +0061C2 1 +0061C2 1 SETMOUSE: ldy #$12 +0061C2 1 jmp GOMOUSE +0061C2 1 SERVEMOUSE: ldy #$13 +0061C2 1 jmp GOMOUSE +0061C2 1 READMOUSE: ldy #$14 +0061C2 1 jmp GOMOUSE +0061C2 1 CLEARMOUSE: ldy #$15 +0061C2 1 jmp GOMOUSE +0061C2 1 POSMOUSE: ldy #$16 +0061C2 1 jmp GOMOUSE +0061C2 1 CLAMPMOUSE: ldy #$17 +0061C2 1 jmp GOMOUSE +0061C2 1 HOMEMOUSE: ldy #$18 +0061C2 1 jmp GOMOUSE +0061C2 1 INITMOUSE: ldy #$19 +0061C2 1 jmp GOMOUSE +0061C2 1 +0061C2 1 ;-------------------------------------------------- +0061C2 1 .proc GOMOUSE +0061C2 1 ;-------------------------------------------------- +0061C2 1 tax ; Preserve the value in A +0061C2 1 lda (MOUSEPTR),Y ; Get the routine entry point +0061C2 1 sta TOMOUSE_lsb ; Patch the JMP instruction +0061C2 1 txa ; Restore the value in A +0061C2 1 .endproc +0061C2 1 ; fall through +0061C2 1 +0061C2 1 ; The following operand bytes must be patched by the +0061C2 1 ; initialization code which detects the mouse. +0061C2 1 +0061C2 1 BANK = $C054 +0061C2 1 +0061C2 1 TOMOUSE: +0061C2 1 ldx #$C1 ; Set up slot in $Cn form in X +0061C2 1 ldy #$10 ; Set up slot in $n0 form in Y +0061C2 1 php ; Save interrupt state +0061C2 1 sei ; No interrupts while calling +0061C2 1 bit BANK +0061C2 1 jsr SLOT_BASE ; Go to the mouse routine +0061C2 1 plp ; Restore interrupt state +0061C2 1 rts +0061C2 1 +0061C2 1 TOMOUSE_Cn = TOMOUSE + 1 +0061C2 1 TOMOUSE_n0 = TOMOUSE + 3 +0061C2 1 TOMOUSE_lsb = TOMOUSE + 10 +0061C2 1 TOMOUSE_msb = TOMOUSE + 11 +0061C2 1 +0061C2 1 +0061C2 1 ; TODO: Turn this into a proper delta-sending routine +0061C2 1 +0061C2 1 ;-------------------------------------------------- +0061C2 1 .proc FOOMOUSE +0061C2 1 ;-------------------------------------------------- +0061C2 1 ; +0061C2 1 ;-------------------------------------------------- +0061C2 1 txa ; save x +0061C2 1 pha +0061C2 1 tya ; save y +0061C2 1 pha +0061C2 1 +0061C2 1 jsr READMOUSE +0061C2 1 +0061C2 1 jmp DONE +0061C2 1 +0061C2 1 ldx MOUSE_SLOT +0061C2 1 +0061C2 1 lda MOUSE_X_LSB,x +0061C2 1 sta LAST_MX +0061C2 1 +0061C2 1 lda MOUSE_Y_LSB,x +0061C2 1 sta LAST_MY +0061C2 1 +0061C2 1 lda LAST_MX +0061C2 1 cmp #MOUSE_CENTER_X +0061C2 1 bne SEND +0061C2 1 +0061C2 1 lda LAST_MY +0061C2 1 cmp #MOUSE_CENTER_Y +0061C2 1 beq DONE +0061C2 1 +0061C2 1 SEND: +0061C2 1 lda LAST_MX +0061C2 1 ora #SIS_MX +0061C2 1 jsr SSCPUT +0061C2 1 lda LAST_MY +0061C2 1 ora #SIS_MY +0061C2 1 jsr SSCPUT +0061C2 1 +0061C2 1 PosMouse MOUSE_CENTER_X, MOUSE_CENTER_Y +0061C2 1 +0061C2 1 DONE: +0061C2 1 pla ; restore y +0061C2 1 tay +0061C2 1 pla ; restore x +0061C2 1 tax +0061C2 1 rts +0061C2 1 +0061C2 1 .endproc +0061C2 1 +0061C2 1 .endif ; MOUSE_SUPPORT +0061C2 1 +0061C2 1 +0061C2 1 ;------------------------------------------------------------------- +0061C2 1 ; +0061C2 1 ; Lookup Tables and Variable Storage +0061C2 1 ; +0061C2 1 ;------------------------------------------------------------------- +0061C2 1 +0061C2 1 ; Lookup table for UACTRL register, by baud rate +0061C2 1 +0061C2 1 16 1E 1F 10 BPSCTRL: .byte $16,$1E,$1F,$10 ; 300, 9600, 19200, 115k (with 8 data bits, 1 stop bit, no echo) +0061C6 1 .enum +0061C6 1 BPS_300 +0061C6 1 BPS_9600 +0061C6 1 BPS_19200 +0061C6 1 BPS_115k +0061C6 1 .endenum +0061C6 1 CMND_NRDI = $0B ; Command: no parity, RTS on, DTR on, no interrupts +0061C6 1 +0061C6 1 +0061C6 1 ; Application configuration +0061C6 1 03 PSPEED: .byte BPS_115k ; Hardcoded for Apple IIc (TODO: Allow configuration) +0061C7 1 02 PSLOT: .byte 2 ; Hardcoded for Apple IIc (TODO: Allow configuration) +0061C8 1 00 PEXIT: .byte 0 ; Set when it's time to exit (Not Yet Implemented) +0061C9 1 +0061C9 1 +0061C9 1 ; Keyboard state +0061C9 1 00 LASTKB: .byte 0 +0061CA 1 00 LASTOA: .byte 0 +0061CB 1 00 LASTCA: .byte 0 +0061CC 1 +0061CC 1 .ifdef PADDLE_SUPPORT +0061CC 1 +0061CC 1 ; Paddle state +0061CC 1 00 LASTP0: .byte 0 +0061CD 1 00 LASTP1: .byte 0 +0061CE 1 +0061CE 1 .endif ; PADDLE_SUPPORT +0061CE 1 +0061CE 1 +0061CE 1 .ifdef MOUSE_SUPPORT +0061CE 1 +0061CE 1 ; Mouse +0061CE 1 MOUSE_SLOT: .byte 0 ; mouse slot, or 0 if none +0061CE 1 LAST_MX: .byte $7f +0061CE 1 LAST_MY: .byte $7f +0061CE 1 +0061CE 1 ; Mouse ID bytes +0061CE 1 MOUSEID_MAX = 4 +0061CE 1 MOUSEID_ADDR: .byte $05, $07, $0b, $0c, $fb +0061CE 1 MOUSEID_VAL: .byte $38, $18, $01, $20, $d6 +0061CE 1 +0061CE 1 .endif ; MOUSE_SUPPORT +0061CE 1 +0061CE 1 diff --git a/client/client.s b/client/client.s new file mode 100644 index 0000000..092b2f2 --- /dev/null +++ b/client/client.s @@ -0,0 +1,924 @@ + +PADDLE_SUPPORT = 1 +;MOUSE_SUPPORT = 1 ; NOTE: tests for ifdef + +;--------------------------------------------------------- +; Super Serial constants/locations +;--------------------------------------------------------- + +; These get incremented by the slot where they appear +UACTRL = $C08B ; Control Register +UACMND = $C08A ; Command Register +UASTAT = $C089 ; Status Register +UADATA = $C088 ; Data Register - incoming and outgoing data + + +;--------------------------------------------------------- +; Hi-res graphics constants/locations +;--------------------------------------------------------- + +PLOTPAGE = $E6 ; Active hires plotting page (Applesoft) +PLOTPAGE1 = $20 +PLOTPAGE2 = $40 +PAGESIZE = $20 ; Size of hi-res screen in pages + + +CLRTEXT = $C050 ;display graphics +SETTEXT = $C051 ;display text +CLRMIXED = $C052 ;clear mixed mode- enable full graphics +SETMIXED = $C053 ;enable graphics/text mixed mode +PAGE1 = $C054 ;select text/graphics page1 +PAGE2 = $C055 ;select text/graphics page2 +CLRHIRES = $C056 ;select Lo-res +SETHIRES = $C057 ;select Hi-res + + +;--------------------------------------------------------- +; Keyboard input constants/locations +;--------------------------------------------------------- + +KEYBD = $C000 ; key down in bit 7; key code in lower bits +STROBE = $C010 ; write to clear key down state +OPNAPPLE = $C061 ; open apple (command) key data (read) +CLSAPPLE = $C062 ; closed apple (option) key data (read) +PB2 = $C063 ; Paddle button 2 (read) +PB3 = $C060 ; Paddle button 3 (read) + + +;--------------------------------------------------------- +; Paddle/Joystick constants/locations/routines +;--------------------------------------------------------- + +PADDLE0 = $C064 ; bit 7 = status of pdl-0 timer (read) +PADDLE1 = $C065 ; bit 7 = status of pdl-1 timer (read) +PADDLE2 = $C066 ; bit 7 = status of pdl-2 timer (read) +PADDLE3 = $C067 ; bit 7 = status of pdl-3 timer (read) +PDLTRIG = $C070 ; trigger paddles + +PREAD = $FB1E ; Monitor paddle reading routine, call + ; with paddle # in X, returns value in Y + + +;-------------------------------------------------- +; Mouse locations and constants +;-------------------------------------------------- + +; For READMOUSE and POSMOUSE + +MOUSE_X_LSB = $0478 ; + slot Low byte of absolute X position +MOUSE_X_MSB = $0578 ; + slot High byte of absolute X position +MOUSE_Y_LSB = $04F8 ; + slot Low byte of absolute Y position +MOUSE_Y_MSB = $05F8 ; + slot High byte of absolute Y position +MOUSE_RSV1 = $0678 ; + slot Reserved and used by the firmware +MOUSE_RSV2 = $06F8 ; + slot Reserved and used by the firmware +MOUSE_BTN = $0778 ; + slot Button 0/1 interrupt status byte +MOUSE_MODE = $07F8 ; + slot Mode byte + +; For CLAMPMOUSE: + +MOUSE_CMIN_LSB = $0478 ; low byte of low clamp +MOUSE_CMIN_MSB = $0578 ; high byte of low clamp +MOUSE_CMAX_LSB = $04F8 ; low byte of high clamp +MOUSE_CMAX_MSB = $05F8 ; high byte of high clamp + +MOUSE_CLAMP_X = 0 ; Value for A when setting X clamp with CLAMPMOUSE +MOUSE_CLAMP_Y = 1 ; Value for A when setting X clamp with CLAMPMOUSE + + +;--------------------------------------------------------- +; Other +;--------------------------------------------------------- + +SLOT_CASE = $c000 ; Firmware for slots are at $cx00 +MAX_SLOT = 7 ; Maximum slot # on an Apple II + +ZP = $FA ; Write cursor location on zero page +ESCAPE = $80 ; Unused image data byte (all black2) +ESCAPE2 = $FF ; Unused image data byte (all white2) + + +;--------------------------------------------------------- +; Generic Macros +;--------------------------------------------------------- + +;---------------------------------------- +.macro SaveRegisters +;---------------------------------------- + pha + txa + pha + tya + pha +.endmacro + +;---------------------------------------- +.macro RestoreRegisters +;---------------------------------------- + pla + tay + pla + tax + pla +.endmacro + + +;------------------------------------------------------------------- +; +; Application-level logic +; +;------------------------------------------------------------------- + + +.ORG $6000 + +;--------------------------------------------------------- +.proc APP_ENTRY +;--------------------------------------------------------- +; Initialize the application, and enter the main loop +;--------------------------------------------------------- + lda PSLOT ; Use slot 2 + jsr INITSSC ; Initialize Super Serial Card + jsr INITHIRES ; Initialize Hi-Res graphics + jsr INITINPUT ; Initialize input devices + jsr MAINLOOP + + ; fall through +.endproc + +;--------------------------------------------------------- +.proc APP_EXIT +;--------------------------------------------------------- +; Clean up and exit app +;--------------------------------------------------------- + jsr RESETSSC + sta PAGE1 + sta SETTEXT + rts +.endproc + + +;------------------------------------------------------------------- +; +; Main loop functionality +; +;------------------------------------------------------------------- + + +;--------------------------------------------------------- +.proc MAINLOOP +;--------------------------------------------------------- + +; TODO: Sort out the protocol - should be able to send +; input state without receiving data +; jsr SSCHASDATA ; Anything to read? +; bne :+ ; Nope + +: jsr RECEIVEPAGE + jsr FLIPHIRES + jmp :- ; TODO: define an exit trigger + rts +.endproc + + +;--------------------------------------------------------- +.proc RECEIVEPAGE +;--------------------------------------------------------- +; Pull a hi-res page down over serial +; +; Protocol is: +; * Recieve 256 bytes (graphic data) +; * Send 1 byte (input state) +;--------------------------------------------------------- + + lda #0 ; set up write pointer + sta ZP + lda PLOTPAGE + sta ZP+1 + ldx #PAGESIZE ; plan to receive this many pages + ldy #0 + +: jsr SSCGET ; TODO: look for escape codes in the sequence + sta (ZP),Y + iny + bne :- ; Do a full page... + + jsr SENDINPUTSTATE ; brief moment to send data back upstream + + inc ZP+1 + dex + bne :- ; ...as many pages as we need + rts +.endproc + + +;------------------------------------------------------------------- +; +; Input device routines +; +;------------------------------------------------------------------- +; Protocol: +; $7f - $ff - key down, ASCII code + $80 +; otherwise, a transition: +; + SIS_KBUP = $00 ; Key up + SIS_OADOWN = $01 ; Open Apple transitioned to down + SIS_OAUP = $02 ; Open Apple transitioned to up + SIS_CADOWN = $03 ; Closed Apple transitioned to down + SIS_CAUP = $04 ; Closed Apple transitioned to up +; +; $05 - $0f : reserved +; + SIS_MX = $10 ; Mouse X high nibble + SIS_MY = $20 ; Mouse Y high nibble + SIS_PDL0 = $30 ; Paddle 0 high nibble + SIS_PDL1 = $40 ; Paddle 1 high nibble +; +; $50 - $7e : reserved +; + SIS_SYNC = $7f + +;--------------------------------------------------------- +.proc INITINPUT +;--------------------------------------------------------- +; Initialize input devices and storage for detecting +; state transitions +;--------------------------------------------------------- + +; Init keyboard state + lda #SIS_KBUP + sta LASTKB + +; Init Open/Closed Apple states + lda #SIS_OAUP ; NOTE: Don't store OA state as it fluctuates + sta LASTOA + lda #SIS_CAUP ; NOTE: Don't store CA state as it fluctuates + sta LASTCA + +.ifdef PADDLE_SUPPORT +; Init Paddle state + lda #SIS_PDL0 + ora #8 ; Middle of range 0...15 + sta LASTP0 + lda #SIS_PDL1 + ora #8 ; Middle of range 0...15 + sta LASTP1 +.endif + +.ifdef MOUSE_SUPPORT + jsr FINDMOUSE +.endif + + rts +.endproc + + +;--------------------------------------------------------- +.proc SENDINPUTSTATE +;--------------------------------------------------------- +; Send keyboard joystick and/or mouse state over the +; serial port +; +; Algorithm: +; - Send key state (if it changed) +; - otherwise send open-apple state (if it changed) +; - otherwise send closed-apple state (if it changed) +; - otherwise send paddle 0 state (if it changed) +; - (TODO: Mouse state) +; - otherwise send sync byte +;--------------------------------------------------------- + + SaveRegisters ; Store registers + clc + +;-------------------------------------- +; Send key state, if it changed + +; NOTE: Can't use STROBE to detect key up -> key down transition +; since the msb can change before the key code. Instead, consider +; these cases: +; +; OLD STATE KEYBD STROBE 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 +; + + lda LASTKB + bne KEY_WAS_DOWN + +KEY_WAS_UP: + lda KEYBD ; Read keyboard + bpl END_KEY ; - still up + sta LASTKB ; Down, so save it + jsr SSCPUT ; and send it + jmp DONE + +KEY_WAS_DOWN: + ; key was down - strobe should match + ; unless the key changed or was released + lda STROBE + bmi STROBE_DOWN +STROBE_UP: + lda #SIS_KBUP ; Key was released + sta LASTKB ; so save it + jsr SSCPUT ; and send it + jmp DONE +STROBE_DOWN: + cmp LASTKB ; Same key as last time? + beq END_KEY ; - no change + sta LASTKB ; New key, so save it + jsr SSCPUT ; and send it + jmp DONE + +END_KEY: + +;-------------------------------------- +; Send Open Apple state, if it changed + +; TODO: Can simplify this code if we make the high bits the same +; for both OA states and bit = 0 down: lda OPNAPPLE ; ROL ; LDA #0 ; ROL ; ORA #signature + +TEST_OA: + lda OPNAPPLE ; Test Open Apple state + bmi OA_IS_DOWN +OA_IS_UP: + lda #SIS_OAUP + cmp LASTOA ; Changed? + beq END_OA ; Nope + sta LASTOA ; Yes, save it / send it! + jsr SSCPUT + jmp DONE +OA_IS_DOWN: + lda #SIS_OADOWN + cmp LASTOA ; Changed? + beq END_OA ; Nope + sta LASTOA ; Yes, save it / send it! + jsr SSCPUT + jmp DONE + +END_OA: + +;-------------------------------------- +; Send Closed Apple state, if it changed + +TEST_CA: + lda CLSAPPLE ; Has the Open Apple/Button 1 value changed? + bmi CA_IS_DOWN +CA_IS_UP: + lda #SIS_CAUP + cmp LASTCA ; Changed? + beq END_CA ; Nope + sta LASTCA ; Yes, save it + jsr SSCPUT ; and send it + jmp DONE +CA_IS_DOWN: + lda #SIS_CADOWN + cmp LASTCA ; Changed? + beq END_CA ; Nope + sta LASTCA ; Yes, save it + jsr SSCPUT ; and send it + jmp DONE + +END_CA: + +.ifdef PADDLE_SUPPORT + +;-------------------------------------- +; Send Paddle 0 state, if it changed +TEST_PDL0: + ldx #0 + jsr PREAD + tya + lsr ; Shift to low nibble + lsr + lsr + lsr + ora #SIS_PDL0 ; And mark it with the signature + cmp LASTP0 ; Change? + beq END_PDL0 ; Nope + sta LASTP0 ; Yes, save it + jsr SSCPUT ; and send it + jmp DONE +END_PDL0: + ; Chew up time so next paddle read will be correct + ; TODO: Replace this with a "read both" strobes + ; routine +: .repeat 11 ; By experiment, need 11 NOPs. + nop + .endrep + iny + bne :- + +;-------------------------------------- +; Send Paddle 1 state, if it changed +TEST_PDL1: + ldx #1 + jsr PREAD + tya + lsr ; Shift to low nibble + lsr + lsr + lsr + ora #SIS_PDL1 ; And mark it with the signature + cmp LASTP1 ; Change? + beq END_PDL1 ; Nope + sta LASTP1 ; Yes, save it + jsr SSCPUT ; and send it + jmp DONE +END_PDL1: + ; NOTE: No need to chew time like PDL0 + ; since data receive will make up for it; if we + ; loop in SENDINPUTSTATE need to add it here + +.endif + + +;-------------------------------------- +.ifdef MOUSE_SUPPORT + .error "Mouse support not fully implemented" +.endif + +;-------------------------------------- +; No state changes so send sync byte + + lda #SIS_SYNC + jsr SSCPUT + +DONE: + RestoreRegisters + rts + +.endproc + + +;------------------------------------------------------------------- +; +; Hi-res graphics routines +; +;------------------------------------------------------------------- + +;--------------------------------------------------------- +.proc INITHIRES +;--------------------------------------------------------- +; Set up the graphics display and pointers +;--------------------------------------------------------- + lda #PLOTPAGE1 ; clear page 1 + sta PLOTPAGE + jsr CLEARHIRES + + jsr FLIPHIRES ; then show it and flip to 2 + sta SETHIRES + sta CLRTEXT + sta CLRMIXED + sta PAGE1 + + rts +.endproc + + +;--------------------------------------------------------- +.proc FLIPHIRES +;--------------------------------------------------------- +; Call when done with the current plotting page +; (selected in PLOTPAGE) and it will be shown and the +; other page will be shown. +;--------------------------------------------------------- + lda PLOTPAGE ; plotting on which page? + cmp #PLOTPAGE1 + beq :+ + + sta PAGE2 ; page 2 - so show it + lda #PLOTPAGE1 ; and plot on page 1 + sta PLOTPAGE + rts + +: sta PAGE1 ; page 1 - so show it + lda #PLOTPAGE2 ; and plot on page 2 + sta PLOTPAGE + rts +.endproc + + +;--------------------------------------------------------- +.proc CLEARHIRES +;--------------------------------------------------------- +; Clear hires plotting page (selected in PLOTPAGE) to +; black uses ZP; not terribly efficient +;--------------------------------------------------------- + lda #0 ; Set up ZP as a pointer into the hires page + sta ZP + lda PLOTPAGE + sta ZP+1 + ldx #PAGESIZE ; Clear this many pages + lda #0 ; with black! + tay +: sta (ZP),Y + iny + bne :- + inc ZP+1 + dex + bne :- + rts +.endproc + + + +;------------------------------------------------------------------- +; +; Serial port routines +; +;------------------------------------------------------------------- + + +;--------------------------------------------------------- +.proc INITSSC +;--------------------------------------------------------- +; Initialize the SSC; slot passed in A +; [based on ADTPro] +;--------------------------------------------------------- + asl ; Slot passed in A + asl + asl + asl ; Now $S0 + adc #$88 ; Low byte of UADATA + tax + lda #CMND_NRDI ; Command register: no parity, RTS on, DTR on, no interrupts + sta $C002,X + ldy PSPEED ; Control register: look up by baud rate (8 data bits, 1 stop bit) + lda BPSCTRL,Y + sta $C003,X + stx MOD_UADATA_1+1 ; Modify references to + stx MOD_UADATA_2+1 ; UADATA to point at + stx MOD_UADATA_3+1 ; correct slot (UADATA+S0) + inx + stx MOD_UASTAT_1+1 ; Modify reference to + stx MOD_UASTAT_2+1 ; UASTAT to point at + stx MOD_UASTAT_3+1 ; correct slot (UASTAT+S0) + rts + +.endproc + + +;--------------------------------------------------------- +SSCPUT: +;--------------------------------------------------------- +; Send accumulator out the serial port +; (this is a blocking call) +; [based on ADTPro] +;--------------------------------------------------------- + pha ; Push A onto the stack +MOD_UASTAT_1: +: lda UASTAT ; Check status bits + and #$70 + cmp #$10 + bne :- ; Output register is full, so loop + pla +MOD_UADATA_1: + sta UADATA ; Put character + rts + + +;--------------------------------------------------------- +SSCGET: +;--------------------------------------------------------- +; Read a character from the serial port to the accumulator +; (this is a blocking call) +; [based on ADTPro] +;--------------------------------------------------------- +MOD_UASTAT_2: + lda UASTAT ; Check status bits + and #$68 + cmp #$8 + bne SSCGET ; Input register empty, loop +MOD_UADATA_2: + lda UADATA ; Get character + rts + + +;--------------------------------------------------------- +SSCHASDATA: +;--------------------------------------------------------- +; Read a character from the serial port to the accumulator +; (this is a blocking call) +; [based on ADTPro] +;--------------------------------------------------------- +MOD_UASTAT_3: + lda UASTAT ; Check status bits + and #$68 + cmp #$8 + rts + + +;--------------------------------------------------------- +RESETSSC: +;--------------------------------------------------------- +; Clean up serial port +; [based on ADTPro] +;--------------------------------------------------------- +MOD_UADATA_3: + bit UADATA + rts + + + +.ifdef MOUSE_SUPPORT +;------------------------------------------------------------------- +; +; Mouse routines +; +;------------------------------------------------------------------- + +MOUSEPTR = $EB ; Zero page location + +MOUSE_MIN_X = $10 +MOUSE_MAX_X = $1f +MOUSE_CENTER_X = $17 +MOUSE_MIN_Y = $20 +MOUSE_MAX_Y = $2f +MOUSE_CENTER_Y = $2f + + +;-------------------------------------------------- +; Macros for common mouse operations +;-------------------------------------------------- + +;---------------------------------------- +.macro ClampMouse axis, min, max +;---------------------------------------- +; axis: MOUSE_CLAMP_X or MOUSE_CLAMP_Y +; min: minimum value (2 byte) +; max: maximum value (2 byte) +;---------------------------------------- + ; Clamp X to 0...255 + lda #min + sta MOUSE_CMIN_MSB + lda #max + sta MOUSE_CMAX_MSB + lda #axis + jsr CLAMPMOUSE +.endmacro + +;---------------------------------------- +.macro PosMouse px, py +;---------------------------------------- + ldx MOUSE_SLOT + lda #px + sta MOUSE_X_MSB,X + lda #py + sta MOUSE_Y_MSB,X + jsr POSMOUSE +.endmacro + + +;--------------------------------------------------------- +.proc FINDMOUSE +;--------------------------------------------------------- +; Find and initialize the mouse port +;--------------------------------------------------------- + +; Reference: http://home.swbell.net/rubywand/R034MOUSEPRG.TXT + + sei ; No interrupts while we're getting set up +; +; Step 1: Find the mouse card by scanning slots for ID bytes +; + + ldy #MAX_SLOT ; Start search in slot 7 + +TESTSLOT: + sty MOUSE_SLOT ; Save for later + tya + clc + adc #>SLOT_BASE ; Firmware is $c0 + slot + sta MOD_MOUSE_ID + 2 ; Update msb of signature test + ldx #MOUSEID_MAX ; This many signature bytes + +TESTID: + lda MOUSEID_ADDR,x + sta MOD_MOUSE_ID + 1 ; Update lsb of signature test +MOD_MOUSE_ID: + lda SLOT_BASE + cmp MOUSEID_VAL,x ; Does it match the signature? + bne NOMATCH ; Nope - try the next slot + dex ; Yes! Keep testing + bpl TESTID ; Fall through if all done + jmp FOUND_MOUSE + +NOMATCH: + dey ; Didn't match + bne TESTSLOT ; Keep looking until slot 0 + sty MOUSE_SLOT ; Oops, no mouse - make a note + rts ; and bail + +; +; Step 2: Set up indirect calling routines +; + +FOUND_MOUSE: + ; Slot is in y + + tya + ora #>SLOT_BASE ; Compute $Cn - needed for + sta MOUSEPTR+1 ; MSB of MOUSEPTR ($Cn00) + sta TOMOUSE_Cn ; X register before firmware calls + sta TOMOUSE_msb ; MSB of firmware calls + + lda #0 + sta MOUSEPTR ; LSB of MOUSEPTR ($Cn00) + + tya + asl ; Compute $n0 - needed for + asl + asl + asl + sta TOMOUSE_n0 ; Y register before firmware calls + +; +; Step 3: Configure the mouse card +; + +; Initialize the mouse for use + jsr INITMOUSE ; reset, clamp to 0-1023 x/y + lda #1 ; mouse on, no interrupts + jsr SETMOUSE ; TODO: test carry bit result (set = error) +; +; Since we want deltas, clamp and center +; + ClampMouse MOUSE_CLAMP_X, MOUSE_MIN_X, MOUSE_MAX_X + ClampMouse MOUSE_CLAMP_Y, MOUSE_MIN_Y, MOUSE_MAX_Y + PosMouse MOUSE_CENTER_X, MOUSE_CENTER_Y + + cli ; Enable interrupts so mouse can function + + rts + +.endproc + + +;-------------------------------------------------- +; Indirect jump table for mouse firmware routines +;-------------------------------------------------- + +SETMOUSE: ldy #$12 + jmp GOMOUSE +SERVEMOUSE: ldy #$13 + jmp GOMOUSE +READMOUSE: ldy #$14 + jmp GOMOUSE +CLEARMOUSE: ldy #$15 + jmp GOMOUSE +POSMOUSE: ldy #$16 + jmp GOMOUSE +CLAMPMOUSE: ldy #$17 + jmp GOMOUSE +HOMEMOUSE: ldy #$18 + jmp GOMOUSE +INITMOUSE: ldy #$19 + jmp GOMOUSE + +;-------------------------------------------------- +.proc GOMOUSE +;-------------------------------------------------- + tax ; Preserve the value in A + lda (MOUSEPTR),Y ; Get the routine entry point + sta TOMOUSE_lsb ; Patch the JMP instruction + txa ; Restore the value in A +.endproc + ; fall through + +; The following operand bytes must be patched by the +; initialization code which detects the mouse. + +BANK = $C054 + +TOMOUSE: + ldx #$C1 ; Set up slot in $Cn form in X + ldy #$10 ; Set up slot in $n0 form in Y + php ; Save interrupt state + sei ; No interrupts while calling + bit BANK + jsr SLOT_BASE ; Go to the mouse routine + plp ; Restore interrupt state + rts + +TOMOUSE_Cn = TOMOUSE + 1 +TOMOUSE_n0 = TOMOUSE + 3 +TOMOUSE_lsb = TOMOUSE + 10 +TOMOUSE_msb = TOMOUSE + 11 + + +; TODO: Turn this into a proper delta-sending routine + +;-------------------------------------------------- +.proc FOOMOUSE +;-------------------------------------------------- +; +;-------------------------------------------------- + txa ; save x + pha + tya ; save y + pha + + jsr READMOUSE + + jmp DONE + + ldx MOUSE_SLOT + + lda MOUSE_X_LSB,x + sta LAST_MX + + lda MOUSE_Y_LSB,x + sta LAST_MY + + lda LAST_MX + cmp #MOUSE_CENTER_X + bne SEND + + lda LAST_MY + cmp #MOUSE_CENTER_Y + beq DONE + +SEND: + lda LAST_MX + ora #SIS_MX + jsr SSCPUT + lda LAST_MY + ora #SIS_MY + jsr SSCPUT + + PosMouse MOUSE_CENTER_X, MOUSE_CENTER_Y + +DONE: + pla ; restore y + tay + pla ; restore x + tax + rts + +.endproc + +.endif ; MOUSE_SUPPORT + + +;------------------------------------------------------------------- +; +; Lookup Tables and Variable Storage +; +;------------------------------------------------------------------- + +; Lookup table for UACTRL register, by baud rate + +BPSCTRL: .byte $16,$1E,$1F,$10 ; 300, 9600, 19200, 115k (with 8 data bits, 1 stop bit, no echo) +.enum + BPS_300 + BPS_9600 + BPS_19200 + BPS_115k +.endenum +CMND_NRDI = $0B ; Command: no parity, RTS on, DTR on, no interrupts + + +; Application configuration +PSPEED: .byte 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) + + +; Keyboard state +LASTKB: .byte 0 +LASTOA: .byte 0 +LASTCA: .byte 0 + +.ifdef PADDLE_SUPPORT + +; Paddle state +LASTP0: .byte 0 +LASTP1: .byte 0 + +.endif ; PADDLE_SUPPORT + + +.ifdef MOUSE_SUPPORT + +; Mouse +MOUSE_SLOT: .byte 0 ; mouse slot, or 0 if none +LAST_MX: .byte $7f +LAST_MY: .byte $7f + +; Mouse ID bytes +MOUSEID_MAX = 4 +MOUSEID_ADDR: .byte $05, $07, $0b, $0c, $fb +MOUSEID_VAL: .byte $38, $18, $01, $20, $d6 + +.endif ; MOUSE_SUPPORT + diff --git a/index.html b/index.html index 3c3bdc0..7f0ec34 100644 --- a/index.html +++ b/index.html @@ -8,246 +8,4 @@ video, canvas { border: 2px dotted black; }
- +