	NOLIST
;	This file is part of picide, ATA(PI) interface to PIC18 family MCUs.
;	Copyright (C) 2004-5 Toby Thain, toby@telegraphics.com.au

;	This program is free software; you can redistribute it and/or modify
;	it under the terms of the GNU General Public License as published by  
;	the Free Software Foundation; either version 2 of the License, or
;	(at your option) any later version.

;	This program is distributed in the hope that it will be useful,
;	but WITHOUT ANY WARRANTY; without even the implied warranty of
;	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;	GNU General Public License for more details.

;	You should have received a copy of the GNU General Public License  
;	along with this program; if not, write to the Free Software
;	Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
	LIST

	TITLE picide
	SUBTITLE "Main program"

;; Version history
;; 16-Oct-2004: 0.1 - started by Toby
;; 23-Feb-2005: 1.0 - public release under GPL

; this module contains the PIC reset & interrupt vectors, and main program.

; main program currently:
; - initialises RS232 and IDE
; - probes one or two ATA(PI) devices (master, and slave if attached)
; - does some drive initialisation (e.g. sets standby/idle timer)
; - enters command monitor (see monitor.asm)

#include "defs.inc"
#include "idecmd.inc"
#include "atapidefs.inc"

	extern init_rs232,putch,getch
	extern dumpsec,monitor,dumpid

	extern init_ide,ide_identify,ide_idle
	extern ide_lbacmd,ide_rdata,ide_wdata
	extern ide_probe,ide_atapiiddev,ide_checkerr
	extern time400ms,delay1ms,delay2ms
	extern idecmd,seccnt,feat,lba0,lba8,lba16,lba24
	extern dasplo,dasphi
	extern atapi_read10,atapi_readcd,atapi_reqsense,atapi_modesense,atapi_zeropkt
	extern atapi_readcapacity,atapi_checkerr,atapipkt

	extern putdec,putdec16,puthex,dec16,hexdigit,rnd8,LFSRVALUEL
	extern init_tick,start_time,end_time

	global stop,checkerr,devtype,tick1khz

;;; DATA -------------------------------------------------

	udata_acs
cnt	res 1
probecnt res 1	; # devices to probe
errcode	res 1
loopcnt	res 2
devtype	res 2	; device types, two entries
is_cnt	res 1	; used by initsec
temp	res 2
tick1khz res 3	; 1kHz interrupt driven tick count (if used)

;;; RESET vector --------------------------------------------
	org 0
	bra start

;;; interrupt vectors & service routines ----------------------------
	org 0x8		; high priority vector
inthi:	btfsc PIR1,TMR2IF	; was it ours?
	  bra timer2int		; yes
	retfie

	org 0x18	; low priority vector
intlo:	retfie

	org 0x40

	; service 1kHz tick interrupt (if used)
timer2int:	; interrupt overhead: around 9 cycles per 10,000
	bcf PIR1,TMR2IF	; clear timer int flag
	incf tick1khz	; bump tick counter
	bnc intdone
	incf tick1khz+1
	bnc intdone
	incf tick1khz+2	; 24-bit timer overflows every 4hrs 39mins approx
intdone:retfie 1	; restore Wreg, STATUS from fast register stack

;;; MAIN PROGRAM -----------------------------------------------

start:	;movlw b'00011111'	; watchdog timer enable, prescale 1:32768
	;movwf CONFIG2H
	clrf INTCON	; disable all interrupts
  IF 0
	; setup 1kHz tick interrupt
	call init_tick
	bsf RCON,IPEN	; enable interrupt priority
	bsf IPR1,TMR2IP	; make Timer2 high priority interrupt
	bsf INTCON,GIEH	; enable high priority interrupts
  ENDIF

	; initialise serial and IDE
	call init_rs232
	call init_ide	; returns # of possible devices in W (1 or 2)
	movwf probecnt

	_puts "\r\nPIC START\r\n\0"
	
  IF 0	; debugging only - log time for DASP assert (IDE slave)
	_puts "DASP_ @ 25.6us x \0"
	movlw low (15625)
	addwf dasplo,w
	movwf dec16
	movlw high (15625)
	addwfc dasphi,w
	movwf dec16+1
	call putdec16
	_crlf
  ENDIF

  IF 0
	_puts "Probe \0"
	movf probecnt,w
	call hexdigit
	_crlf
  ENDIF
	clrf devtype+0
	clrf devtype+1

	movlw IDE_DRVHEAD_LBA|IDE_DEVICE1 ; default value for drive/head sel
	movwf lba24		; select device 1
	btfss probecnt,1	; probe two?
	  bra probe0
	; probe device 1
	_putstr msg_probe
	_puts "1: \0"
	call ide_probe		; leaves device type in Wreg
	movwf devtype+1
probe0:	; probe device 0
	bcf lba24,DEVBIT	; select device 0
	_putstr msg_probe
	_puts "0: \0"
	call ide_probe		; leaves device type in Wreg
	movwf devtype+0

	; do any device initialisation
	
	movlw IDE_DRVHEAD_LBA	; default value for drive/head sel
	movwf lba24		; select drive 0
	movf devtype+0,w
	rcall initdev
	bsf lba24,DEVBIT	; select device 1
	movf devtype+1,w
	rcall initdev

	_puts "\r\nReady\r\n\0"

	goto monitor		; get user commands

	; we use this twice
msg_probe: dw "Device \0"
;---------------------------------------------------------

stop:	_puts "\r\nSTOP.\r\n\0"
	clrwdt
	bra $-2
;---------------------------------------------------------

checkerr:
	addlw 0	; set flags on W
	bz noerr
	movwf errcode
	_puts "Error \0"
	movf errcode,w
	call puthex
	_putchar ':'
	call ide_checkerr
	;bra stop
	;movf errcode,w
noerr:	return
;---------------------------------------------------------
	
initdev:
	xorlw DEV_ATA
	bnz notata

; ----- ATA-specific device init
	; always set ATA drive's idle/standby timer
	_puts "Enbl standby\r\n\0"
	movlw 60 ; 5 minutes-ish
	call ide_idle
	rcall checkerr

  IF 0	; test code to read ATA sectors
	movlw 5
	movwf temp+1
	clrf lba16
	clrf lba8
	clrf lba0

atatest:
	clrf temp
	incf lba8

	movlw high (65536-10)
	movwf loopcnt+1
	movlw low (65536-10)
	movwf loopcnt
	movwf LFSRVALUEL

sec:
	_puts "  Rd sec 0x\0"
	
	movlw 1
	movwf seccnt
	;call initsec	; pre-fill sector buffer, for debugging


	;call rnd8	; scramble high LBA address bits -
	;movwf lba16	; enable this to deliberately trigger Read errors.
	call rnd8
	movwf lba8
	call rnd8
	movwf lba0

	movf lba16,w
	call puthex
	movf lba8,w
	call puthex
	movf lba0,w
	call puthex
	_crlf
	; lba24 already set up with LBA high bits = 0
	;_puts "lbacmd,\0"
	movlw IDE_CMD_READ
	call ide_lbacmd
	addlw 0
	bnz oops
	;_puts "rdata,\0"

	movlw 0	; 256 words, seccnt already set
	call ide_rdata
	call ide_checkerr	; always check error
	;_puts " ok\r\n\0"
	movlw 1;32
	call dumpsec
	tstfsz temp
	  bra notfst
	setf temp
	call start_time
notfst:

	incf lba0
	bra another

oops:	
	call ide_checkerr
another:
	incf loopcnt
	bnz sec
	incf loopcnt+1
	bnz sec
	call end_time
	decfsz temp+1
	bra atatest
  ENDIF
  
  	; done with ATA specific init
	bra alldrives
	
notata:	xorlw DEV_ATAPI ^ DEV_ATA
	bz isatapi
	goto dunno

; ----- ATAPI specific device init
isatapi:

  IF 0
retryms:
	_puts "\r\nMode Sense \0"
	call atapi_zeropkt
	movlw 0x2a	; CD-ROM Capabilities & Mechanical Status page
	movwf ATAPI_PAGECTL
	movlw high ATAPI_TRANSFER_MAX
	movwf ATAPI_LEN8
	movlw low ATAPI_TRANSFER_MAX
	movwf ATAPI_LEN0
	call atapi_modesense
	xorlw ATAPI_RETRY
	bz retryms
	call atapi_checkerr
	tstfsz WREG
	  bra bad

	_puts "Capabilities:\0"
	;movlw 2
	;call dumpsec
	BANKSEL secbuf
	; note that mode page itself is preceded by the 8-byte "mode parameter header"
	movf secbuf+(8+5),w,1
	_putstrbits "\000 UPC\000 ISRC\000 C2ptrs\000 RWD&C\000 RWsupp\000 DAaccu\000 CDDA\000"
	_crlf
	_puts "Max: \0"
	movff secbuf+(8+8),dec16+1
	movff secbuf+(8+9),dec16
	call putdec16
	_puts "KB/s\r\n\0"
bad:
  ENDIF

  IF 0
	; starting LBA
	movlw 1
	movwf temp+1
#define NSEC -101
	movlw NSEC
	movwf temp
rcdretry:
	_putchar 'R'
rcdloop:
	call atapi_zeropkt
	movlw b'001'<<2
	movwf atapipkt+1	; expected sector type
	movff temp+1,ATAPI_LBA8
	movff temp,ATAPI_LBA0
	movlw 1
	movwf ATAPI_LEN0
	movlw b'00010000'	; user data, no headers
	movwf atapipkt+9	; flags
	movlw b'001'		; RAW subchannel
	movwf atapipkt+10	; subchannel data selection
	;movf temp+1,w
	;call puthex
	;movf temp,w
	;call puthex
	;_putchar '*'
	call atapi_readcd
	xorlw ATAPI_RETRY
	bz rcdretry		; retry command
	xorlw ATAPI_RETRY	; restore error code
	bnz bad			; some other error!

	movf temp,w
	xorlw NSEC
	bnz notfirst
	call start_time
notfirst:
	;_putchar '+'
	;movlw 1 ;ATAPI_CDDASECTOR/16
	;call dumpsec

	incf temp
	bnz rcdloop
	_putchar '.'
	call end_time

	bra alldrives

bad:	call atapi_checkerr
	_puts "ReadCD error\r\n\0"
	bra alldrives
  ENDIF

  IF 1
	; ...try to read some random sectors
	; first try sector 16
	movlw 0
	movwf temp
	clrf temp+1
	clrf temp+2

	movlw 4
	movwf loopcnt
	movwf LFSRVALUEL
loopread:

retryread:
	_puts "\r\nATAPI Read  Sec=0x\0"
	call atapi_zeropkt

	;movf temp+2,w
	;movwf ATAPI_LBA16
	;call puthex
	movf temp+1,w
	movwf ATAPI_LBA8
	call puthex
	movf temp,w
	movwf ATAPI_LBA0
	call puthex
	_crlf

	movlw 1
	movwf ATAPI_LEN0

	call atapi_read10
	xorlw ATAPI_RETRY
	bz retryread
	xorlw ATAPI_RETRY	; restore error
	bnz nogood
	movlw 8 ;2048/16
	call dumpsec
domore:
	call rnd8
	movff LFSRVALUEL,temp+1
	call rnd8
	movff LFSRVALUEL,temp

	decfsz loopcnt
	  bra loopread

	bra alldrives
nogood:	_puts "ERROR\r\n\0"
	bra domore
  ENDIF

	bra alldrives

alldrives:
	; do stuff here for both ATA and ATAPI

dunno:	return
;---------------------------------------------------------

initsec:
	; fill the sector buffer with decreasing byte values (pairs)
	; just for something pretty to look at
	movwf is_cnt	; # of sectors in Wreg on entry
	lfsr 1,secbuf
	clrf cnt
doword:	movff cnt,POSTINC1
	movff cnt,POSTINC1
	decfsz cnt
	bra doword
	decfsz is_cnt
	bra doword
	return

;---------------------------------------------------------
	end
