#processor 6502
#include "options.inc"

#if target & c128
host	= 128
#endif
#if target & c64
host	= 64
#endif
#if target & vic20
host	= 20
#endif
#if target & (pet3001 | pet4001)
host	= 201	; want 2001 but it must fit in a byte
#endif

  seg MAIN
  .org prsrv

#if target & (vic20 | c64 | c128)
strval	= $10	; strobe bit value on the Vic-20/C64/C128

start	= $ac	; some memory locations
end	= $ae
cinv	= $314
#endif		; target & (vic20 | c64 | c128)

#if cable & (pc64 | c64net | transnib)
#if target & (pet3001 | pet4001)
stemp	= $bd	; memory place that holds the byte that will be received
#else
stemp	= $a7	; memory place that holds the byte that will be received
#endif
#endif

#if target & c128
bstart	= $2d	; start of basic program text
bend	= $1210 ; end of basic program text

bank	= $a9	; temporary variable that holds the memory bank
stack	= $105	; offset to the stack while in interrupt

peek	= $2a2
peekptr = $2aa
poke	= $2af
pokeptr = $2b9

linkprg = $4f4f
basrun	= $5aa6

ackval	= 4
ack	= $dd00
data	= $dd01
ddr	= $dd03
strobe	= $dd0d
#endif		; target & c128

#if target & c64
bstart	= $2b	; start of basic program text
bend	= $2d	; end of basic program text
#if ramexp
bank	= $a9	; temporary variable that holds the memory bank
#else
bank	= none	; non-banked systems
#endif
blnsw	= $cc
crsw	= $d0

stack	= $104	; offset to the stack while in interrupt

clr	= $a659
linkprg = $a533
stxpt	= $a68e
newstt	= $a7ae

ackval	= 4
ack	= $dd00
data	= $dd01
ddr	= $dd03
strobe	= $dd0d
#endif		; target & c64

#if target & vic20
bstart	= $2b	; start of basic program text
bend	= $2d	; end of basic program text
bank	= none
blnsw	= $cc
crsw	= $d0

stack	= $104	; offset to the stack while in interrupt

clr	= $c659
linkprg = $c533
stxpt	= $c68e
newstt	= $c7ae

ackval	= $20
ack	= $911c
data	= $9110
ddr	= $9112
strobe	= $911d
#endif		; target & vic20

#if target & (pet3001 | pet4001)
start	= $c7	;; the PET series
end	= $c9
cinv	= $0090

bstart	= $28	; start of basic program text
bend	= $2a	; end of basic program text
blnsw	= $a7
crsw	= $ac

#if ramexp & pet96
bank  = $bf	; temporary variable that holds the memory bank
latch = $fff0	; write-only register
#else
bank	= none	; non-banked systems
#endif

stack = $104	; offset to the stack while in interrupt

ackval	= $20	; CB2 value bit when set to manual control
ack	= $e84c ; VIA CRB
data	= $e84f ; VIA PA without handshake
ddr	= $e843 ; VIA DDRA
strval	= $02	; VIA CA1 bit in IFR
strobe	= $e84d ; VIA IFR
#endif		; target & (pet3001 | pet4001)

#if target & pet4001	; for BASIC 4.0
clr	= $b5e9
linkprg = $b4b6
stxpt	= $b622
newstt	= $b74a
#endif
#if target & pet3001	; for BASIC 2.0
clr	= $c572
linkprg = $c442
stxpt	= $c5a7
newstt	= $c6c4
#endif

cmdinfo = 0	; code for requesting machine type info
cmdload = 1	; code for loading
cmdsave = 2	; code for saving
cmdjump = 3	; code for jumping to an address
cmdrun	= 4	; code for running a BASIC program

mystart:	; A jump table for external programs
  jmp install		; Installs the prserver wedge.
  jmp deinstall		; Removes the prserver wedge.
  jmp send_switch	; Switches to send configuration, and sends a byte.
  jmp send		; Sends a byte assuming send configuration.
  jmp receive_switch	; Switches to receive configuration, and receives a byte.
  jmp receive		; Receives a byte assuming receive configuration.
  jsr install		; Reinstalls the prserver wedge and
#if target & c128	; continues with the previous program.
  jmp $ff33
#else
  pla
  tay
  pla
  tax
  pla
  rti
#endif		; target & c128

#include "prtrans.inc"	; transfer routines

install:
  sei
  lda cinv	; check the original IRQ vector
  ldx cinv+1
  cmp #<irq
  bne irqinit
  cpx #>irq
  beq skipirq
irqinit:	; if it was different from our irq wedge, install the wedge
  sta oldirq	; first save the old IRQ vector
  stx oldirq+1
  lda #<irq	; and then install ours
  sta cinv
  lda #>irq
  sta cinv+1
skipirq:
#if ramexp & piaexp
#if target & c128
  lda #$3e
  sta $ff00
#endif
  ldx #11	; initialize the PIA
0$:
  lda piatable-1,x
  sta pia,x
  dex
  bne 0$
#endif		; ramexp & piaexp
#if cable & (prlink | prlink88 | pc64)
#if target & vic20
  lda #$ef
  and ack
  sta ack	; set CB1 to trigger on falling edge
#endif
#if target & (pet3001 | pet4001)
  lda ack
  and #%11111110; set CA1 to trigger on falling edge
  ora #%11100000; set CB2 to output with manual control
  sta ack
#endif
#endif		; cable & (prlink | prlink88 | pc64)
#if cable & transnib
  lda #$40	; prepare for input: only DRCV is output
  sta data	; idle state of lines is high
  sta ddr
#endif
  cli
  rts

#if ramexp & piaexp
piatable:
  .byte $34,$fe,4,$ff,0,$ff,0,$dc,4,$fe,4
#endif

irq:
#if (target & c64) && (cable & pc64)
  lda #ackval	; this is needed for Action Replay, whose fastload command
  ora ack	; resets the PA2 line
  sta ack
#endif
#if cable & (prlink | prlink88)
  lda #strval
  bit strobe	; check the -FLAG signal
  beq return	; jump if the client didn't want to send anything
  ldx #0
  stx ddr	; restore the data lines to inputs
#endif
#if cable & pc64
  lda #strval
  bit strobe	; check the -FLAG signal
  beq return	; jump if the client didn't want to send anything
  ldx #$f
  stx ddr	; restore the data lines to inputs/outputs
#endif

#if cable & c64net
  lda data
  and #$F8	; check if the remote host wants to send a byte
  eor #8
  bne return	; jump if not

  lda #$7
  sta data
  sta ddr
#endif

#if cable & transnib
  bit data	; test if DRDY is low.
  bmi return
  lda #$40	; prepare for input: only DRCV is output
  sta data	; idle state of lines is high
  sta ddr
#endif

  jsr rereceive ; receive byte without requiring handshaking
  cmp #cmdinfo
  bne load	; branch if the client did not request for host info
  lda #host	; return the host info byte
  jsr send_switch
  lda #<mystart ; tell the driver start address
  jsr send
  lda #>mystart
  jsr send
  lda bstart	; and the BASIC start address
  jsr send
  lda bstart+1
  jsr send

return:
oldirq = * + 1
  jmp *		; continue the normal irq (selfmodifying code)

load:
  cmp #cmdload	; was it the command for loading?
  beq 0$
  jmp jump	; no, skip

0$:
  jsr gethdr	; get the addresses
#if bank
  lda bank	; other bank than the default specified => can't be basic
  bne 1$
#endif
  lda start	; check if it is a basic program
  eor bstart
  bne 1$
  lda start+1
  eor bstart+1
1$:
  pha		; accu is 0 if it is a basic program
#if target & (c64 | c128)
  lda $d011
  pha
  lda $d030
  pha
  lda #$1
  sta $d011	; blank the screen
  sta $d030	; switch to 2 MHz mode
#endif

#if ramexp & reuexp
  lda bank
  cmp #reubmin	; is it a REU bank?
  bcc noreuld	; no, do not save REC registers on stack

#if actionreuplay
  lda #$2a
  sta $de00	; enable Action Replay RAM at $df00 (disable the ROM)
		; Action Replay has one write-only I/O register at $de00-$deff.
		; Its bits are as follows:
		; 7 - not used
		; 6 - probably resets the IRQ/NMI flipflop in the freezer mode
		; 5 - enables RAM at I/O2
		; 4 - bank selector
		; 3 - bank selector
		; 2 - 1=cartridge off
		; 1 - 1=-EXROM high
		; 0 - 1=-GAME low
#else
  ldx #10
11$:
  lda reu,x
  pha
  dex
  bne 11$
#endif
  lda end+1	; store the end of file pointer high byte
  pha
  jsr reuinit	; initialize the REU registers.	 C flag is set at this stage.
12$:
  jsr receive
  sta (start),y
  jsr incmptreu
  bne 12$

  dec $d030	; switch to 1 MHz mode
  lda #$f2
  sta reu+1	; swap the REU memory back
  pla
  sta start+1	; adjust the end address pointers
  sta end+1

#if actionreuplay
  lda #$0a
  sta $de00	; restore normal Action Replay configuration
#else		; actionreuplay
  ldx #0
13$:
  inx
  pla
  sta reu,x
  cpx #10
  bne 13$
#endif		; actionreuplay
  jmp endload

noreuld:
#endif		; ramexp & reuexp

#if ramexp & piaexp
  lda end+1	; store the end of file pointer high byte
  pha
  lda pia	; store the PIA memory configuration
  pha
#if target & c128
  lda $d506	; store the common memory configuration
  pha
#endif
  jsr piainit	; initialize the PIA expansion (with $d506 value in A)
2$:
  jsr receive
  sta (start),y
  jsr incmptr
  bne 2$

#if target & c128
  pla		; restore the original MMU banking
  ldx #0	; enable all ROMs, select RAM bank 0
  stx $d500
  sta $d506	; restore the common memory configuration
#endif
  pla
  sta pia
  pla
  sta start+1	; adjust the end address pointers
  sta end+1
#else		; ramexp & piaexp
#if target & c128
  lda pokeptr
  pha
  lda #start
  sta pokeptr

2$:
  jsr receive	; get the bytes
  ldx bank
  jsr poke
  jsr incmptr	; update the pointer
  bne 2$

  sty bank
  pla
  sta pokeptr
#else		; target & c128
2$:
  jsr receive	; get the bytes
  sta (start),y
  jsr incmptr	; update the pointer
  bne 2$
#endif		; target & c128
#endif		; ramexp & piaexp

endload:
#if target & (c64 | c128)
  pla
  sta $d030	; restore original speed
  pla
  sta $d011	; and screen mode
#endif

  pla
  bne 0$	; it was not a basic program
  lda end	; set the basic end address
  sta bend
  lda end+1
  sta bend+1
  jsr linkprg	; relink the program
#if target & c128
  lda #0
  sta $ff00	; restore the MMU configuration (linkprg changes it)
#endif
0$:
  jmp (oldirq)

jump:
  cmp #cmdjump	; was it the command for jumping?
  bne save	; no, skip

  jsr receive	; get the memory configuration (for the c128)
#if target & c128
  sta bank	; and temporarily store it
#endif
  jsr receive	; get the jump address low
  tax
  jsr receive	; get the jump address high
  pha		; and store it
  txa
  pha		; store the jump address low
  lda #0
  pha		; clear the flags
  pha		; and the registers
  pha
  pha
#if target & c128
  lda bank
  pha		; store the MMU configuration
#endif
  jsr deinstall ; deinstall the IRQ routine
#if target & c128
#else
  sta crsw	; turn the cursor off
  sta blnsw
#endif
  jmp (oldirq)

save:
  cmp #cmdsave
  beq *+5
  jmp run

#if target & (c64 | c128)
  lda $d011
  pha
  lda $d030
  pha
  lda #$1
  sta $d011	; blank the screen
  sta $d030	; switch to 2 MHz mode
#endif

  jsr gethdr	; get the pointers

#if ramexp & reuexp
  lda bank
  cmp #reubmin	; is it a REU bank?
  bcc noreusv	; no, jump to normal save

#if actionreuplay
  lda #$2a
  sta $de00	; disable Action ROM at $df00
#else
  ldx #10
11$:
  lda reu,x	; store the REU registers
  pha
  dex
  bne 11$
#endif
  lda end+1	; store the end of file pointer high byte
  pha
  jsr reuinit	; initialize the REU registers.	 C flag is set at this stage.
#if send != send_switch
  lda (start),y	; unrolled once
  jsr send_switch
  jsr incmptreu
  beq skiploop$
#endif		; send != send_switch
loop$:
  lda (start),y
  jsr send
  jsr incmptreu
  bne loop$
skiploop$:

  dec $d030	; switch to 1 MHz mode
  lda #$f2
  sta reu+1	; swap the REU memory back
  pla
  sta start+1
  sta end+1

#if actionreuplay
  lda #$0a
  sta $de00	; restore normal Action Replay configuration
#else		; actionreuplay
  ldx #0
13$:
  inx
  pla
  sta reu,x
  cpx #10
  bne 13$
#endif		; actionreuplay
  jmp endsave

noreusv:
#endif		; ramexp & reuexp

#if ramexp & piaexp
  lda pia	; store the PIA memory configuration
  pha
#if target & c128
  lda $d506	; store the common memory configuration
  pha
#endif
  jsr piainit	; initialize the PIA expansion (with $d506 value in A)
#if send != send_switch
  lda (start),y	; unrolled once
  jsr send_switch
  jsr incmptr
  beq skiploop$
#endif		; send != send_switch
loop$:
  lda (start),y
  jsr send
  jsr incmptr
  bne loop$
skiploop$:

#if target & c128
  pla
  ldx #0
  stx $d500
  sta $d506
#endif
  pla
  sta pia
#else		; ramexp & piaexp
#if target & c128
  lda peekptr
  pha
  lda #start
  sta peekptr

#if send != send_switch
  ldx bank	; unrolled once
  jsr peek
  jsr send_switch
  jsr incmptr
  beq skiploop$
#endif		; send != send_switch

loop$:
  ldx bank
  jsr peek
  jsr send
  jsr incmptr
  bne loop$
skiploop$:

  sty bank
  pla
  sta peekptr
#else		; target & c128
#if target & c64
#if send != send_switch
  ldx 1		; unrolled once
  lda #$34
  sta 1		; switch to 64 kB RAM config
  lda (start),y
  stx 1		; restore the normal config
  jsr send_switch
  jsr incmptr
  beq skiploop$
#endif		; send != send_switch

  ldx 1
loop$:
  lda #$34
  sta 1		; switch to 64 kB RAM config
  lda (start),y
  stx 1		; restore the normal config
#else		; target & c64
#if send != send_switch
  lda (start),y	; unrolled once
  jsr send_switch
  jsr incmptr
  beq skiploop$
#endif		; send != send_switch
loop$:
  lda (start),y
#endif		; target & c64
  jsr send
  jsr incmptr
  bne loop$
skiploop$:
#endif		; target & c128
#endif		; ramexp & piaexp

endsave:
#if target & (c64 | c128)
  pla
  sta $d030	; restore original speed
  pla
  sta $d011	; and screen mode
#endif		; target & (c64 | c128)

return2:
  jmp (oldirq)	; return from interrupt

run:
  cmp #cmdrun
  bne return2

  jsr deinstall ; Install the original interrupt routine
#if target & c128
  cli		; process the pending interrupt in a very kludgeous manner.
  jmp basrun
#else
  sta crsw	; turn the cursor off
  sta blnsw
  cli		; process the pending interrupt in a very kludgeous manner.
  jsr clr	; The stack won't run out, since the BASIC clr call
		; initializes the stack pointer.
  jsr stxpt	; Set the BASIC text pointer (to the beginning of the prg)
  jmp newstt	; BASIC warm start
#endif

deinstall:	; restore the original interrupt routine
  lda oldirq
  sta cinv
  lda oldirq+1
  sta cinv+1
  rts

gethdr:
  jsr receive	; get the memory bank byte
#if ramexp
  sta bank
#else
#if target & c128
  lsr
  ror
  ror
  ora #$3f
  sta bank
#endif		; target & c128
#endif		; ramexp
  lda #0	; send the acknowledgement code 0
  jsr send_switch
  jsr receive_switch ; get the file start and end vectors
  sta start
  jsr receive
  sta start+1
  jsr receive
  sta end
  jsr receive
  sta end+1
  rts

#if ramexp & piaexp
incmptr:	; increment and compare the current byte pointer
  inc start	; and change the PIA bank if necessary.
  bne 0$
  inc start+1
  bpl 0$
  dex		; decrease the bank jump counter
  lda #$40
  sta start+1	; restore the address
  clc
  lda #$10
  adc pia
  sta pia	; and adjust the PIA bank
0$:
  txa		; is it the last bank?
  bne 1$	; no, return
  lda start	; perform the comparison
  cmp end
  bne 1$
  lda start+1
  cmp end+1
1$:
  rts
#else		; ramexp & piaexp
incmptr:	; increment and compare the current byte pointer.
  inc start	; increment.
  bne 0$
  inc start+1
0$:
  lda start	; compare.  if start == end, the zero flag will be set.
  cmp end
  bne 1$
  lda start+1
  cmp end+1
1$:
  rts
#endif		; ramexp & piaexp

#if ramexp & reuexp
incmptreu:
  inc start	; increment and compare the current byte pointer
  bne 0$	; and transfer another REU block in if necessary.
  inc start+1
  bpl 0$
  dex		; decrease the bank jump counter
  dec $d030	; slow down to 1 MHz
  lda #$f2
  sta reu+1	; swap the buffers
  lda #$40
  sta start+1	; restore the REU buffer address
  clc
#if actionreuplay
  adc bank
  sta bank
#else
  adc reu+5
#endif
  sta reu+5	; update the REU memory pointer
  lda #$f2
  sta reu+1	; swap the buffers
  inc $d030	; switch back to 2 MHz mode
0$:
  txa		; is it the last bank?
  bne 1$	; no, return
  lda start	; perform the comparison
  cmp end
  bne 1$
  lda start+1
  cmp end+1
1$:
  rts
#endif		; ramexp & reuexp

#if ramexp & piaexp
piainit:
#if target & c128
  ora #$3f	; the accu must be loaded with $d506 when calling this routine
  sta $d506	; set the MMU configuration to 32kB common memory (top and low)
  lda bank
  and #$f
  lsr
  ror
  ror
  tax
  and #$c0
  ora #2
  sta $d500	; set the MMU banking (disabling the BASIC ROM)
  txa
  ror
  ror
#else
  lda bank
  asl
  asl
  asl
  asl
#endif
  eor #$c0
  and #$c0
  ora #$c
  sta pia	; store the PIA banking configuration
  lda start+1
  lsr
  lsr
  and #$30
  ora pia
  sta pia	; set the initial PIA configuration
#endif		; ramexp & piaexp
#if ramexp
rptrinit:
  sec
  lda end+1
  ora #$3f
  sbc start+1	; get the amount of bank crossings
  asl
  rol
  rol
  and #3
  tax		; and store it to x
  bne 0$	; check for address wraparound
  lda start
  cmp end	; if start >= end, the amount of bank crossings will be 4.
  lda start+1
  sbc end+1
  bcc 0$
  ldx #4
0$:
  lda start+1
  and #$3f
  ora #$40
  sta start+1
  lda end+1
  and #$3f
  ora #$40
  sta end+1
  rts
#endif		; ramexp

#if ramexp & reuexp
reuinit:
  lda #0
  ldx #$40
  sta reu+2	; set the buffer address for the C64/C128
  stx reu+3
  sta reu+7	; set the buffer length
  stx reu+8
  sta reu+9	; reset some other registers
  sta reu+10
  lda bank
  sbc #reubmin	; set the memory bank (C flag initially set)
  sta reu+6
  lda start	; set the start address in the REU
  sta reu+4
  lda start+1
  sta reu+5
#if actionreuplay
  sta bank
#endif
#if target & c128
  lda #2
  sta $ff00	; switch to RAM bank 0 and disable the BASIC ROM
#endif
  dec $d030	; slow down to 1 MHz
  lda #$f2
  sta reu+1	; swap the buffers
  inc $d030	; speed up again
  jmp rptrinit
#endif		; ramexp & reuexp

#if target & c64
  jmp mystart	; this is for the C128 bootsector
#endif
