;xmodem receive a .SMA file
;fairly hairy, and far from perfect, but it does seem to work

org 25400

jr start

regpos	equ 0d000h
pgrsptr: defw 05800h
ignore: defb 0

;config
max_retry equ 10

msg_soh equ 1
msg_nak equ 015h
msg_ack equ 6
msg_eot equ 4
msg_can equ 018h

bank1 equ 07ffdh
bankm equ 05b5ch
bank2 equ 01ffdh
bank678 equ 05b67h
select equ 01601h
frames equ 05c78h

recv equ 01e78h
send equ 0205bh

start:
call init

ld hl,regpos
call xrecv

jp finishup



retries: defb 0
seqnum: defb 1

;error exit
stop:
;restore old ram/rom
di
ld bc,bank2
ld a,(bank678)
set 2,a
ld (bank678),a
out (c),a

ld bc,bank1
ld a,(bankm)
and 0f8h
ld (bankm),a
out (c),a
ei

;now cause an error
rst 1		;really 'rst 8'
defb 26		;R tape loading error :-)


xrecv:
ex de,hl

ld a,1
ld (seqnum),a
xor a
ld (retries),a

push de
xrlp0:
;try to make it send
ld a,msg_nak
call putbyte

ld b,50
xrlp1:
halt
push bc
call recv	;watch out - this screws the registers
pop bc
jr c,xrready
djnz xrlp1
jp xrlp0

xrready:
pop de
ld c,a

xrloop:
ld a,(retries)
cp max_retry
jr nz,xr1

ld a,msg_can
call putbyte
jp stop

xr1:
ld a,c
cp msg_soh
jr nz,xr2

xr1a:
;read the block
call blockrd
jp xrnext

xr2:
cp msg_eot	;no more to receive
jr nz,xr3

;make sure they get the message
ld a,msg_ack
call putbyte
ld a,msg_eot
call putbyte
ld b,10
xrlp2:
halt
push bc
call recv	;watch out - this screws the registers
pop bc
djnz xrlp2
ret		;success!

xr3:
cp msg_can	;cancel
jp z,stop	;stop xfer now

;for all others, something messed up.
;just get another byte.

xrnext:
call getbyte
ld c,a
jp xrloop


blockrd:
xor a
ld (retries),a

;read the block
ld b,131
ld hl,blockbuf
push hl
blkloop:
call getbyte
ld (hl),a
inc hl
djnz blkloop

;check sequence is correct
;this is a bit hairy
pop hl	;= blockbuf
ld b,(hl)
ld a,(seqnum)
cp b
jr z,blkrpt

;  i=(seq_num-1) mod 256
;  if(block[0]!=i)
dec a
cp b
jr z,blk2

;    if(block[1]!=(255-seq_num))
inc hl
ld b,(hl)
ld a,(seqnum)
ld c,a
ld a,255
sub c
cp b
jr z,blk2

;      i=255-i;
ld a,(seqnum)
dec a
ld c,a
ld a,255
sub c
;      if(block[1]==i)
;(b is still block[1])
cp b
jr z,blk1
;        send NAK
;        retries++
;        ret
ld a,msg_nak
call putbyte
ld hl,retries
inc (hl)
ret

blk1:
;      send CANCEL
;      stop
ld a,msg_can
call putbyte
jp stop

blk2:
;  retries++
;  ret
ld hl,(retries)
inc (hl)
ret

blkrpt:
;if(block[1]!=(255-seq_num))
ld hl,blockbuf+1
ld a,(seqnum)
ld c,a
ld a,255
sub c
ld c,a
ld a,(hl)
cp c
jr z,blk3

;  send NAK
;  retries++
;  ret
ld a,msg_nak
call putbyte
ld hl,retries
inc (hl)
ret

blk3:
;calc checksum (mod 256) like this:
;	xsum = 0;
;	for (i = 0, p = &block[2]; i < 128; i++) xsum += (*p++ & 0xff);
;	xsum &= 0xff;
;(in other words, it's literally just a checksum mod 256 of the data)
;hl currently = blockbuf+1
ld b,128
xor a
blklp2:
inc hl
add a,(hl)
djnz blklp2

;if(block[130]!=xsum)
inc hl
cp (hl)
jr z,blk4

;  send NAK
;  retries++
;  ret
ld a,msg_nak
call putbyte
ld hl,retries
inc (hl)
ret

blk4:
;put the 128 bytes of data wherever
ld hl,blockbuf+2
call hndlbuf

;retries=0
xor a
ld (retries),a
;seq_num++ (mod 256)
ld hl,seqnum
inc (hl)
;send ACK
ld a,msg_ack
call putbyte
ret



init:
;use rom1 and ram3
di
ld bc,bank2
ld a,(bank678)
res 2,a
ld (bank678),a
out (c),a

ld bc,bank1
ld a,(bankm)
and 0f8h
or 3
ld (bankm),a
out (c),a
ei

ld hl,05800h
ld de,05801h
ld bc,767
ld (hl),1*8+7
ldir

xor a
out (0feh),a
ret


;switch back to ram0
useram0:
di
ld bc,bank1
ld a,(bankm)
and 0f8h
ld (bankm),a
out (c),a
ei
ret


;exit: a=byte read
;XXX needs to interrupt after 10 sec or whatever
getbyte:
push hl
push de
push bc
gb1:
call recv
jr nc,gb1
pop bc
pop de
pop hl
ret


;entry: a=byte read
putbyte:
push hl
push de
push bc
call send
pop bc
pop de
pop hl
ret


;handles buffer movement.
;the 27 bytes at the start of the snap make the rest of it
;a pain to deal with (since we get it in 128-byte blocks).
;this routine works around the related problems.
;these things need to happen for these packet numbers:
;pkt#	1st 27 bytes		the rest
;1	to regpos		to 4000h
;49				to c000h
;81				call useram0; to 6800h
;385				throw away

;in practice we do nothing special for packet 385, since
;the extra bytes will only be 'overwriting' ROM anyway.

;entry: hl=start of xmodem 128-byte buffer
;	de=next byte to be written to
hndlbuf:
ld a,(ignore)
and a
jr nz,hbnorm2

ld bc,27
ld a,(seqnum)
cp 255
jr nz,hb0
ld (ignore),a	;so that we ignore the mod 256 pkt numbers
hb0:
cp 1
jr nz,hb1

ldir
ld de,04000h
jr hbld101

hb1:
call progress

cp 49
jr nz,hb2

ldir
ld de,0c000h
jr hbld101

hb2:
cp 81
jr nz,hbnorm

ldir
call useram0	;only uses bc and a
ld de,06800h
jr hbld101

hbnorm2:
call progress
hbnorm:
;just ldir them all
ld bc,128
ldir
ret

hbld101:
ld bc,101	;i.e. 128-27
ldir
ret



;do progress report attr. thingy
progress:
push af
push bc
push hl
ld a,3*8+7
ld hl,(pgrsptr)
ld b,2
prglp:
ld (hl),a
inc hl
djnz prglp
ld (pgrsptr),hl
pop hl
pop bc
pop af
ret



;sort out memory and registers and start the snap
finishup:
di
ld bc,bank1
ld a,013h
out (c),a
ld bc,bank2
ld a,4
out (c),a

ld sp,04020h

;first sort out interrupt address reg.
ld a,(regpos)
ld i,a

ld hl,regpos+1
ld sp,hl
exx
pop hl
pop de
pop bc
pop af
exx
pop hl
pop de
pop bc
pop iy
pop ix

;sort out interrupt mode
im 2
ld a,(regpos+25)
cp 1
jr nz,skip1
im 1
skip1:
cp 0
jr nz,skip2
im 0
skip2:

;and get interrupts enabled/disabled with the benefit of foresight :-)
ld a,(regpos+19)
and a
jr nz,skip3
xor a
ld (useints),a
skip3:

;border colour - we assume it's ok
ld a,(regpos+26)
out (0FEh),a

pop af		;junk, effectively
pop af

ld sp,04020h
push af
push hl
push de
push bc

ld hl,(regpos+23)
ld (04000h),hl		;sp

ld hl,lastbit
ld de,04002h
ld bc,veryend-lastbit
ldir
ld bc,bank1
ld a,010h
push bc
ld hl,0c000h
ld de,05800h
ld bc,01000h
jp 04002h

;this bit runs on the screen, and fixes up the rest
lastbit:
ldir
pop bc
out (c),a	;all memory is now in place

;nearly there...
pop bc
pop de
pop hl
pop af

ld sp,(04000h)

;nasty, but safe... we change this to a nop if interrupts should be
;kept disabled.
useints: ei
;cue fanfare!
ret

veryend:


;this must be the last thing here, and there must be 131 free bytes
blockbuf:
