	include	"exec/types.i"
	include	"exec/nodes.i"
	include	"exec/lists.i"
	include	"exec/memory.i"
	include	"exec/ports.i"
	include	"exec/io.i"
	include	"exec/devices.i"
	include	"exec/resident.i"
	include	"devices/trackdisk.i"
	include	"dos.i"

	xref	_LVOOpenLibrary,_LVOCloseLibrary
	xref	_LVOAllocSignal,_LVOFreeSignal,_LVOFindTask
	xref	_LVOAddPort,_LVORemPort
	xref	_LVOOpenDevice,_LVOCloseDevice,_LVODoIO
	xref	_LVORawDoFmt,_LVOAllocMem,_LVOFreeMem
	xref	_LVOFindResident,_LVOOutput,_LVOWrite
	xref	_LVOLoadSeg,_LVOUnLoadSeg

BOOTSIZE	equ	1024		; boottilohkon koko tavuina
BUF		equ	2048		; koodipuskurin koko
LF		equ	10		; rivinsiirto

.DosBase	equ	-4		; dos-kirjaston perusosoite pinossa
port		equ	-8		; viestiportin osoite pinossa
ioreq		equ	-12		; ioreq-osoitin
buf		equ	-16		; puskurin osoite
seglist		equ	-20		; segmenttilistan osoitin
unit		equ	-24		; levyaseman numero
filename	equ	-28		; tiedoston nimen osoitin
devflag		equ	-30		; lippu: trackdisk auki

Start	link	a4,#-30
	clr.b	-1(a0,d0.l)		; lisätään loppunolla
	move.l	a0,a5			; komentorivin osoite

	moveq	#6,d0
	move.l	a4,a0
1$	clr.l	-(a0)			; nollataan muuttujat
	dbf	d0,1$
	clr.w	-(a0)

	lea	dosname,a1		; dos-kirjaston nimi
	moveq	#0,d0			; mikä tahansa versio
	move.l	$4,a6			; avataan dos
	jsr	_LVOOpenLibrary(a6)
	move.l	d0,.DosBase(a4)		; perusosoite talteen
	beq	cleanup			; jos ei auennut, pois

	move.l	#BUF,d0			; puskurin koko
	move.l	#MEMF_CHIP!MEMF_CLEAR,d1; tyyppi=CHIP-muisti
	move.l	$4,a6
	jsr	_LVOAllocMem(a6)	; varataan puskuri
	move.l	d0,buf(a4)		; osoite talteen
	beq	cleanup			; jos ei puskuria, pois
	move.l	d0,a0
	lea.l	BOOTSIZE(a0),a0
	move.l	a0,filename(a4)		; filename = buf + BOOTSIZE

	move.l	a5,a0			; komentorivin osoite
	bsr	parse			; nuuskitaan komentorivi
	move.l	a0,d0
	bne	cmderr			; virhe: pois

	moveq.l	#MP_SIZE,d0		; viestiportin koko
	sub.l	a0,a0			; ei nimeä (yksit. portti)
	bsr	createport		; luodaan portti
	move.l	d0,port(a4)		; osoite talteen
	beq	cleanup			; jos ei porttia, pois

	moveq.l	#IOSTD_SIZE,d0		; IORequestin koko
	move.l	port(a4),a0		; viestiportin osoite
	bsr	createio		; luodaan IOReq
	move.l	d0,ioreq(a4)		; osoite talteen
	beq	cleanup			; jos ei IOReq:a, pois

	lea	INITIAL(pc),a0		; kopioidaan puskuriin..
	moveq	#(USERCODE-INITIAL),d0	; ..näin monta tavua
	move.l	d0,d1
	subq.l	#1,d0			; dbf:lle yhtä pienempi luku
	move.l	buf(a4),a2		; puskurin osoite
cploop	move.b	(a0)+,(a2)+		; kopioidaan koodia..
	dbf	d0,cploop		; ..kunnes loppu

	move.l	#BOOTSIZE,d2		; boottilohko BOOTSIZE tavua
	sub.l	d1,d2			; d2 = jäljelle jäävän koko
	move.l	filename(a4),d1		; ladattava tiedosto
	move.l	.DosBase(a4),a6
	jsr	_LVOLoadSeg(a6)		; ladataan se muistiin
	move.l	d0,seglist(a4)		; seglistin osoitin talteen
	bne.s	ck_len			; hyppy jos latautui
	lea	eltxt(pc),a0		; virheilmoitus, jos..
	move.l	filename(a4),d0		; ..ei saatu ladattua
	bsr	dpymsg			; tulostetaan virhe
	bra	cleanup			; pois

ck_len	add.l	d0,d0			; muunnetaan BCPL-osoitin..
	add.l	d0,d0			; ..abs. osoitteeksi
	move.l	d0,a0			; osoite a0:aan

	tst.l	(a0)			; onko useita segmenttejä?
	bne	multip			; on, hyppää ja valita
	move.l	-(a0),d0		; segmentin koko d0:aan
	cmp.l	d2,d0			; riittääkö tila?
	bhi	toolong			; ei, hyppää ja valita
	addq.l	#8,a0			; segm. koodi alkaa tästä
	subq.l	#1,d0			; kopioitava määrä
cp_code	move.b	(a0)+,(a2)+		; kopioidaan puskuriin..
	dbf	d0,cp_code		; ..kunnes segmentti kopiotu

; Lasketaan boottilohkon tarkistussumma.  Kun kaikki 256 long wordia
; lasketaan yhteen, tuloksen on oltava nolla.  Jokaisen yhteenlaskun
; jälkeen tarkistetaan carry-bitin tila ja lisätään summaan ykkönen,
; jos carry oli 1 (yhteenlaskussa syntyi ylivuoto).  Lopuksi asete-
; taan tarkistussumma sellaiseksi, että summasta tosiaan tulee nolla.
	move.l	buf(a4),a0		; puskurin osoite
	move.l	a0,a1			; myös a1:een
	move.w	#BOOTSIZE/4-1,d1	; lohkon koko longeina
	moveq	#0,d0			; summa alussa = 0
c_csum	add.l	(a0)+,d0		; lisätään jokainen LONG
	bcc.s	1$			; jos muistinumero, ..
	addq.l	#1,d0			; ..lisätään vielä ykkönen
1$	dbf	d1,c_csum		; lasketaan kaikki
	move.l	buf(a4),a0		; puskurin osoite
	addq.l	#4,a0			; offset tarkistussummaan
	sub.l	d0,(a0)			; vähenn. tarkistussummasta
	bcc.s	2$			; pitikö lainata?
	subq.l	#1,(a0)			; joo, vähennetään vielä 1

2$	lea	TDname(pc),a0		; laitenimi
	move.l	unit(a4),d0		; yksikkö
	move.l	ioreq(a4),a1		; IORequestin osoite
	moveq.l	#0,d1			; liput
	move.l	$4,a6
	jsr	_LVOOpenDevice(a6)	; avataan trackdisk
	tst.l	d0			; saatiinko auki?
	beq.s	dev_ok			; joo, hyppää

	lea	optxt(pc),a0
	move.l	unit(a4),d0		; yksikkö
	bsr	dpymsg			; tulostetaan virhe
	bra	cleanup			; pois

dev_ok	st	devflag(a4)		; trackdisk saatiin auki
	moveq	#1,d0			; käynnistetään moottori
	bsr	Motor
	bsr	WriteBlk		; kirjoitetaan boottilohko
	bne.s	wrerr			; kirjoitus ei onnistunut

	moveq	#0,d0
	bsr	Motor			; pysäytetään moottori
	bra	cleanup			; siivotaan jäljet


wrerr	lea	wrtxt(pc),a0		; virheilmoituksia
	bra.s	cmderr

multip	lea	ootxt(pc),a0
	bra.s	cmderr

toolong	lea	tltxt(pc),a0
	move.l	d2,d1
cmderr	bsr	dpymsg
cleanup	tst.b	devflag(a4)		; onko trackdisk auki?
	beq.s	100$
	move.l	ioreq(a4),a1
	move.l	$4,a6
	jsr	_LVOCloseDevice(a6)	; on -> suljetaan se

100$	move.l	ioreq(a4),a0		; vapautetaan IOReq
	bsr	deleteio
	move.l	port(a4),a0		; vapautetaan MsgPort
	bsr	deleteport

	move.l	seglist(a4),d1		; segmenttilista olemassa?
	beq.s	103$
	move.l	.DosBase(a4),a6
	jsr	_LVOUnLoadSeg(a6)	; joo -> vapautetaan se

103$	move.l	buf(a4),d0		; puskuri varattu?
	beq.s	105$
	move.l	d0,a1
	move.l	#BUF,d0
	move.l	$4,a6
	jsr	_LVOFreeMem(a6)		; jep -> vapautetaan se

105$	move.l	.DosBase(a4),d0		; suljetaan Dos-kirjasto
	beq.s	107$
	move.l	d0,a1
	move.l	$4,a6
	jsr	_LVOCloseLibrary(a6)
107$	unlk	a4
	rts

skipblk	move.b	(a0)+,d0		; hypätään välilyöntien ja
	beq.s	10$			; tabulaattorien yli
	cmp.b	#32,d0
	beq.s	skipblk
	cmp.b	#9,d0
	beq.s	skipblk
10$	subq.l	#1,a0
	rts

;;;; tutkitaan komentorivin parametrit
parse	bsr.s	skipblk			; hypätään rivin alussa tyhjän yli
	beq	perr1			; jos rivi loppui, virhe
	moveq	#34,d1			; lainausmerkki d1:een
	move.l	filename(a4),a1		; tila tiedoston nimelle
	move.b	(a0)+,d0		; otetaan merkki komentoriviltä
	beq	perr1			; jos nolla, virhe
	cmp.b	d1,d0			; lainausmerkki?
	bne.s	101$			; ei -> hypellään yli
10$	move.b	(a0)+,d0		; kopioidaan tiedoston nimeä,
	beq.s	11$			; kunnes .. .. nolla ..
	cmp.b	d1,d0			; .. tai lainausmerkki
	beq.s	11$
	move.b	d0,(a1)+		; merkki talteen nimipuskuriin
	bra.s	10$			; kopioidaan koko nimi

100$	move.b	(a0)+,d0		; kopioidaan nimeä, kunnes...
	beq.s	11$			; .. nolla ...
101$	cmp.b	#' ',d0			; .. tai välilyönti ..
	beq.s	11$
	cmp.b	#9,d0			; .. tai tabulaattori
	beq.s	11$
	move.b	d0,(a1)+		; merkki talteen
	bra.s	100$
11$	clr.b	(a1)			; lisätään nimen loppuun nolla
	bsr.s	skipblk			; hypätään mahdollisen tyhjän yli

	move.b	(a0)+,d0		; otetaan levyaseman numero
	beq.s	perr2
	bsr.s	ucase
	cmp.b	#'D',d0			; 'D'
	bne.s	perr2
	move.b	(a0)+,d0
	beq.s	perr2
	bsr.s	ucase
	cmp.b	#'F',d0			; 'F'
	bne.s	perr2
	moveq	#0,d0
	move.b	(a0)+,d0
	beq.s	perr2
	sub.b	#'0',d0			; '0'...'3'
	cmp.b	#3,d0
	bhi.s	perr2
	move.l	d0,unit(a4)
	cmp.b	#':',(a0)		; kaksoispiste?
	bne.s	perr2
	sub.l	a0,a0			; ei virhettä
	rts
perr1	lea	fntxt(pc),a0
	rts
perr2	lea	ddtxt(pc),a0
	rts

ucase	cmp.b	#'a',d0			; muunnetaan ISOKSI KIRJAIMEKSI
	bcs.s	10$			; alle 'a'?
	cmp.b	#'z',d0
	bhi.s	10$			; yli 'z'?
	sub.b	#('a'-'A'),d0		; ISONNA
10$	rts

; dpymsg tulostaa a0:n osoittaman merkkijonon ja sen mahdollisesti
; sisältämät muuttujat d0...d7 C-kielen printf-lauseen tavoin.
dpymsg	movem.l	d0-d7/a0-a3,-(sp)	; rekisterit pinoon
	move.l	sp,a1
	lea.l	110$(pc),a2		; putchar-rutiinin osoite
	move.l	buf(a4),a3		; tulostuspuskuri
	move.l	$4,a6
	jsr	_LVORawDoFmt(a6)	; muotoillaan rivi
	move.l	.DosBase(a4),a6
	jsr	_LVOOutput(a6)		; tulostustiedosto
	move.l	d0,d1
	beq.s	105$
	move.l	a3,d2
100$	tst.b	(a3)+			; lasketaan rivin pituus
	bne.s	100$
	subq.w	#1,a3
	move.l	a3,d3
	sub.l	d2,d3
	jsr	_LVOWrite(a6)		; tulostetaan rivi
	movem.l	(sp)+,d0-d7/a0-a3	; rekisterit takaisin
105$	rts
110$	move.b	d0,(a3)+		; putchar-rutiini
	rts

; port = createport(size,name);
;  d0                d0   a0
;
createport:
	movem.l	d2-d3/a2-a3,-(sp)
	move.l	d0,d3			; koko
	move.l	a0,a3			; nimen osoite
	move.l	a0,d1			; nimen osoite = 0?
	beq.s	10$			; joo -> ei nimeä
1$	tst.b	(a0)+			; mitataan nimen pituus
	bne.s	1$
	sub.l	d1,a0
	add.l	a0,d0			; lisätään nimen pituus kokoon
10$	addq.l	#4,d0			; tilaa portin koolle (4 tavua)
	move.l	d0,d2			; lopullinen koko talteen
	move.l	#1!(1<<16),d1		; MEMF_PUBLIC!MEMF_CLEAR
	move.l	$4,a6			; ExecBase
	jsr	_LVOAllocMem(a6)	; varataan muistia
	move.l	d0,a2			; varatun muistin osoite
	tst.l	d0			; saatiinko muistia?
	beq	creaport1		; ei -> pois
	move.l	d2,(a2)+		; varatun alueen koko muistiin
	moveq.l	#-1,d0			; varataan signaalibitti
	jsr	_LVOAllocSignal(a6)
	move.b	d0,MP_SIGBIT(a2)	; asetetaan signaali porttiin
	bmi.s	creaporte		; ei saatu signaalia -> pois
	sub.l	a1,a1			; etsitään tämä taski
	jsr	_LVOFindTask(a6)
	move.l	d0,MP_SIGTASK(a2)	; asetetaan osoite porttiin
	move.b	#NT_MSGPORT,LN_TYPE(a2)	; noden tyyppi = MSGPORT
	clr.b	MP_FLAGS(a2)		; nollataan liput

	move.l	a3,d0			; nimen osoite
	beq.s	20$			; jos ei nimeä, hypätään
	lea.l	0(a2,d3.l),a0		; a0 = portin loppuosoite
	move.l	a0,LN_NAME(a2)		; asetetaan nimen osoite porttiin
2$	move.b	(a3)+,(a0)+		; kopioidaan nimi
	bne.s	2$			; kunnes NULL
	move.l	a2,a1			; portilla nimi -> lisätään listaan
	jsr	_LVOAddPort(a6)		; julkiseksi portiksi
	move.l	a2,d0			; palautetaan portin osoite d0:ssa
	bra.s	creaport1
20$	lea	MP_MSGLIST(a2),a0	; ei nimeä -> tehdään vain NEWLIST
	NEWLIST	A0
	move.l	a2,d0			; palautetaan portin osoite d0:ssa
	bra.s	creaport1

; deleteport(MsgPort);
;              a0
;
deleteport:
	movem.l	d2-d3/a2-a3,-(sp)
	move.l	a0,d0			; portin osoite
	beq.s	creaport1		; jos nolla, ei tehdä mitään
	move.l	a0,a2
	tst.l	LN_NAME(a2)		; onko portilla nimi?
	beq.s	30$			; ei -> hyppää
	move.l	a2,a1			; on -> poistetaan portti listalta
	move.l	$4,a6			; ExecBase
	jsr	_LVORemPort(a6)		; poistetaan listalta
30$	moveq.l	#0,d0
	move.b	MP_SIGBIT(a2),d0	; signaalin numero
	bmi.s	creaporte		; negatiivinen -> ei signaalia, pois
	move.l	$4,a6
	jsr	_LVOFreeSignal(a6)	; vapautetaan signaali
creaporte:
	move.l	-(a2),d0		; d0 = portille varatun alueen koko
	move.l	a2,a1			; a1 = alueen osoite
	move.l	$4,a6
	jsr	_LVOFreeMem(a6)		; vapautetaan muisti
	moveq.l	#0,d0			; palautetaan nolla
creaport1:
	movem.l	(sp)+,d2-d3/a2-a3
	move.l	d0,a0			; nolla myös a0:aan
	rts

; IOReq = createio(size,MsgPort);
;  d0               d0    a0
;
createio:
	movem.l	d2/a2,-(sp)     	; d0=koko, a0=MsgPort
	move.l	a0,a2			; msgport* talteen
	addq.l	#4,d0			; tilaa stuktuurin koolle
	move.l	d0,d2
	move.l	#MEMF_PUBLIC!MEMF_CLEAR,d1
	move.l	$4,a6
	jsr	_LVOAllocMem(a6)	; varataan muistia IORequestille
	move.l	d0,a0
	tst.l	d0
	beq.s	100$			; ei muistia -> pois
	move.l	d2,(a0)+		; tallennetaan struktuurin koko
	move.l	a2,MN_REPLYPORT(a0)	; portin osoite IORequestiin
	move.b	#NT_MESSAGE,LN_TYPE(a0)	; portin tyyppi
	subq.l	#4,d2
	move.w	d2,MN_LENGTH(a0)	; asetetaan viestin koko
	move.l	a0,d0			; palautetaan struktuurin osoite
100$    movem.l	(sp)+,d2/a2
	rts

; deleteio(IOReq);
;	   a0
;
deleteio:
	move.l	a0,d0			; a0 = IoRequest
	beq.s	100$			; nolla -> ei tehdä mitään
	move.l	a0,a1
	move.l	-(a1),d0		; otetaan struktuurin koko
	move.l	$4,a6
	jsr	_LVOFreeMem(a6)		; vapautetaan muisti
100$	rts

; Motor kytkee levyaseman moottorin päälle tai pois riippuen d0:n
; arvosta.
Motor	move.l	ioreq(a4),a1		; IORequest, kuinkas muuten
	move.l	d0,IO_LENGTH(a1)	; moottorin tila on/off
	move.w	#TD_MOTOR,IO_COMMAND(a1); komento TD_MOTOR
	move.l	$4,a6
	jsr	_LVODoIO(a6)		; komenna
	rts

; WriteBlk kirjoittaa boottilohkon levylle trackdisk devicen avulla.
WriteBlk:
	move.l	ioreq(a4),a1		; IORequest
	clr.l	IO_OFFSET(a1)		; aloitetaan lohkosta nolla
	move.l	#BOOTSIZE,IO_LENGTH(a1)	; pituus = 1024 tavua
	move.l	buf(a4),IO_DATA(a1)	; puskurin osoite
	move.w	#CMD_WRITE,IO_COMMAND(a1); IO-komento = WRITE
	move.l	a1,a2			; IOReq talteen
	move.l	$4,a6			; kirjoitetaan boottilohko
	jsr	_LVODoIO(a6)
	tst.l	d0			; virhe?
	bne.s	WrBlke

	move.l	a2,a1			; IOReq taas
	move.w	#CMD_UPDATE,IO_COMMAND(a1); update-komento
	jsr	_LVODoIO(a6)		; kirjoitetaan puskurit levylle
	tst.l	d0			; virhe?
WrBlke	rts

; Boottilohkon alkuun kopioitava koodi, joka hoitaa varsinaisen käyttäjän
; aliohjelman kutsumisen sekä koneen boottauksen jatkamisen käyttäjän oh-
; jelman ajon päätyttyä.
INITIAL	dc.b	'DOS',0			; levykkeen tyyppi (OFS)
	dc.l	0			; tarkistussumma (lasketaan myöh.)
	dc.l	880			; levykkeen root blockin numero
	movem.l	d0-d7/a0-a6,-(sp)	; rekisterit pinoon
	clr.l	IO_LENGTH(a1)		; moottori sammuksiin
	move.w	#TD_MOTOR,IO_COMMAND(a1); moottorin ohjauskomento
	move.l	$4,a6			; sammutetaan moottori
	jsr	_LVODoIO(a6)
	movem.l	(sp),d0-d7/a0-a6	; luetaan rekisterien arvot
	bsr.s	USERCODE		; kutsutaan käyttäjän koodia
	movem.l	(sp)+,d0-d7/a0-a6	; otetaan rekisterit pinosta
	lea	dosname(pc),a1		; Dos-kirjaston nimi
	jsr	_LVOFindResident(a6)	; etsitään dos-resident modulia
	tst.l	d0
	beq.s	INITIAL_err		; ei löytynyt -> guru
	move.l	d0,a0			; resident-palikka
	move.l	RT_INIT(a0),a0		; resident-palikan alustusrutiini
	moveq	#0,d0			; ei virhettä, jatketaan boottia
	rts
INITIAL_err:
	moveq	#-1,d0			; virhe, guruillaan
	rts
dosname	dc.b	'dos.library',0		; nimen pituus parillinen

USERCODE	; käyttäjän koodi tulee tähän

TDname	dc.b	'trackdisk.device',0	; trackdiskin nimi
eltxt	dc.b	'Error loading "%s"',LF,0
optxt	dc.b	'Unable to open unit df%ld:',LF,0
wrtxt	dc.b	'Could not write boot block - error %ld.',LF,0
ootxt	dc.b	'Only one segment allowed.',LF,0
tltxt	dc.b	'Length %ld (space %ld).',LF,0
fntxt	dc.b	'error in filename',LF,0
ddtxt	dc.b	'give destination drive dfN: where N is 0...3',LF,0
	ds.w	0

	end

