; SaveROM.mac

.z80

	aseg
	org	100h

start:
	ld	de,IntroMsg
	ld	c,9
	call	5

	ld	a,(5dh)
	cp	' '
	jr	z,ShowUsage
	ld	a,(6dh)
	cp	' '
	jr	nz,ShowUsage

	call	CheckMem
	call	input

	ld	de,SavingMsg
	ld	c,9
	call	5

	call	CreateFile
	call	SaveFile
	call	CloseFile

	ld	de,SaveOK
	ld	c,9
	call	5
	
	rst	0

ShowUsage:
	ld	de,usage
	ld	c,9
	call	5
	rst	0

input:
	ld	de,PrimMsg
	ld	hl,Prim
	call	do_inp
	ld	hl,(prim)
	ld	a,h
	or	a
	jr	nz,input
	ld	a,l
	cp	4
	jr	nc,input
	ld	(slot),a
	ld	bc,0fcc1h
	add	hl,bc
	ld	a,(hl)
	or	a
	jp	p,no_sec
input2:
	ld	de,SecMsg
	ld	hl,Sec
	call	do_inp
	ld	hl,(Sec)
	ld	a,h
	or	a
	jr	nz,input2
	ld	a,l
	cp	4
	jr	nc,input2
	sla	a
	sla	a
	or	80h
	ld	hl,slot
	or	(hl)
	ld	(hl),a
no_sec:
	ld	de,MegaMsg
	ld	c,9
	call	5
euh:
	ld	c,8
	call	5
	ld	c,1
	cp	'y'
	jr	z,okay
	cp	'Y'
	jr	z,okay
	dec	c
	cp	'n'
	jr	z,okay
	cp	'N'
	jr	nz,euh
okay:
	push	bc
	ld	de,Output
	ld	(de),a
	ld	c,9
	call	5
	pop	bc
	ld	a,c
	ld	(type),a
	or	a
	jp	z,normal
	; assume megarom
	ld	de,SwAdMsg
	ld	hl,swaddr
	call	do_inp
	ld	de,SaAdMsg
	ld	hl,saaddr
	call	do_inp
	ld	de,BlSizeMsg
	ld	hl,bloksize
	call	do_inp
ToHighFB:
	ld	de,FirstMsg
	ld	hl,firstblok
	call	do_inp
	ld	a,(firstblok+1)
	or	a
	jr	nz,ToHighFB
ToHighLB:
	ld	de,LastMsg
	ld	hl,lastblok
	call	do_inp
	ld	a,(lastblok+1)
	or	a
	ret	z
	jr	ToHighLB

do_inp:
	ld	(string),de
	ld	(des),hl
	ld	de,(string)
	ld	c,9
	call	5

	ld	hl,InputStr
	ld	b,4
NumbWrong:
	push	bc
	push	hl
	ld	c,8
	call	5
	pop	hl
	pop	bc
	cp	13
	jr	z,return
	cp	8
	jp	z,bs
	cp	7fh
	jp	z,bs
	ld	c,a
	ld	a,b
	or	a
	jr	z,NumbWrong
	ld	a,c
	sub	'0'
	jr	c,NumbWrong
	cp	10
	jr	c,NumbOk      
	ld	a,c
	cp	'f'+1
	jr	nc,NumbWrong
	and	NOT 32
	cp	'F'+1
	jr	nc,NumbWrong
	sub	'A'
	jr	c,NumbWrong
	add	a,10
NumbOk:
	; echo
	push	af
	push	bc
	push	hl
	ld	e,c
	ld	c,2
	call	5	 
	pop	hl
	pop	bc
	pop	af
	; store it
	ld	(hl),a
	inc	hl
	dec	b
	jr	NumbWrong
return:
	ld	a,b
	cp	4
	jr	z,NumbWrong
	push	bc
	push	hl
	ld	de,crlf
	ld	c,9
	call	5
	pop	hl
	pop	bc
	; do some parsing stuff
	ld	hl,InputStr
	ld	de,0
NextNumb:
	ld	c,(hl)
	ld	a,b
	cp	4
	jr	z,NumbComplete
	inc	b
	inc	hl
	sla	e
	rl	d
	sla	e
	rl	d
	sla	e
	rl	d
	sla	e
	rl	d
	ld	a,e
	or	c
	ld	e,a
	jr	NextNumb
NumbComplete:
	ld	hl,(des)
	ld	(hl),e
	inc	hl
	ld	(hl),d
	ret

bs:
	ld	a,b
	cp	4
	jp	nc,NumbWrong
	push	bc
	push	hl
	ld	c,2
	ld	e,127
	call	5
	pop	hl
	pop	bc
	dec	hl
	inc	b
	jp	NumbWrong

normal:
	ld	de,BeginAdMsg
	ld	hl,firstadd
	call	do_inp
	ld	de,EndAdMsg
	ld	hl,lastadd
	jp	do_inp

CreateFile:
	ld	hl,5ch
	ld	de,FCB
	ld	bc,12
	ldir
	ld	de,FCB
	ld	c,16h
	call	5
	or	a
	jr	z,CreateOK
	ld	de,CreateErr
	ld	c,9
	call	5
	rst	0
CreateOK:
	ld	hl,1
	ld	(FCB+14),hl
	ld	l,h		; clears HL
	ld	(FCB+33),hl
	ld	(FCB+35),hl
	ret

FileSave:
	push	af
	push	bc
	push	de
	push	hl
	ld	de,FCB
	ld	c,26h
	call	5
	or	a
	jr	nz,SaveError
	pop	hl
	pop	de
	pop	bc
	pop	af
	ret
SaveError:
	ld	de,SaveErr
	ld	c,9
	call	5
	rst	0

CloseFile:
	ld	de,FCB
	ld	c,10h
	call	5
	or	a
	ret	z
	ld	de,SaveErr
	ld	c,9
	call	5
	rst	0

CheckMem:
	ld	hl,(6)
	ld	a,h
	cp	0c0h
	ret	nc
	ld	de,MemoryErr
	ld	c,9
	call	5
	rst	0

SaveFile:
	ld	a,(type)
	or	a
	jr	z,SaveStdROM
	; assume megarom
	; we will use the routine for Standard ROMs for
	; every blok. Iterates from (firstblok) to
	; (lastblok).
	ld	hl,(saaddr)
	ld	(firstadd),hl
	ld	de,(bloksize)
	add	hl,de
	dec	hl
	ld	(lastadd),hl
	; the actual loop
	ld	a,(firstblok)
	ld	e,a
	ld	a,(lastblok)
	ld	d,a
	cp	e
	jr	c,NextBlokI
NextBlok:
	push	de
	ld	a,(slot)
	ld	hl,(swaddr)
	call	14h
	call	SaveStdROM
	pop	de
	inc	e
	ld	a,d
	or	a
	jr	z,Till1
	cp	e
	jr	nc,NextBlok
	ret
Till1:
	ld	a,e
	dec	a
	jr	nz,NextBlok
	ret
NextBlokI:
	push	de
	ld	a,(slot)
	ld	hl,(swaddr)
	call	14h
	call	SaveStdROM
	pop	de
	dec	e
	ld	a,d
	or	a
	jr	z,TillFF
	ld	a,e
	cp	d
	jr	nc,NextBlokI
	ret
TillFF:
	ld	a,e
	inc	a
	jr	nz,NextBlokI
	ret

; this funtion saves memory area (firstadd - lastadd) in 
; slot (slot) to disk

SaveStdROM:
	ld	hl,(firstadd)
	ld	a,h
	cp	40h
	jr	nc,SavePage1
	call	Page0Copy
	ld	hl,(firstadd)
	ld	de,8000h
	add	hl,de
	ex	de,hl
	ld	c,1ah
	call	5
	ld	de,(firstadd)
	ld	hl,(lastadd)
	ld	a,h
	cp	040h
	jr	c,NoOF0
	ld	hl,3fffh
NoOF0:
	or	a
	sbc	hl,de
	inc	hl
	call	FileSave
SavePage1:
	ld	hl,(firstadd)
	ld	a,h
	cp	080h
	jr	nc,SavePage2
	ld	hl,(lastadd)
	ld	a,h
	cp	040h
	ret	c
	call	Page1Copy
	ld	hl,(firstadd)
	ld	a,h
	cp	040h
	jr	nc,NoOF10
	ld	hl,4000h
NoOF10:
	; set write address
	push	hl
	ld	de,4000h
	add	hl,de
	ex	de,hl
	ld	c,1ah
	call	5
	; calc length
	ld	hl,(lastadd)
	ld	a,h
	cp	080h
	jr	c,NoOF11
	ld	hl,7fffh
NoOF11:
	pop	de
	or	a
	sbc	hl,de
	inc	hl
	call	FileSave	
SavePage2:
	ld	hl,(firstadd)
	ld	a,h
	cp	0c0h
	jr	nc,SavePage3
	ld	hl,(lastadd)
	ld	a,h
	cp	080h
	ret	c
	call	Page2Copy
	ld	hl,(firstadd)
	ld	a,h
	cp	080h
	jr	nc,NoOF20
	ld	hl,8000h
NoOF20:
	; set write address
	push	hl
	ex	de,hl
	ld	c,1ah
	call	5
	; calc length
	ld	hl,(lastadd)
	ld	a,h
	cp	0c0h
	jr	c,NoOF21
	ld	hl,0bfffh
NoOF21:
	pop	de
	or	a
	sbc	hl,de
	inc	hl
	call	FileSave
SavePage3:
	ld	hl,(lastadd)
	ld	a,h
	cp	0c0h
	ret	c
	call	Page3Copy
	ld	hl,(firstadd)
	ld	a,h
	cp	0c0h
	jr	nc,NoOF30
	ld	hl,0c000h
NoOF30:
	; set write address
	push	hl
	ld	de,4000h
	or	a
	sbc	hl,de
	ex	de,hl
	ld	c,1ah
	call	5
	; calc length
	ld	hl,(lastadd)
	pop	de
	or	a
	sbc	hl,de
	inc	hl
	jp	FileSave

; These functions copy memory pages 0, 1, 2 or 3 from
; (slot) to page 2 RAM. Used for saving them.

Page0Copy:
	di
	ld	a,(0f342h)
	ld	h,40h
	call	024h
	ld	a,(0f343h)
	ld	h,80h
	call	024h
	ld	hl,MoveIt
	ld	de,4000h
	ld	bc,100h
	ldir
	jp	4000h
MoveIt:
	di
	ld	a,(slot)
	bit	7,a
	jr	nz,SubSlots
	and	3
	ld	c,a
	in	a,(0a8h)
	ld	(4100h),a
	and	0fch
	or	c
	out	(0a8h),a
	ld	hl,0
	ld	de,8000h
	ld	bc,4000h
	ldir
	ld	a,(4100h)
	out	(0a8h),a
	ei
	ret
SubSlots:
	ld	e,a
	ld	a,(0ffffh)
	cpl
	ld	c,a
	in	a,(0a8h)
	ld	b,a
	ld	(4100h),bc	
	ld	a,e
	and	3
	ld	d,a
	rrca
	rrca
	or	d
	ld	d,a
	ld	a,b
	and	03ch
	or	d
	out	(0a8h),a
	ld	a,(0ffffh)
	cpl
	and	0fch
	ld	d,a
	ld	a,e
	rrca
	rrca
	and	3
	or	d
	ld	(0ffffh),a
	ld	hl,0
	ld	de,8000h
	ld	bc,4000h
	ldir
	ld	bc,(4100h)
	ld	a,b
	out	(0a8h),a
	ld	a,c
	ld	(0ffffh),a
	ei
	ret
Page1Copy:
	di
	ld	a,(slot)
	ld	h,40h
	call	024h
	ld	a,(0f343h)
	ld	h,80h
	call	024h
	ld	hl,4000h
	ld	de,8000h
	ld	bc,4000h
	ldir
	ei
	ret
Page2Copy:
	di
	ld	a,(0f342h)
	ld	h,40h
	call	024h
	ld	a,(slot)
	ld	h,80h
	call	024h
	ld	hl,8000h
	ld	de,4000h
	ld	bc,4000h
	ldir
	ld	a,(0f343h)
	ld	h,80h
	call	024h
	ld	hl,4000h
	ld	de,8000h
	ld	bc,4000h
	ldir
	ei
	ret
Page3Copy:
	di
	ld	a,(0f343h)
	ld	h,80h
	call	024h
	ld	a,(slot)
	bit	7,a
	jr	nz,SubSlots2
	rrca
	rrca
	and	0c0h
	ld	c,a
	in	a,(0a8h)
	ld	(temp),a
	and	03fh
	or	c
	out	(0a8h),a
	ld	hl,0c000h
	ld	de,08000h
	ld	bc,04000h
	ldir
	ld	a,(temp)
	out	(0a8h),a
	ei
	ret
SubSlots2:
	ld	c,a
	rrca
	rrca
	and	0c0h
	ld	b,a
	ld	a,(0ffffh)
	cpl
	ld	(temp+1),a
	in	a,(0a8h)
	ld	(temp),a
	and	03fh
	or	b
	out	(0a8h),a
	ld	a,(0ffffh)
	cpl
	and	03fh
	ld	b,a
	ld	a,c
	rlca
	rlca
	rlca
	rlca
	and	0c0h
	or	b
	ld	(0ffffh),a
	ld	hl,0c000h
	ld	de,08000h
	ld	bc,04000h
	ldir
	ld	a,(temp)
	out	(0a8h),a
	ld	a,(temp+1)
	ld	(0ffffh),a
	ei
	ret

; "temp" variables
string: 	dw	0
des:		dw	0
temp:		dw	0
; information about cartridge to save
Prim:		dw	0
Sec:		dw	0
slot:		db	0
type:		db	0
swaddr: 	dw	0
saaddr: 	dw	0
bloksize:	dw	0
firstblok:	dw	0
lastblok:	dw	0
firstadd:	dw	0
lastadd:	dw	0

; strings

Output: 	db	0
crlf:		db	13,10,'$'
IntroMsg:	db	'SaveROM 1.0',13,10,'By Sean Young 1997, 1998',13,10,10,'$'
PrimMsg:	db	'Primary Slot: $'
SecMsg: 	db	'Secondary Slot: $'
MegaMsg:	db	'MegaROM (y/n)? $'
SwAdMsg:	db	'Switch Address: $'
SaAdMsg:	db	'Save Address: $'
BlSizeMsg:	db	'Block Size: $'
FirstMsg:	db	'First Block: $'
LastMsg:	db	'Last Block: $'
BeginAdMsg:	db	'Begin Address: $'
EndAdMsg:	db	'End Address: $'
InputStr:	db	'0000',13,10
Usage:		db	'Usage:',13,10,10
		db	'     saverom <filename>',13,10,'$'
SavingMsg:	db	13,10,'Saving...',13,10,'$'
CreateErr:	db	'File creation error!',13,10,'$'
MemoryErr:	db	'Not enough memory!',13,10,'$'
SaveErr:	db	'Save error!',13,10,'$'
SaveOK: 	db	'Save completed.',13,10,'$'
FCB:		db	0

END start

