PIC data logger to CF Card file; CF Card filesystem updated; Data readable via Windows PC software.
 

PIC 18F452 to CF Card to Windows PC Instrumentation Data Collection

Required Environment:

The program is setup to write an instrumentation file of maximum size based on code logic documented in routine 'cf_file_write'. Maximum file size, as the code is implemented, is 32MB. This is because I have only used 16-bit variables to count CF card file segments. 2**16 * 512 = 32MB. Larger file sizes can be generated by modifying the code to use 24-bit variables. A 24-bit variable would accommodate the sector count for the max file size of 4GB.

Instrumentation data is assigned to file: 'PIC_DATA.DAT'. This can be easily changed as it is specified in ROM via a DB string.

Only the file size and first cluster are updated in the directory entry for the file at completion of data collection.

To setup the CF card for use; I format the CF card (FAT16) and then using an edit program save an empty file to "pic_data.dat". The format ensures no data clusters have been allocated and that my file entry will be in the first segment of the root directory. Then its ready to go.

Miscellaneous notes regarding the program:

Code:

;******************************************************************************
;*  CF_DB_Inst.ASM
;*
;*  Revision: 2     Comments somewhat cleaned-up and working code.
;*                  Tested file size to 16MB.
;*
;*  03/09/2008
;******************************************************************************
;*
;*      This program interfaces with a pre-formatted (FAT16) compact flash memory
;*      card in PC Card Memory Mode.  The disk partition table is read to get the
;*      first partition segment offset.  Next the BIOS Parameter Block in the
;*      boot sector is read to verify the CF card filesystem format is FAT16.
;*      The 1st segment of the root directory of the FAT (File Allocation Table)
;*      file system is scanned for a specific file (PIC_DATA.DAT).  If that file
;*      is found, the file is then verified to be empty, zero length, and no cluster
;*      number.  Instrumentation data is collected in the CF card user data area
;*      and linked to the file by mapping the data segments of the file written
;*      to the CF card via the cluster chain mapping; which is written to both
;*      copies of the CF Card FAT (FAT1 & FAT2).  The directory entry of the file
;*      is then updated with the starting cluster chain entry and the filesize
;*      in bytes.  The instrumentation file written to the CF Card is readable
;*      on a Windows PC for further data reduction.
;*
;*      Program performs 8-bit I/O with CF card.
;*
;*      The PIC is clocked via a 4.9152MHz oscillator in my test setup.
;*
;******************************************************************************

    list P=PIC18F452
    include "p18f452.inc"
    errorlevel  -302                ; suppress message 302 from list file

;******************************************************************************
;Configuration bits

    CONFIG  OSC = XT
    CONFIG  OSCS = OFF
    CONFIG  PWRT = ON
    CONFIG  BOR = OFF
    CONFIG  WDT = OFF
    CONFIG  CCP2MUX = OFF
    CONFIG  STVR = OFF
    CONFIG  LVP = OFF
    CONFIG  DEBUG = ON
    CONFIG  CP0 = OFF
    CONFIG  CP1 = OFF
    CONFIG  CP2 = OFF
    CONFIG  CP3 = OFF
    CONFIG  CPB = OFF
    CONFIG  WRT0 = OFF
    CONFIG  WRT1 = OFF
    CONFIG  WRT2 = OFF
    CONFIG  WRT3 = OFF
    CONFIG  WRTB = OFF
    CONFIG  WRTC = OFF
    CONFIG  WRTD = OFF
    CONFIG  EBTR0 = OFF
    CONFIG  EBTR1 = OFF
    CONFIG  EBTR2 = OFF
    CONFIG  EBTR3 = OFF
    CONFIG  EBTRB = OFF

;******************************************************************************
;***** Define substitution text

;   #define     W       0   ; already in "p18f452.inc"
    #define     F       1

;******************************************************************************
;***** Define symbol constants

    constant    EOT=0xFF                ; End Of Table

;******************************************************************************
;***** Define Assembler constants
;
; Banking values for Bank Select Register (BSR)
;
    constant    GPR0=0x00               ; These constants correspond to the
    constant    GPR1=0x01               ;   DATABANK names in the linker script
    constant    GPR2=0x02               ;   for the BANKED RAM locations.  Use
    constant    GPR3=0x03               ;   these constants to load the BSR
    constant    GPR4=0x04               ;   (Bank Select Register) for variable
    constant    GPR5=0x05               ;   access to those assigned to each bank.

    ;   **WARNING** - match these constants to linker script assignments.
    constant    PROG_GPR=GPR0           ; udata prog_vars assigned to gpr0
    constant    CFOPS_GPR=GPR3          ; udata cfops_vars assigned to gpr3
    constant    MATH_GPR=GPR3           ; udata math_vars assigned to gpr3
;
; CF Card command values, 8 bits
;
WR_SEC_CMD  EQU     0x30
RD_SEC_CMD  EQU     0x20
;
; CF Card port equates
;
CF_DATA_IN  EQU     PORTD
CF_DATA_OUT EQU     LATD
CF_ADDR     EQU     LATE
CF_CONTROL  EQU     LATC        ; output    MSB:LSB [LED,-WAIT,-CD1,RESET,RDY,-WE,-OE,-CE1]
CF_STATUS   EQU     PORTC       ; input     MSB:LSB [LED,-WAIT,-CD1,RESET,RDY,-WE,-OE,-CE1]
;                               ; input/output       out   in   in   out  in  out out  out
;
; CF Card control port (PortC) pin equates
;
CE1         EQU     0
OE          EQU     1
WE          EQU     2
RDY         EQU     3
RSET        EQU     4           ; reserved here but not implemented in code
CD1         EQU     5
WAIT        EQU     6
LED         EQU     7
;
; CF Card register addresses - Memory Mapped Addressing
;  low 3 bits as follows: (X,X,X,X,X,A2,A1,A0)
;
DATA_REG    EQU     0x00                ; address of data register
ERROR_REG   EQU     0x01                ; address of error register
FEATUR_REG  EQU     0x01                ; address of features register
SEC_CNT_REG EQU     0x02                ; address of sector count register
SEC_NUM_REG EQU     0x03                ; address of sector number register
CYL_LO_REG  EQU     0x04                ; address of low cylinder register
CYL_HI_REG  EQU     0x05                ; address of high cylinder register
HEAD_REG    EQU     0x06                ; address of head/drive register
STATUS_REG  EQU     0x07                ; address of status register
COMMAND_REG EQU     0x07                ; address of command register
;
; CF program operation error codes
;
NO_CARD     EQU     0x01                ; CF card not present
BAD_FORMAT  EQU     0x02                ; CF card file system not FAT16
FIL_NOT_FND EQU     0x03                ; file not found in root dir
FILE_IN_USE EQU     0x04                ; file already in use
FILE_FULL   EQU     0x05                ; maximum file size reached
USER_STOP   EQU     0x06                ; user requested stop

;******************************************************************************
;***** Variable definitions
;******************************************************************************

;   Key to naming of multiple byte variables:
;       4-byte variable;    ll = low word,low byte
;                           lh = low word, high byte
;                           hl = high word, low byte
;                           hh = high word,high byte
;       2-byte variable;    lb = low byte
;                           hb = high byte

acs_vars    udata_acs           ;   interrupt and BSR independent variables
;       variables used for context saving
w_temp      res     1                   ; variables used for context saving
status_temp res     1
pclath_temp res     1
;       variables used for instrumentation process
inst_data_b res     1                   ; data byte to write to CF Card data file
inst_flag   res     1                   ; instrumentation interrupt flag
end_inst_fl res     1                   ; user signal to terminate instrumentation
timeout_cnt res     1                   ; timeout counter, inst not done in time
bank_4_full res     1                   ; GPR bank 4 full/empty flag; 0=E, 1=F
bank_5_full res     1                   ; GPR bank 5 full/empty flag; 0=E, 1=F
bnk_4_5_prt res     1                   ; active GPR bank pointer; 0x04 or 0x05
inst_bytcnt res     1                   ; counter for inst bytes written to GPR banks

prog_vars   udata               ;   program variable data
;
;   None so far

cfops_vars  udata               ;   CF Card routine's variable data
;
portC_sh    res     1                   ; PORTA shadow register for RMW proofing
portE_sh    res     1                   ; PORTE shadow register for RMW proofing
cf_io_data  res     1                   ; CF I/O data byte
cf_io_reg   res     1                   ; CF I/O register address
read_ptr_hi res     1                   ; 16-bit pointer (cnt) of bytes read
read_ptr_lo res     1
read_tgt_hi res     1                   ; 16-bit target
read_tgt_lo res     1
bytecnt     res     1                   ; working variable
loop_cntr   res     1                   ; working variable
error_code  res     1                   ; error code for CF routines, reason for stop
t_16bit_num:                            ; temp 16-bit variable for 8-bit numbers
t_16bit_lb  res     1                   ;  needed in 16-bit math routines
t_16bit_hb  res     1
rdf_off_lb  res     1                   ; offset of 'filename' entry in root dir
rdf_off_hb  res     1                   ;  includes 0x400 for bank 4 start address
rtdirsiz_lb res     1                   ; root directory size in sectors(lb:hb)
rtdirsiz_hb res     1
frdirsec_lb res     1                   ; first sector of root directory in FAT1
frdirsec_hb res     1
ffat1sec_lb res     1                   ; first sector of FAT1
ffat1sec_hb res     1
ffat2sec_lb res     1                   ; first sector of FAT2
ffat2sec_hb res     1
fdatasec_lb res     1                   ; first data sector in CF Card partition
fdatasec_hb res     1
fcluscnt_lb res     1                   ; count of clusters needed for file
fcluscnt_hb res     1
fatcloff_lb res     1                   ; cluster chain link offset into FAT sector
fatcloff_hb res     1
filclus_lb  res     1                   ; cluster number for file data
filclus_hb  res     1
clus_swc_lb res     1                   ; cluster entries written in sector
clus_swc_hb res     1
fil_size_ll res     1                   ; 4-byte; filesize
fil_size_lh res     1
fil_size_hl res     1
fil_size_hh res     1
fil_swrt_lb res     1                   ; 2-byte; file sector swritten cnt
fil_swrt_hb res     1
fil_dsec_lb res     1                   ; 2-byte; writing this data sector
fil_dsec_hb res     1
fil_sbwc_lb res     1                   ; 2-byte; bytes written in current sector
fil_sbwc_hb res     1
;cf_rw_structure                        ; Note: keep the following six variables in order
cf_rw_scnt  res     1                   ; sector cnt to read/write; always = 1
cf_rw_snum  res     1                   ; sector number; LBA 7-0
cf_rw_cyllo res     1                   ; sector number: LBA 15-8
cf_rw_cylhi res     1                   ; sector number: LBA 23-16; always 0
cf_rw_head  res     1                   ; always 0xE0
cf_rw_cmd   res     1                   ; read/write command value
;end_rw_structure
cf_rw_reg   res     1                   ; register offset for read/write sector cmd

;   This directory structure left for documentation purposes only
;dir_entry_struct:                      ; defines a FAT directory entry
;dir_name       res     11                  ;   short name
;dir_attr       res     1                   ;   file attributes
;dir_NTres      res     1                   ;   reserved for Windows NT
;dir_time_10    res     1                   ;   creation time, millisecond stamp
;dir_time_cr    res     2                   ;   time file created
;dir_date_cr    res     2                   ;   date file created
;dir_lstacc     res     2                   ;   date last accessed
;dir_clus_hi    res     2                   ;   high word of first cluster, 0 for FAT16
;dir_time_wr    res     2                   ;   time of last write
;dir_date_wr    res     2                   ;   date of last write
;dir_clus       res     2                   ;   16-bit: 1st cluster of file
;dir_fsize      res     4                   ;   32-bit: filesize


;                   **************************************
;                       start: CF Partition Table data
;                   **************************************
pt_vars     udata           ;   variable data corresponding to pt_parm_tbl
;                           ; MUST CORRESPOND with pt_parm_tbl below
pt_parms:
pt_active   res     1                   ; 0x80 if active (bootable), otherwise 0x00
pt_strt_chs res     3                   ; start of partition in CHS addressing
pt_type     res     1                   ; partition type: 4, 6, 14 (all FAT16 types)
pt_end_chs  res     3                   ; end of partition in CHS addressing
pt_ofset_ll res     1                   ; 4-byte number: partition offset (physical)
pt_ofset_lh res     1
pt_ofset_hl res     1
pt_ofset_hh res     1
pt_size_ll  res     1                   ; 4-byte number: partition size in sectors
pt_size_lh  res     1
pt_size_hl  res     1
pt_size_hh  res     1

pt_prm_tbl      code
;                           ; MUST CORRESPOND with pt_vars above
;                           ; Note: byte offset must be in ascending order
;                   byte        number
;                   offset      bytes       description
;                   ----------  ------      -----------
pt_parm_tbl db      0x01, 0xBE, 0x00,0x01   ; PT; Partition active
            db      0x01, 0xBF, 0x00,0x03   ; PT; Start sector in CHS addressing
            db      0x01, 0xC2, 0x00,0x01   ; PT; Type
            db      0x01, 0xC3, 0x00,0x03   ; PT; End sector in CHS addressing
            db      0x01, 0xC6, 0x00,0x04   ; PT; Relative offset in sectors (LBA)
            db      0x01, 0xCA, 0x00,0x04   ; PT; Size in sectors
            db      EOT                     ; end of table marker
;                   ************************************
;                       end: CF Partition Table data
;                   ************************************

;                   *****************************************************
;                       start: Boot Sector & BIOS Parameter Block data
;                   *****************************************************
bs_bio_vars udata           ;   variable data corresponding to bs_parm_tbl
;                           ; MUST CORRESPOND with bs_parm_tbl below
bs_bio_parms:
bpb_bps_lb  res     1                   ; 2-byte number: bytes per sector
bpb_bps_hb  res     1
bpb_spc     res     1                   ; sectors per cluster
bpb_res_lb  res     1                   ; 2-byte number: reserved sector count
bpb_res_hb  res     1
bpb_num_fat res     1                   ; number of FATs
bpb_rent_lb res     1                   ; 2-byte number: root directory entries
bpb_rent_hb res     1
bpb_fsiz_lb res     1                   ; 2-byte number: sectors per FAT
bpb_fsiz_hb res     1
bpb_hids_ll res     1                   ; 4-byte number: hidden sector count
bpb_hids_lh res     1
bpb_hids_hl res     1
bpb_hids_hh res     1
bpb_tsec_ll res     1                   ; 4-byte number: total sectors in volume
bpb_tsec_lh res     1
bpb_tsec_hl res     1
bpb_tsec_hh res     1
bpb_ext_sig res     1                   ; signature: should be 0x29
bpb_fil_typ res     8                   ; file system type: should be FAT16
bpb_sig_510 res     1                   ; signature: should be 0x55
bpb_sig_511 res     1                   ; signature: should be 0xAA

bs_prm_tbl  code
;                           ; MUST CORRESPOND with bs_bio_parms above
;                           ; Note: byte offset must be in ascending order
;                   byte        number
;                   offset      bytes       description
;                   ----------  ------      -----------
bs_parm_tbl db      0x00, 0x0B, 0x00,0x02   ; BPB; Count of bytes per sector
            db      0x00, 0x0D, 0x00,0x01   ; BPB; Sectors per cluster
            db      0x00, 0x0E, 0x00,0x02   ; BPB; Number of reserved sectors
            db      0x00, 0x10, 0x00,0x01   ; BPB; Number of FATs
            db      0x00, 0x11, 0x00,0x02   ; BPB; Cnt of 32-byte entries in root dir
            db      0x00, 0x16, 0x00,0x02   ; BPB; FAT size in sectors
            db      0x00, 0x1C, 0x00,0x04   ; BPB; Hidden sectors
            db      0x00, 0x20, 0x00,0x04   ; BPB; Total sector count
            db      0x00, 0x26, 0x00,0x01   ; BPB; Extended Boot Signature
            db      0x00, 0x36, 0x00,0x08   ; BPB; File system type (FAT16)
            db      0x01, 0xFE, 0x00,0x01   ; BPB; Boot Signature = 0x55
            db      0x01, 0xFF, 0x00,0x01   ; BPB; Boot Signature = 0xAA
            db      EOT                     ; end of table marker
;                   ***************************************************
;                       end: Boot Sector & BIOS Parameter Block data
;                   ***************************************************

math_vars   udata           ;   math routine variables
;
math_temp   res     1               ; working variable
bsr_temp    res     1               ; save/restore BSR register
;   mult16x16U variables
a1          res     1               ; 16-bit Multiplicand L:H
a2          res     1
b1          res     1               ; 16-bit Multiplier L:H
b2          res     1
prod_lwlb   res     1               ; 32-bit Product - low word:low byte
prod_lwhb   res     1
prod_hwlb   res     1
prod_hwhb   res     1               ; 32-bit Product - high word:high byte
;   div16x16U variables
accAlo      res     1               ; 16-bit Denominator L:H
accAhi      res     1
quotient_lb:
accBlo      res     1               ; 16-bit Numerator (also Quotient) L:H
quotient_hb:
accBhi      res     1
remain_lb:
accClo      res     1               ; 16-bit Remainder L:H
remain_hb:
accChi      res     1
accDlo      res     1               ; 16-bit working registers L:H
accDhi      res     1
;   sub16U variables
diff_lb:
sub_dst_lb  res     1
diff_hb:
sub_dst_hb  res     1
sub_src_lb  res     1
sub_src_hb  res     1
;   add16U variables
sum_lb:
add_dst_lb  res     1
sum_hb:
add_dst_hb  res     1
add_src_lb  res     1
add_src_hb  res     1

pgm_strings code                ;   constant strings used in program
;
fat16_str   db      "FAT16"     ; const str for required FAT type
end_fatstr:
file_name   db      "PIC_DATADAT" ; const str for filename, BPB does not store '.'
end_filename:                     ; between filename 'PIC_DATA' and extension 'DAT'

    constant    FATSTRLEN = end_fatstr - fat16_str - 1  ; -1 for pad byte
    constant    FILSTRLEN = end_filename - file_name - 1    ; -1 for pad byte
                                ; the strings above are odd num of char, so
                                ; assembler adds a pad byte to make a full word

;******************************************************************************
;***** Interrupt Routines
;******************************************************************************
            code    0x0000
; Start at the reset vector.
            org     0x000               ; Reset interrupt vector
            clrf    INTCON,ACCESS       ; Disable all interrupts
            goto    sys_init

;******************************************************************************
; High priority interrupt vector.
            org     0x008
            nop                         ; not currently using high priority ints
high_isr_end:
            retfie  FAST                ; return from interrupt, enable high ints
                                        ;  shadow registers used

;******************************************************************************
; Low priority interrupt vector.
            org     0x018
            movwf   w_temp,ACCESS       ; save current W register contents
            movf    STATUS,W,ACCESS     ; move STATUS to w
            clrf    STATUS,ACCESS
            movwf   status_temp,ACCESS  ; save contents of STATUS register
            movff   PCLATH,pclath_temp  ; save current copy of PCLATH
            clrf    PCLATH,ACCESS       ; reset PCLATH to page 0
;   Check if Timer1 interrupt - used to initiate instrumentation activity.
            btfss   PIR1,TMR1IF,ACCESS
            goto    check_int1          ; No, so check next source
            bcf     PIR1,TMR1IF,ACCESS  ; clear interrupt flag
    ;   Reset Timer1 count preset.
            clrf    TMR1L,ACCESS        ; prevents inc of high byte during init
            movlw   0xFC                ; FFFF-FCFF=0300=768 cnts
            movwf   TMR1H,ACCESS        ; so, int every 768 instructions
            movlw   0xFF
            movwf   TMR1L,ACCESS
    ;   Set instrumentation data available flag.
            bsf     inst_flag,0,ACCESS  ; set instrument available flag
            goto    low_isr_end
check_int1:
;   Check if Int1 interrupt - used to terminate instrumentation activity.
;       Make same priority as interrupt used to write instrumentation so that
;       activity completes for the pending inst value before stopping activity.
            btfss   INTCON3,INT1IF,ACCESS
            goto    low_isr_end         ; No, so exit
            bcf     INTCON3,INT1IF,ACCESS ; clear the interrupt flag
            bcf     INTCON3,INT1IE,ACCESS ; disable further INT1 interrupts
;       Disable source of instrumentation interrupts.
            bcf     PIE1,TMR1IE,ACCESS  ; disable Timer1 interrupts
            bsf     end_inst_fl,0,ACCESS ; set end inst flag
            goto    low_isr_end
low_isr_end:
            clrf    STATUS,ACCESS       ; ensure file register bank set to 0
            movff   pclath_temp,PCLATH  ; restore pre-isr PCLATH register
            movff   status_temp,STATUS  ; restore pre-isr STATUS register
            swapf   w_temp,F,ACCESS     ; swap w_temp nibbles and return to w_temp
            swapf   w_temp,W,ACCESS     ; swap w_temp into W

            retfie  0                   ; return from interrupt, enable low ints
                                        ;  shadow registers not used

;******************************************************************************
;***** Initialization Routine
;******************************************************************************
sys_init:
    ;   Interrupt initialization.
            bcf     INTCON,GIEH,ACCESS  ; disable all interrupts
    ;   PORTC/TRISC initialization - CF Card control lines.
            clrf    PORTC,ACCESS
            clrf    LATC,ACCESS         ; clear PortC output
            movlw   B'01101000'         ; MSB:LSB [LED,-WAIT,-CD1,RESET,RDY,-WE,-OE,-CE1]
            movwf   TRISC,ACCESS        ; PortC [0,1,2,4,7] output; [3,5,6] input
    ;   PORTD/TRISD initialization - CF Card data bus.
            clrf    LATD,ACCESS         ; clear PortD output
            movlw   B'00000000'         ; set RC7-RC0 to outputs
            movwf   TRISD,ACCESS
    ;   PORTE/TRISE initialization - CF Card address bus.
            clrf    LATE,ACCESS         ; clear PortE output
            movlw   B'00000000'         ; set RE2-RE0 to outputs
            movwf   TRISE,ACCESS
    ;   Enable priority on interrupts.
            bsf     RCON,IPEN,ACCESS    ; enable interrupt priority levels
    ;   Timer1 setup.
            bcf     IPR1,TMR1IP,ACCESS  ; set Timer1 interrupt to low priority
            movlw   B'00000000'
            movwf   T1CON,ACCESS        ; pre & post scaler 1:1, timer off
            bcf     PIR1,TMR1IF,ACCESS  ; clear interrupt flag
            bsf     PIE1,TMR1IE,ACCESS  ; enable Timer1 interrupts
            movlw   0xFC                ; FFFF-FCFF=0300h=d'768' cnts
            movwf   TMR1H,ACCESS        ; timer rollover every 768 instructions
            movlw   0xFF                ; so, collect inst data every 625 usec
            movwf   TMR1L,ACCESS        ;   (=1600 bytes/sec)
            clrf    inst_flag,ACCESS    ; clear instrumentation flag
            clrf    timeout_cnt,ACCESS  ; clear inst int timeout cntr
            movlw   0x45                ; initialize inst data to 45h ('E')
            movwf   inst_data_b,ACCESS  ;  this is just a dat pattern value
    ;   External interrupt INT1 setup, used end instrumentation activity.
            bcf     INTCON2,INTEDG1,ACCESS ; interrupt on falling edge
            bcf     INTCON3,INT1IP,ACCESS ; set INT1 to low priority, should be
                                        ;  same priority as instrumentation event
            bcf     INTCON3,INT1IF,ACCESS ; clear interrrupt flag
            bsf     INTCON3,INT1IE,ACCESS ; enable interrupt
            clrf    end_inst_fl,ACCESS  ; clear flag.  Bit 0: 1=end inst
cf_init:
    ;   Set BSR to CF-Ops variable bank.
            movlb   CFOPS_GPR           ; set BSR to CF-ops variables bank
    ;   Initialize CF Card present logic.
            movlw   NO_CARD             ; start out assuming no CF card
            movwf   error_code,BANKED
            movff   CF_STATUS,portC_sh  ; get value of portC
            bsf     portC_sh,LED,BANKED ; turn error LED on
            movff   portC_sh,CF_CONTROL ; using PortA shadow register
    ;   Init fixed portion of CF read/write structure.
            movlw   0x01
            movwf   cf_rw_scnt,BANKED   ; always read/write 1 sector at a time
            clrf    cf_rw_cylhi,BANKED  ; sector address limited to 16-bits
            movlw   0xE0
            movwf   cf_rw_head,BANKED   ; set LBA addressing, high sec addr = 0
    ;   Start'em up!
            goto    main

;******************************************************************************
;***** Main Routine *****
;******************************************************************************
;
main:
;
;   verify CF Card present
check_card:
            btfsc   CF_STATUS,CD1,ACCESS ; check for CF Card present, LATC[5]
            goto    check_card          ; loop till card inserted
;   clear no card error condition
            clrf    error_code,BANKED   ; clear error code
            movff   CF_STATUS,portC_sh  ; get value of portC
            bcf     portC_sh,LED,BANKED ; turn error LED off
            movff   portC_sh,CF_CONTROL ; using PortA shadow register

;***NOTE*** without the stanby mode below, the first read cmd does not work right.
;   Probably issueing a RESET to the CF card first thing would work also.
;   set CF to STANDBY mode
            movlw   B'00000111'         ; MSB:LSB [-WAIT,-CD1,RESET,RDY,-WE,-OE,-CE1]
            movwf   CF_CONTROL,ACCESS   ; set control lines on PortA, STANDBY
            nop                         ; breakpoint opportunity

;   Read CF Card info and setup for file write.
            call    cf_setup_init
;   Just before ready to begin instrumenting data make this call.
            call    cf_file_write_init
;   Enable interrupts and start Timer1.
            bsf     INTCON,GIE,ACCESS
            bsf     INTCON,PEIE,ACCESS  ; enable low priority peripheral ints
            bsf     T1CON,TMR1ON,ACCESS ; start Timer1, used to generate inst data

;   Begin collecting instrumentation data.
inst_loop:
;   Always call 'cf_file_write' first thing in loop as it is the 'background'
;   task that is emptying the GPR instrumentation data buffers to the CF card.
            call    cf_file_write
;   If inst data avail, write it to the GPR inst buffer.
            btfss   inst_flag,0,ACCESS  ; see if inst flag set
            goto    check_quit          ; No, check for user requested stop
    ;   Following instruction alternates inst data between 45h ('E') and 54h ('T').
    ;   This gives a test pattern to data for examining the results.
            swapf   inst_data_b,F,ACCESS
;   Write instrumentation data byte to the GPR buffer.
    ;   Copy inst data to active GPR bank.
            movff   bnk_4_5_prt,FSR2H   ; setup indirect address in active GPR bank
            movff   inst_bytcnt,FSR2L
            movff   inst_data_b,INDF2   ; move inst data to GPR bank
    ;   Increment count of bytes written to active bank.
            incf    inst_bytcnt,F,ACCESS
    ;   Test if counter has rolled over, if so active bank is full.
            btfss   STATUS,Z,ACCESS
            goto    write_inst_done     ; no rollover, nothing else to do
    ;   Counter has rolled over meaning we have written 256 bytes, the active bank
    ;   is full.  So switch to the other bank.
            movlw   0x04
            xorwf   bnk_4_5_prt,W,ACCESS ; was 'bnk_4_5_prt' set to bank 4
            btfss   STATUS,Z,ACCESS     ; Yes, so set active GPR bank to 5
            goto    set_active_4        ; No, so set active GPR bank to 4
    ;   Set active bank to GPR bank 5.
            movlw   0x05
            movwf   bnk_4_5_prt,ACCESS
    ;   Test for timeout on emptying bank.  If we are trying to write data
    ;   to the CF card too fast this variable will still be set meaning we
    ;   are missing some data.
            btfsc   bank_4_full,0,ACCESS
            incf    timeout_cnt,F,ACCESS ; Increment counter if timeout
    ;   Set bank 4 full flag.
            bsf     bank_4_full,0,ACCESS
            goto    write_inst_done
set_active_4:
    ;   Set active bank to GPR bank 4.
            movlw   0x04
            movwf   bnk_4_5_prt,ACCESS
    ;   Test for timeout on emptying bank.
            btfsc   bank_5_full,0,ACCESS
            incf    timeout_cnt,F,ACCESS ; Increment counter if timeout
    ;   Set bank 5 full flag.
            bsf     bank_5_full,0,ACCESS
write_inst_done:
    ;   Clear flag after writing inst data to buffer.
            bcf     inst_flag,0,ACCESS  ; reset inst data available flag
check_quit:
;   Check if user requested end to instrumentation.
            btfss   end_inst_fl,0,ACCESS ; flag set?  Bit 0: 1=end inst
            goto    inst_loop           ; No, so stay in loop
;   User requested stop.
;   To close instrumentation file and perform update of CF Card root directory
;   and FAT structures make this call.
            call    cf_file_close
            movlw   USER_STOP
            call    error_stop          ; Does not return!!!

;******************************************************************************
;******************************************************************************
;           ***** Subroutines *****
;******************************************************************************
;******************************************************************************
;
error_stop:
;   Loads error code passed in WREG to error variable, turns on error LED,
;   puts the CF Card in STANDBY mode, and performs a tight loop on last
;   instruction, effectively stopping.
;
;   Record error code.
            movwf   error_code,BANKED

;   Set CF to STANDBY mode and turn on LED.
            movlw   B'10000111'         ; MSB:LSB [LED,-WAIT,-CD1,RESET,RDY,-WE,-OE,-CE1]
            movwf   CF_CONTROL,ACCESS   ; set control lines on PortA, STANDBY
;   effectively stops here in a tight loop
            goto    $                   ; stop

;
cf_check_ready:
;
;   Loops until the CF Card is ready for next command.
;       CF_CONTROL (PortC) layout; [MSB:LSB]; [LED,-WAIT,-CD1,RESET,RDY,-WE,-OE,-CE1]
;
            btfss   CF_STATUS,RDY,ACCESS ; check that CF card is ready, PORTC[3]
            goto    cf_check_ready      ; loop till CF card ready
            return

;
cf_write:
;
;   Writes one byte of data to the CF card I/O buffer.
;   CF_CONTROL (PortC) layout; [MSB:LSB]; [X,-WAIT,-CD1,RESET,RDY,-WE,-OE,-CE1]
;       CF Card register address passed in cf_io_reg.
;       Write data passed in cf_io_data.
;
            clrf    TRISD,ACCESS        ; make data bus an output
            call    cf_check_ready
            movff   cf_io_reg,LATE      ; put register address on LATE
            movff   cf_io_data,LATD     ; put data on data bus, LATD
            movff   CF_STATUS,portC_sh  ; get value of portC
            bcf     portC_sh,CE1,BANKED ; strobe -CE1 line low
            bcf     portC_sh,WE,BANKED  ; strobe -WE line low
            movff   portC_sh,CF_CONTROL ; using PortC shadow register
ck_wr_done:
            btfss   CF_STATUS,WAIT,ACCESS ; wait for CF done, PORTC[6]
            goto    ck_wr_done
            movff   CF_STATUS,portC_sh  ; get value of portC
            bsf     portC_sh,CE1,BANKED ; hold -CE1 line high
            bsf     portC_sh,WE,BANKED  ; hold -WE line high
            movff   portC_sh,CF_CONTROL ; using PortA shadow register
            return

;
cf_sec_rw_cmd:
;
;   Reads/Writes a sector of CF card data.
;   Command parameters should have already been loaded in cf_rw_structure.
;
            movlw   d'6'
            movwf   loop_cntr,BANKED    ; will be writing 6 registers
            movlw   d'2'
            movwf   cf_rw_reg,BANKED    ; starting with reg offset 2
            lfsr    FSR0,cf_rw_scnt     ; point to cf_rw_structure
cf_rw_loop:
            movff   POSTINC0,cf_io_data ; load register data
            movff   cf_rw_reg,cf_io_reg ; load register to load
            call    cf_write
            incf    cf_rw_reg,F,BANKED  ; advance to next register
            decfsz  loop_cntr,F,BANKED  ; loop till all 6 written
            goto    cf_rw_loop
            return

;
cf_read:
;
;   Reads one byte from the CF Card I/O buffer.
;   Data is read from the 'data' register, CF register addr = 0.
;   CF_CONTROL (PortC) layout; [MSB:LSB]; [X,-WAIT,-CD1,RESET,RDY,-WE,-OE,-CE1]
;

            movlw   B'11111111'
            movwf   TRISD,ACCESS        ; make data bus an input
            clrf    CF_ADDR,ACCESS      ; clear A0 address line, LATE
    ;   read one byte
            call    cf_check_ready
            movff   CF_STATUS,portC_sh  ; get value of portC
            bcf     portC_sh,CE1,BANKED ; strobe -CE1 line low
            bcf     portC_sh,OE,BANKED  ; strobe -OE line low
            movff   portC_sh,CF_CONTROL ; using PortA shadow register
ck_rd_done:
            btfss   CF_STATUS,WAIT,ACCESS ; wait for CF done, PORTC[6]
            goto    ck_rd_done
            movff   CF_DATA_IN,cf_io_data ; save data byte
            movff   CF_STATUS,portC_sh  ; get value of portC
            bsf     portC_sh,CE1,BANKED ; hold -CE1 line high
            bsf     portC_sh,OE,BANKED  ; hold -OE line high
            movff   portC_sh,CF_CONTROL ; using PortA shadow register
            return

;
load_table_params:
;
;   Collects the CF Card data defined in ROM table.  The table pointer registers
;   have already been setup by the calling routine.  Locates variables defined in
;   the ROM table by offset into the CF card I/O buffer and copies the value to GPR.
;

            bsf     EECON1,EEPGD,ACCESS ; access flash program memory
            movlw   0xFF
            movwf   read_ptr_hi,BANKED  ; init byte pointer to -1, will rollover to 0
            movwf   read_ptr_lo,BANKED  ;  on the first increment
tbl_loop:
            tblrd   *+                  ; read table:byte_offset into TABLAT and inc ptr
            movff   TABLAT,WREG         ; WREG=table:byte_offset
            xorlw   EOT                 ; compare table:byte_offset to EOT char
            btfsc   STATUS,Z,ACCESS     ; was last table:byte_offset the EOT
            goto    end_tbl             ; yes, so quit
            movff   TABLAT,read_tgt_hi  ; copy 16-bit offset to target
            tblrd   *+
            movff   TABLAT,read_tgt_lo
find_offset_byte:
            call    cf_read             ; read byte of CF ID data
            incf    read_ptr_lo,F,BANKED ; increment 16-bit pointer
            btfsc   STATUS,C,ACCESS     ; low byte inc cause carry?
            incf    read_ptr_hi,F,BANKED ; yes, so inc high byte
            call    compr_ptr_to_tgt    ; we there yet?
            btfss   STATUS,Z,ACCESS     ; if cmpr X=Y then Z=1
            goto    find_offset_byte    ; No, read next CF id byte
            movlw   0x01
            movwf   bytecnt,BANKED      ; 1 byte per read, 1 byte read
;       Store CF ID value in GPR variables.
            movff   cf_io_data,POSTINC0
;       Read number of CF ID data bytes for table entry.
            tblrd   *+                  ; throw away pad byte
            tblrd   *+                  ; read table:bytes into TABLAT and inc ptr
            movff   TABLAT,WREG         ; WREG=table:bytes
            subwf   bytecnt,W,BANKED    ; bytecnt = table:bytes ?
            btfsc   STATUS,Z,ACCESS
            goto    tbl_loop            ; YES, get next table:word
byte_loop:
;       Get byte of CF Card data.
            call    cf_read             ; read byte of CF ID data
            incf    read_ptr_lo,F,BANKED ; increment 16-bit pointer
            btfsc   STATUS,C,ACCESS     ; low byte inc cause carry?
            incf    read_ptr_hi,F,BANKED ; yes, so inc high byte
;       Store CF card value in GPR variables.
            movff   cf_io_data,POSTINC0
;       Adjust count of bytes read.
            incf    bytecnt,F,BANKED    ; inc cnt of bytes read
;       Test to see if we've read all the bytes required.
            movff   TABLAT,WREG         ; WREG=table:bytes
            subwf   bytecnt,W,BANKED    ; bytecnt = table:bytes ?
            btfss   STATUS,Z,ACCESS
            goto    byte_loop           ; NO, get next CF ID data
            goto    tbl_loop            ; YES, so get next table entry
end_tbl:
            return

;
compr_ptr_to_tgt:
;
; Signed and unsigned 16 bit comparison routine:
; by David Cary 2001-03-30
; Returns the correct flags (Z and C) to indicate the X=Y, X>Y, or X<=Y.
; Does not modify X or Y (X=read pointer; Y=read target).
; Results:  if X=Y then Z=1.
;           if X>Y then C=0.
;           if X<=Y then C=1.

            movff   read_ptr_hi,WREG
            subwf   read_tgt_hi,W,BANKED ; subtract Y-X
            btfss   STATUS,Z,ACCESS     ; Are they equal?
            goto    compr_ptr_end       ; No
            movff   read_ptr_lo,WREG    ; yes, they are equal -- compare lo
            subwf   read_tgt_lo,W,BANKED ; subtract Y-X
compr_ptr_end:
            return

;
comp_strings:
;
; Compares string in ROM with one in RAM:
; Length of string passed in WREG.
; Results:  if X=Y then STATUS:Z=1, otherwise STATUS:Z=0.
; Result is set by XOR instruction.

            movwf   bytecnt,BANKED      ; initialize counter
compr_str_loop:
            movff   POSTINC0,WREG       ; get char of RAM string
            tblrd   *+                  ; read table: get char of ROM string
            xorwf   TABLAT,W,ACCESS     ; compare RAM to ROM char
            btfss   STATUS,Z,ACCESS     ; if equal, compare till done
            goto    compr_str_end       ; no, so quit
            decfsz  bytecnt,F,BANKED    ; dec string length counter
            goto    compr_str_loop      ; compare next characters
compr_str_end:
            return

;
cf_load_bootsec_parms:
;
;   Sends command to read the CF Card boot sector; sector number: 0.
;   Sector zero contains the partition table at offset 0x1BE.
;
;       setup sector number (LBA 7:0) register
            clrf    cf_rw_snum,BANKED   ; reading sector zero
;       setup cylinder low (LBA 15:8) register
            clrf    cf_rw_cyllo,BANKED  ; reading sector zero
;       setup comand register
            movlw   RD_SEC_CMD          ; get read sector command value
            movwf   cf_rw_cmd,BANKED
            call    cf_sec_rw_cmd
;   Load Boot Sector/BIOS Param Block parameters defined in table structure 'bs_parm_tbl'.
;       Setup indirect addr for bs_bio_parms variables.
            lfsr    FSR0,pt_parms
;       Setup TBLPTR for ROM reads.
            movlw   upper pt_parm_tbl
            movwf   TBLPTRU,ACCESS
            movlw   high pt_parm_tbl
            movwf   TBLPTRH,ACCESS
            movlw   low pt_parm_tbl
            movwf   TBLPTRL,ACCESS
;       Load RAM variables defined in ROM table.
            call    load_table_params   ; load CF Card ID data defined in table pt_parm_tbl
            return

;
cf_load_BIOS_parms:
;
;   Send command to read the 1st partition Boot Sector/BIOS Parameter Block.
;
;       setup sector number (LBA 7:0) register
            movff   pt_ofset_ll,cf_rw_snum ; partition table offset (lwlb)
;       setup cylinder low (LBA 15:8) register
            movff   pt_ofset_lh,cf_rw_cyllo ; partition table offset (lwhb)
;       setup comand register
            movlw   RD_SEC_CMD          ; get read sector command value
            movwf   cf_rw_cmd,BANKED
            call    cf_sec_rw_cmd
;   Load Boot Sector/BIOS Param Block parameters defined in table structure 'bs_parm_tbl'.
;       Setup indirect addr for bs_bio_parms variables.
            lfsr    FSR0,bs_bio_parms
;       Setup TBLPTR for ROM reads.
            movlw   upper bs_parm_tbl
            movwf   TBLPTRU,ACCESS
            movlw   high bs_parm_tbl
            movwf   TBLPTRH,ACCESS
            movlw   low bs_parm_tbl
            movwf   TBLPTRL,ACCESS
;       Load RAM variables defined in ROM table.
            call    load_table_params   ; load CF Card ID data defined in table cf_parm_tbl
            return

;
cf_verify_FAT16:
;
;   Verifies the  BPB file type = FAT16.
;

;       Setup indirect addr for BPB file type variable.
            lfsr    FSR0,bpb_fil_typ
;       Setup TBLPTR for ROM reads.
            movlw   upper fat16_str
            movwf   TBLPTRU,ACCESS
            movlw   high fat16_str
            movwf   TBLPTRH,ACCESS
            movlw   low fat16_str
            movwf   TBLPTRL,ACCESS
            movlw   FATSTRLEN           ; get string length
;       Load RAM variables defined in ROM table.
            call    comp_strings        ; see if strings match
            btfsc   STATUS,Z,ACCESS     ; successful (Z=0)?
            goto    end_verify
wrong_format:
            movlw   BAD_FORMAT          ; CF Card format not FAT16
            call    error_stop
end_verify:
            return

;
cf_read_sector:
;
;   Read CF Flash sector, put in GPR banks: 4 & 5.
;

;       setup comand register
            movlw   RD_SEC_CMD          ; get read sector command value
            movwf   cf_rw_cmd,BANKED
            call    cf_sec_rw_cmd
;   copy sector of data to GPR banks 4 & 5, 256 bytes per bank
            movlw   0x02                ; twice through 256 byte loop
            movwf   loop_cntr,BANKED
;       Setup indirect addr for read data
            lfsr    FSR0,0x400          ; point to GPR bank 4
;       Setup CF read register
            movlw   DATA_REG
            movwf   cf_io_reg,BANKED
rd_bank_loop:
;       Setup read counter.
            clrf    bytecnt,BANKED      ; 0 -> 0xFF inclusive = d'256'
read_data_loop:
            call    cf_read
;       Store CF data value in GPR.
            movff   cf_io_data,POSTINC0
            decfsz  bytecnt,F,BANKED
            goto    read_data_loop
;       finished bank 4 (256 bytes) now do GPR bank 5
            decfsz  loop_cntr,F,BANKED
            goto    rd_bank_loop
            return

;
cf_write_sector:
;
;   Write sector of data in GPR banks: 4 & 5 to CF Card.
;

;   setup comand register
            movlw   WR_SEC_CMD          ; get write sector command value
            movwf   cf_rw_cmd,BANKED
            call    cf_sec_rw_cmd
;   write data in GPR banks 4 & 5, 256 bytes per bank
            movlw   0x02                ; twice through 256 byte loop
            movwf   loop_cntr,BANKED
;       Setup indirect addr for write data
            lfsr    FSR0,0x400          ; point to GPR bank 4
;       Setup CF write register
            movlw   DATA_REG
            movwf   cf_io_reg,BANKED
wr_bank_loop:
;       Setup write counter
            clrf    bytecnt,BANKED      ; 0 -> 0xFF inclusive = d'256'
write_data_loop:
;       write data to CF Card
            movff   POSTINC0,cf_io_data
            call    cf_write
            decfsz  bytecnt,F,BANKED
            goto    write_data_loop
;       finished bank 4 (256 bytes) now do GPR bank 5
            decfsz  loop_cntr,F,BANKED
            goto    wr_bank_loop
            return

;
cf_find_file:
;
;   Loads the first sector of the root directory and searches for filename.
;   If filename not found an error condition is raised.
;

;   Compute first segment of FAT Root Directory, offset by 1st partion offset.
;       FirstRootDirSecNum = BPB_ResvdSecCnt + (BPB_NumFATs * BPB_FATSz16) +
;                            partition offset
            movff   bpb_num_fat,t_16bit_lb ; make 16-bit from 8-bit
            clrf    t_16bit_hb,BANKED
            lfsr    FSR0,t_16bit_lb
            lfsr    FSR1,bpb_fsiz_lb
            call    mult16x16U          ; product = (BPB_NumFATs * BPB_FATSz16)
            lfsr    FSR0,prod_lwlb      ; low word of product
            lfsr    FSR1,bpb_res_lb
            call    add16U              ; sum(1) = BPB_ResvdSecCnt + (BPB_NumFATs * BPB_FATSz16)
            movff   sum_lb,t_16bit_lb   ; move product to temp variable
            movff   sum_hb,t_16bit_hb
            lfsr    FSR0,t_16bit_lb
            lfsr    FSR1,pt_ofset_ll
            call    add16U              ; sum(2) = pt_ofset_ll:lh + sum(1)
            movff   sum_lb,frdirsec_lb  ; save: FirstRootDirSecNum
            movff   sum_hb,frdirsec_hb
;   Get the root directory. Send command to read the 1st sector of root dir.
;       setup sector number (LBA 7:0) register
            movff   frdirsec_lb,cf_rw_snum ; root dir sector (lb)
;       setup cylinder low (LBA 15:8) register
            movff   frdirsec_hb,cf_rw_cyllo ; root dir sector (hb)
            call    cf_read_sector
;   Search 1st sector of root dir for 'file_name'.
;       Each directory entry is 32 bytes, so 512/32=16 entries per sector
            movlw   d'16'
            movwf   loop_cntr           ; setup loop counter
            movlw   0x04
            movwf   t_16bit_hb,BANKED   ; set 16-bit variable to Bank4
            clrf    t_16bit_lb,BANKED
root_dir_loop:
;       Setup indirect addr for root directory sector entries
;           lfsr    FSR0,bpb_fil_typ
            movff   t_16bit_hb,FSR0H
            movff   t_16bit_lb,FSR0L
;       Setup TBLPTR for ROM reads
            movlw   upper file_name
            movwf   TBLPTRU,ACCESS
            movlw   high file_name
            movwf   TBLPTRH,ACCESS
            movlw   low file_name
            movwf   TBLPTRL,ACCESS
            movlw   FILSTRLEN           ; get string length
;       Load RAM variables defined in ROM table.
            call    comp_strings        ; see if strings match
            btfsc   STATUS,Z,ACCESS     ; successful (Z=0)?
            goto    end_find_file
;       increment variable to access next 32-byte entry
            movlw   d'32'
            addwf   t_16bit_lb,F,BANKED
            btfsc   STATUS,C,ACCESS     ; did add cause carry
            incf    t_16bit_hb,F,BANKED ; yes, so inc high byte of variable
;       test for end of sector
            decfsz  loop_cntr,F,BANKED
            goto    root_dir_loop
;       filename not found in first sector
            movlw   FIL_NOT_FND         ; filename not found
            call    error_stop
end_find_file:
;   At this point t_16bit_lb:hb holds offset of 'filename' entry in root
;   directory sector 0, including the 0x400 offset for bank 4.
            movff   t_16bit_lb,rdf_off_lb ; save offset for later
            movff   t_16bit_hb,rdf_off_hb
            return

;
cf_verify_file_empty:
;
;   The last six bytes (cluster & filesize) of the directory entry should
;   be zero.  These are at offset 0x1A from the beginning of the entry.
;   At this point t_16bit_lb:hb holds offset of 'filename' entry in root
;   directory sector 0, including the 0x400 offset for bank 4.

            movlw   0x1A                ; add offset to dir entry offset value
            addwf   t_16bit_lb,F,BANKED
            btfsc   STATUS,C,ACCESS     ; did add cause carry
            incf    t_16bit_hb,F,BANKED ; yes, so inc high byte of variable
            movff   t_16bit_hb,FSR0H    ; setup indirect addr pointer
            movff   t_16bit_lb,FSR0L
            movlw   d'6'
            movwf   loop_cntr           ; setup loop counter
verfy_empty_loop:
            movff   POSTINC0,WREG       ; get dir entry byte
            xorlw   0x00
            btfss   STATUS,Z,ACCESS     ; test if zero
            goto    not_empty_err
            decfsz  loop_cntr,F,BANKED
            goto    verfy_empty_loop
            goto    end_verify_empty
not_empty_err:
;       filename not empty
            movlw   FILE_IN_USE         ; filename already has data
            call    error_stop
end_verify_empty:
            return

;
cf_comp_rootdir_size:
;
;   Compute the root directory size (in sectors) and the first data sector.
;   RootDirSectors = ((BPB_RootEntCnt * 32) + ( BPB_BytesPerSec - 1))/BPB_BytesPerSec

            lfsr    FSR0,bpb_bps_lb     ; init ptr to minuend
            clrf    t_16bit_hb,BANKED
            movlw   d'1'
            movwf   t_16bit_lb,BANKED   ; put 1 in 16-bit temp variable
            lfsr    FSR1,t_16bit_lb     ; init ptr to subtrahend
            call    sub16U              ; diff = (BPB_BytesPerSec - 1)

            clrf    t_16bit_hb,BANKED
            movlw   d'32'
            movwf   t_16bit_lb,BANKED   ; put 32 in 16-bit temp variable
            lfsr    FSR0,t_16bit_lb
            lfsr    FSR1,bpb_rent_lb
            call    mult16x16U          ; product = (BPB_RootEntCnt * 32)

            lfsr    FSR0,prod_lwlb      ; low word of product
            lfsr    FSR1,diff_lb
            call    add16U              ; sum = ((BPB_RootEntCnt * 32) +
                                        ;        ( BPB_BytesPerSec - 1))
            lfsr    FSR0,bpb_bps_lb     ; init ptr to denominator
            lfsr    FSR1,sum_lb         ; init ptr to numerator
            call    div16by16U          ; quotient = RootDirSectors
            movff   quotient_lb,rtdirsiz_lb ; save result in GPR
            movff   quotient_hb,rtdirsiz_hb ; save result in GPR
            return

;
cf_comp_1st_data_sect:
;
;   The start of the CF card data region, the first sector of cluster 2.
;   FirstDataSector = BPBResvdSecCnt + (BPB_NumFATs * BPB_FATSz16) +
;                     RootDirSectors + partition offset
            movff   bpb_num_fat,t_16bit_lb ; make 16-bit from 8-bit
            clrf    t_16bit_hb,BANKED
            lfsr    FSR0,t_16bit_lb
            lfsr    FSR1,bpb_fsiz_lb
            call    mult16x16U          ; product = (BPB_NumFATs * BPB_FATSz16)
            lfsr    FSR0,prod_lwlb      ; low word of product
            lfsr    FSR1,bpb_res_lb
            call    add16U              ; sum(1) = BPB_ResvdSecCnt + (BPB_NumFATs * BPB_FATSz16)
            movff   sum_lb,t_16bit_lb   ; move product to temp variable
            movff   sum_hb,t_16bit_hb
            lfsr    FSR0,t_16bit_lb
            lfsr    FSR1,rtdirsiz_lb
            call    add16U              ; sum(2) = sum(1) + BPBResvdSecCnt
            movff   sum_lb,t_16bit_lb   ; move product to temp variable
            movff   sum_hb,t_16bit_hb
            lfsr    FSR0,t_16bit_lb
            lfsr    FSR1,pt_ofset_ll
            call    add16U              ; sum(3) = pt_ofset_ll:lh + sum(2)
            movff   sum_lb,fdatasec_lb  ; save: FirstDataSector
            movff   sum_hb,fdatasec_hb
            movff   fdatasec_lb,fil_dsec_lb ; set 1st file sector to 1st data sector
            movff   fdatasec_hb,fil_dsec_hb
            return

;
cf_close_dirupdt:
;
;   Update root directory file entry.
;

;   Get the root directory, put in GPR banks 4&5.
;       setup sector number (LBA 7:0) register
            movff   frdirsec_lb,cf_rw_snum ; root dir sector (lb)
;       setup cylinder low (LBA 15:8) register
            movff   frdirsec_hb,cf_rw_cyllo ; root dir sector (hb)
            call    cf_read_sector
;   DIR_FstClusLo:low byte is at offset 0x1A into the directory entry.
            clrf    t_16bit_hb,BANKED
            movlw   0x1A
            movwf   t_16bit_lb,BANKED   ; put 0x1A in 16-bit temp variable
            lfsr    FSR0,t_16bit_lb
            lfsr    FSR1,rdf_off_lb     ; addr of file entry in GPR
            call    add16U              ; sum = addr(file in root dir +
                                        ;        offset to first cluster num)
            movff   sum_lb,FSR0L        ; setup indirect address cluster
            movff   sum_hb,FSR0H
            movlw   d'2'                ; we are starting file at cluster 2
            movff   WREG,INDF0          ; put cluster number 4 in directory entry
;   DIR_FileSize:low byte is at offset 0x1C into the directory entry.
            clrf    t_16bit_hb,BANKED
            movlw   0x1C
            movwf   t_16bit_lb,BANKED   ; put 0x1C in 16-bit temp variable
            lfsr    FSR0,t_16bit_lb
            lfsr    FSR1,rdf_off_lb     ; addr of file entry in GPR
            call    add16U              ; sum = addr(file in root dir +
                                        ;        offset to file size)
            movff   sum_lb,FSR0L        ; setup indirect address to file size
            movff   sum_hb,FSR0H
;       move 4 bytes of filesize to directory entry
            movff   fil_size_ll,POSTINC0
            movff   fil_size_lh,POSTINC0
            movff   fil_size_hl,POSTINC0
            movff   fil_size_hh,POSTINC0
;   Write root directory update back to CF Card.
;       setup sector number (LBA 7:0) register
            movff   frdirsec_lb,cf_rw_snum ; root dir sector (lb)
;       setup cylinder low (LBA 15:8) register
            movff   frdirsec_hb,cf_rw_cyllo ; root dir sector (hb)
            call    cf_write_sector
            return

;
cf_close_clusupdt:
;
;   Update the FAT cluster chains for the file, FAT2 is duplicate of FAT1.
;
;   Compute FAT1 starting sector number = reserved sectors + 1st partion offset
            lfsr    FSR0,bpb_res_lb
            lfsr    FSR1,pt_ofset_ll
            call    add16U              ; sum = pt_ofset + reserved sectors
            movff   sum_lb,ffat1sec_lb
            movff   sum_hb,ffat1sec_hb
;   Compute FAT2 starting sector number
            lfsr    FSR0,ffat1sec_lb
            lfsr    FSR1,bpb_fsiz_lb
            call    add16U              ; sum = FAT1 start sector + FAT sector size
            movff   sum_lb,ffat2sec_lb
            movff   sum_hb,ffat2sec_hb
;   Compute cluster count for file cluster chain.
;       cluster count = file data sectors / sectors per cluster
            movff   bpb_spc,t_16bit_lb  ; convert 8-bit to 16-bit number
            clrf    t_16bit_hb,BANKED
            lfsr    FSR0,t_16bit_lb     ; init ptr to denominator
            lfsr    FSR1,fil_swrt_lb    ; init ptr to numerator
            call    div16by16U          ; quotient = RootDirSectors
            movff   quotient_lb,fcluscnt_lb ; save result in RAM
            movff   quotient_hb,fcluscnt_hb ; save result in RAM
;       round cluster count up if any remainder
            clrf    WREG,ACCESS
            xorwf   remain_lb,W,BANKED
            btfss   STATUS,Z,ACCESS     ; test if remain_lb = 0
            goto    clus_round_up       ; no, so round up
            clrf    WREG,ACCESS
            xorwf   remain_hb,W,BANKED
            btfss   STATUS,Z,ACCESS     ; test if remain_hb = 0
            goto    clus_round_up       ; no, so round up
            goto    build_chain
clus_round_up:
            incf    fcluscnt_lb,F,BANKED
            btfsc   STATUS,C,ACCESS     ; carry out?
            incf    fcluscnt_hb,F,BANKED ; yes, so inc high byte
build_chain:
;   Get first sector of FAT1, put in GPR banks 4&5.
;       setup sector number (LBA 7:0) register
            movff   ffat1sec_lb,cf_rw_snum
;       setup cylinder low (LBA 15:8) register
            movff   ffat1sec_hb,cf_rw_cyllo
            call    cf_read_sector
;   Set first cluster for file = 2, cluster 0 & 1 are reserved.
            movlw   d'2'
            movwf   filclus_lb,BANKED
            clrf    filclus_hb,BANKED
;   Set clusters written to sector count = 2 (cluster 0 & 1 are reserved).
            movlw   d'2'
            movwf   clus_swc_lb,BANKED
            clrf    clus_swc_hb,BANKED
begin_fat_segment:
;   Initialize FAT cluster offset to beginning of GPR bank 4.
            movlw   d'4'
            movwf   fatcloff_hb,BANKED
            clrf    fatcloff_lb,BANKED ; offset = 0x0400
;   Compute FAT cluster offset (2-bytes per cluster).
            movlw   d'2'
            movwf   t_16bit_lb,BANKED
            clrf    t_16bit_hb,BANKED
            lfsr    FSR0,t_16bit_lb
            lfsr    FSR1,clus_swc_lb
            call    mult16x16U          ; product = 2 * clusters written
            lfsr    FSR0,prod_lwlb
            lfsr    FSR1,fatcloff_lb
            call    add16U              ; sum = GRP4 offset + (2 * clusters written)
            movff   sum_lb,fatcloff_lb
            movff   sum_hb,fatcloff_hb
            movff   fatcloff_lb,FSR0L   ; setup indirect address for chain link
            movff   fatcloff_hb,FSR0H
write_chain_loop:
;   Test case of only 1 cluster for file, or, one cluster left to write.
            decf    fcluscnt_lb,W,BANKED ; subtract 1 from low byte
            btfss   STATUS,Z,ACCESS     ; result zero?
            goto    more_than_one       ; no, so create link
            clrf    WREG,ACCESS         ; yes, see if high byte = 0
            xorwf   fcluscnt_hb,W,BANKED
            btfsc   STATUS,Z,ACCESS     ; file cluster count high byte = 0?
            goto    write_eoc           ; yes, so write end-of-chain mark
more_than_one:
;   Each link of cluster chain points to next link, so, increment file cluster number.
            incf    filclus_lb,F,BANKED
            btfsc   STATUS,C,ACCESS
            incf    filclus_hb,F,BANKED
;   Write next file cluster number to current FAT cluster entry.
            movff   filclus_lb,POSTINC0 ; write pointer to next link
            movff   filclus_hb,POSTINC0
;   Decrement count of file clusters.
            decf    fcluscnt_lb,F,BANKED
            btfss   STATUS,C,ACCESS     ; borrow\ set?
            decf    fcluscnt_hb,F,BANKED ; yes, so adjust high byte
;   Increment count of cluster chain entries written to FAT segment.
            incf    clus_swc_lb,F,BANKED ; max value is 256
;   See if 256 chain entries written (counter rollover), if so, sector is full.
            btfss   STATUS,Z,ACCESS     ; test if = 256
            goto    write_chain_loop    ; no, so continue writing to this sector of FAT
;   Write sector of FAT data to CF Card FAT1 from GPR banks 4&5.
;       setup sector number (LBA 7:0) register
            movff   ffat1sec_lb,cf_rw_snum
;       setup cylinder low (LBA 15:8) register
            movff   ffat1sec_hb,cf_rw_cyllo
            call    cf_write_sector
;   Write sector of FAT data to CF Card FAT2 from GPR banks 4&5.
;       setup sector number (LBA 7:0) register
            movff   ffat2sec_lb,cf_rw_snum
;       setup cylinder low (LBA 15:8) register
            movff   ffat2sec_hb,cf_rw_cyllo
            call    cf_write_sector
;   Increment to next sector of FAT1 & FAT2.
            incf    ffat1sec_lb,F,BANKED ; inc FAT1 sector count
            btfsc   STATUS,Z,ACCESS
            incf    ffat1sec_hb,F,BANKED
            incf    ffat2sec_lb,F,BANKED ; inc FAT2 sector count
            btfsc   STATUS,Z,ACCESS
            incf    ffat2sec_hb,F,BANKED
;   Get next sector of FAT1, put in GPR banks 4&5.
;       setup sector number (LBA 7:0) register
            movff   ffat1sec_lb,cf_rw_snum
;       setup cylinder low (LBA 15:8) register
            movff   ffat1sec_hb,cf_rw_cyllo
            call    cf_read_sector
;   Clear count of FAT entries written to segment.
            clrf    clus_swc_lb,BANKED
            goto    begin_fat_segment
write_eoc:
;   We're at the last cluster of the chain, so write end-of-chain to cluster.
            movlw   0xFF
            movff   WREG,POSTINC0   ; write eoc mark: 0xFFFF
            movff   WREG,POSTINC0
;   Write sector of FAT data to CF Card FAT1 from GPR banks 4&5.
;       setup sector number (LBA 7:0) register
            movff   ffat1sec_lb,cf_rw_snum
;       setup cylinder low (LBA 15:8) register
            movff   ffat1sec_hb,cf_rw_cyllo
            call    cf_write_sector
;   Write sector of FAT data to CF Card FAT2 from GPR banks 4&5.
;       setup sector number (LBA 7:0) register
            movff   ffat2sec_lb,cf_rw_snum
;       setup cylinder low (LBA 15:8) register
            movff   ffat2sec_hb,cf_rw_cyllo
            call    cf_write_sector
            return

;
cf_file_write_init:
;
;   Initializes instrumentation process variables and sends a command to
;   the CF card to write the first data sector.

;   Zero instrumentation file variables.
            clrf    fil_size_ll,BANKED  ; filesize
            clrf    fil_size_lh,BANKED
            clrf    fil_size_hl,BANKED
            clrf    fil_size_hh,BANKED
            clrf    fil_swrt_lb,BANKED  ; file sectors written
            clrf    fil_swrt_hb,BANKED
            clrf    fil_sbwc_lb,BANKED  ; sector bytes written count
            clrf    fil_sbwc_hb,BANKED
;   Initialize instrumentation interrupt variables.
            clrf    bank_4_full,ACCESS  ; init inst data banks to empty
            clrf    bank_5_full,ACCESS  ; init inst data banks to empty
            clrf    inst_bytcnt,ACCESS  ; inst data bytes written to bank
            movlw   0x04
            movwf   bnk_4_5_prt,ACCESS  ; start writing to bank 4
;   Send command to write the first data sector.
;       setup sector number (LBA 7:0) register
            movff   fil_dsec_lb,cf_rw_snum ; 1st data sector for file(lwlb)
;       setup cylinder low (LBA 15:8) register
            movff   fil_dsec_hb,cf_rw_cyllo ; 1st data sector for file (lwhb)
;       setup comand register
            movlw   WR_SEC_CMD          ; get write sector command value
            movwf   cf_rw_cmd,BANKED
            call    cf_sec_rw_cmd
            return

;
cf_file_write:
;
;   Writes one byte of instrumentation data to file.  When the max file size
;   is reached the file close and CF card file system updates are performed.
;   The file size is a 4 byte number; the bytes being hh:hl:lh:ll.  The max
;   file size test below looks at a single bit of byte 'hl'.
;                                       hh:hl:lh:ll
;       Possibilities are:  bit 0 set = 00:01:00:00h (65,536) bytes,
;                           bit 1 set = 00:02:00:00h (131,072) bytes,
;                           bit 2 set = 00:04:00:00h (262,144) bytes,
;                           bit 3 set = 00:08:00:00h (524,288) bytes, etc.
;             This code mas file size = 02:00:00:00h (33,554,432) bytes.
;               Windows max file size = FF:FF:FF:FFh (4,294,967,295) bytes.
;
;   Limitation of file size for the code is that I have only implemented 16-bit
;   counters for counting sectors. 4GB file requires 8,388,607 sectors!
;
;       In code below, the max file size is 0x10000 (65,536) bytes; 128 sectors.
;
;   Test for max file size.
            btfsc   fil_size_hl,0,BANKED ; Test if max filesize reached
            goto    close_file
;   Current sector byte count = 512?
            btfss   fil_sbwc_hb,1,BANKED ; 511=1FFh, 512=200h so check bit 1
                                        ;  of counter high byte (bit cnt zero based)
            goto    cf_file_wbyte       ; No, write next byte to file
;   Current sector full, so start new sector.
;       Increment count of file sectors.
            incf    fil_swrt_lb,F,BANKED ; inc count of sectors written
            btfsc   STATUS,C,ACCESS     ; carry on increment
            incf    fil_swrt_hb,F,BANKED ; yes, fixup high byte for carry
;       Add 1 to fil_dsec_lb:hb as address of next file sector.
            incf    fil_dsec_lb,F,BANKED ; setup for next data sector
            btfsc   STATUS,C,ACCESS     ; carry on increment?
            incf    fil_dsec_hb,F,BANKED ; yes, fixup high byte for carry
;       Zero the sector byte count.
            clrf    fil_sbwc_lb,BANKED
            clrf    fil_sbwc_hb,BANKED
;       Send command to write the next data sector.
;       setup sector number (LBA 7:0) register
            movff   fil_dsec_lb,cf_rw_snum ; data sector (lwlb)
;       setup cylinder low (LBA 15:8) register
            movff   fil_dsec_hb,cf_rw_cyllo ; data sector (lwhb)
;       setup comand register
            movlw   WR_SEC_CMD          ; get write sector command value
            movwf   cf_rw_cmd,BANKED
            call    cf_sec_rw_cmd
cf_file_wbyte:
;   We started writing to file when bank 4 was full.  After we empty bank 4 we
;   start on bank 5, etc.  Continue emptying the current bank until it is empty
;   before switching to the other bank.
;       Determine which GPR bank we are currently emptying.
;           bank4:  'fil_sbwc_hb' = 00; 'fil_sbwc_lb' = 00-FF
;           bank5:  'fil_sbwc_hb' = 01; 'fil_sbwc_lb' = 00-FF
;       rollover to 'fil_sbwc_hb' = 02; 'fil_sbwc_lb' = 00 (at completion of
;               bank 5 is handled above)
;
            movf    fil_sbwc_hb,W,BANKED ; movf causes Z status update
            btfss   STATUS,Z,ACCESS     ; 'fil_sbwc_hb' = 0?
            goto    current_bank5       ; No - so we are working bank 5
current_bank4:
            btfss   bank_4_full,0,ACCESS ; bank 4 full?
            goto    file_write_done     ; No, so wait for data
;       Bank 4 full, so work on emptying it.
            movlw   0x04                ; setup indirect address to get data
            movwf   FSR0H,ACCESS        ;  byte from GPR bank
            goto    get_data
current_bank5:
            btfss   bank_5_full,0,ACCESS ; bank 5 full?
            goto    file_write_done     ; No, so wait for data
;       Bank 5 full, so work on emptying it.
            movlw   0x05                ; setup indirect address to get data
            movwf   FSR0H,ACCESS        ;  byte from GPR bank
get_data:
            movff   fil_sbwc_lb,FSR0L   ; counter 'fil_sbwc_lb' will run from 0-FFh
                                        ;  twice, once for each bank
;       Write byte of data to CF card instrumentation file.
            movff   INDF0,cf_io_data    ; get the data byte from the GPR bank
            clrf    cf_io_reg,BANKED
            call    cf_write
;       Increment count of bytes written to sector.
            incf    fil_sbwc_lb,F,BANKED
            btfsc   STATUS,C,ACCESS     ; carry on increment?
            incf    fil_sbwc_hb,F,BANKED ; yes, fixup high byte for carry
;       Increment count of bytes written to file.
            incf    fil_size_ll,F,BANKED
            btfsc   STATUS,C,ACCESS     ; carry on increment?
            incf    fil_size_lh,F,BANKED ; yes, fixup high byte for carry
            btfsc   STATUS,C,ACCESS     ; carry on increment?
            incf    fil_size_hl,F,BANKED ; yes, fixup high byte for carry
            btfsc   STATUS,C,ACCESS     ; carry on increment?
            incf    fil_size_hh,F,BANKED ; yes, fixup high byte for carry
;   Test to see if done emptying data bank.
            movf    fil_sbwc_lb,W,BANKED ;  rollover from FFh to 00h?
            btfss   STATUS,Z,ACCESS     ; low byte of counter = 00h?
            goto    file_write_done     ; No, so we're done
;   Done emptying GPR bank, clear 'buffer_x_full' flag for bank.
;       If  'fil_sbwc_hb' = 01h we just finished bank 4
;       If  'fil_sbwc_hb' = 02h we just finished bank 5
            movlw   0x01
            xorwf   fil_sbwc_hb,W,BANKED
            btfss   STATUS,Z,ACCESS     ; is 'fil_sbwc_hb' = 01h
            goto    clear_5             ; No, clear bank 5 flag
            bcf     bank_4_full,0,ACCESS ; Yes, clear bank 4 flag
            goto    file_write_done
clear_5:
            bcf     bank_5_full,0,ACCESS
file_write_done:
            return
close_file:
;   Perform CF Card instrumentation data file close and stop.
            call    cf_file_close
            movlw   FILE_FULL
            call    error_stop

;
cf_file_close:
;
;   Update root directory and FATs with instrumentation file information.
;   If user terminated instrumentation prior to first data byte being
;   written to the file, do nothing, CF card file system remains with
;   zero length for filename.
;
;   Check if user requested instrumentation termination prior to any
;       instrumentation data being collected.  Test for filesize = 0.
            clrf    WREG,ACCESS
            xorwf   fil_size_ll,W,BANKED ; low:low byte=0?
            btfss   STATUS,Z,ACCESS
            goto    close_continue      ; No, so something in file
            xorwf   fil_size_lh,W,BANKED ; low:high byte=0?
            btfss   STATUS,Z,ACCESS
            goto    close_continue      ; No, so something in file
            xorwf   fil_size_hl,W,BANKED ; high:low byte=0?
            btfss   STATUS,Z,ACCESS
            goto    close_continue      ; No, so something in file
            xorwf   fil_size_hh,W,BANKED ; high:high byte=0?
            btfss   STATUS,Z,ACCESS
            goto    close_continue      ; No, so something in file
            goto    close_end           ; Yes, filesize = 0
close_continue:
;   Be sure last sector is full and therefore written.
            btfsc   fil_sbwc_hb,1,BANKED ; 511=1FFh, 512=200h so check bit 2
                                        ;  of counter high byte
            goto    updt_directory      ; Yes, continue to dir update
;   Finish writing to last sector.
            clrf    inst_data_b,ACCESS  ; finish by writing 0x00's
cf_close_finish:
;       Write byte of data to instrumentation file.
            movff   inst_data_b,cf_io_data
            clrf    cf_io_reg,BANKED
            call    cf_write
;       Increment count of bytes written to sector.
            incf    fil_sbwc_lb,F,BANKED
            btfsc   STATUS,C,ACCESS     ; carry on increment?
            incf    fil_sbwc_hb,F,BANKED ; yes, fixup high byte for carry
;       Full yet?
            btfss   fil_sbwc_hb,1,BANKED ; 511=1FFh, 512=200h so check bit 2
                                        ;  of counter high byte
            goto    cf_close_finish     ; No, continue to fill segment
;       Increment count of file sectors.
            incf    fil_swrt_lb,F,BANKED ; inc count of sectors written
            btfsc   STATUS,C,ACCESS     ; carry on increment
            incf    fil_swrt_hb,F,BANKED ; yes, fixup high byte for carry
updt_directory:
;   Update root directory entry for instrumentation file.
            call    cf_close_dirupdt
;   Update FAT1 & FAT2 with instrumentation file cluster chains.
            call    cf_close_clusupdt
close_end:
            return

;
cf_setup_init:
;
;   Load the CF card partition table info.
            call    cf_load_bootsec_parms
;   Load the BIOS Parameter Block for card partition.
            call    cf_load_BIOS_parms
;   Verify the card format is FAT16.
            call    cf_verify_FAT16
;   Find the inst data file 'PIC_DATA.DAT' in the root directory.
            call    cf_find_file
;   Verify the data file is empty.
            call    cf_verify_file_empty
;   Compute the root directory size in sectors (needed for next computation).
            call    cf_comp_rootdir_size
;   Compute sector number for first data sector of partition.
            call    cf_comp_1st_data_sect
            return

;
;******************************************************************************
; 16-bit x 16-bit unsigned integer multiplication routine
;******************************************************************************
mult16x16U:
;
;   Routine length 32 line; time 129 to 228 cycles
;   This program looks at the lsb of a1 to decide whether to add b1 to prod_lwhb
;   and b2 to prod_hwlb, with appropriate carrys.
;   It then looks at the lsb of a2 to decide whether to add b1 to prod_hwlb and
;   b2 to prod_hwhb, again with appropriate carrys.
;   The rotates then only have to be done 8 times
;
;   This is uses slightly more program but takes a little less time than
;   a routine that performs one 16 bit addition per rotate and 16 rotates
;
;   Code from malin@onspec.co.uk
;   Multiple byte addition routine from Microchip AN617
;   Result registers used as loop counter from Bob Fehrenbach & Scott Dattalo
;
            movff   BSR,bsr_temp        ; save source BSR
    ;   Set BSR to math variable bank
            movlb   MATH_GPR            ; set BSR to math variable bank
    ;   load multiplication operands
            movff   POSTINC0,a1         ; load multiplicand variable L:H
            movff   POSTINC0,a2
            movff   POSTINC1,b1         ; load multiplier variable L:H
            movff   POSTINC1,b2

            clrf    prod_hwhb,BANKED
            clrf    prod_hwlb,BANKED
            clrf    prod_lwhb,BANKED
            movlw   0x80
            movwf   prod_lwlb,BANKED
nextbit:
            rrcf    a2,F,BANKED
            rrcf    a1,F,BANKED
            btfss   STATUS,C,ACCESS
            goto    nobit_l
            movf    b1,W,BANKED
            addwf   prod_lwhb,F,BANKED
            movf    b2,W,BANKED
            btfsc   STATUS,C,ACCESS
            incfsz  b2,W,BANKED
            addwf   prod_hwlb,F,BANKED
            btfsc   STATUS,C,ACCESS
            incf    prod_hwhb,F,BANKED
            bcf     STATUS,C,ACCESS
nobit_l:
            btfss   a1,7,BANKED
            goto    nobit_h
            movf    b1,W,BANKED
            addwf   prod_hwlb,F,BANKED
            movf    b2,W,BANKED
            btfsc   STATUS,C,ACCESS
            incfsz  b2,W,BANKED
            addwf   prod_hwhb,F,BANKED
nobit_h:
            rrcf    prod_hwhb,F,BANKED
            rrcf    prod_hwlb,F,BANKED
            rrcf    prod_lwhb,F,BANKED
            rrcf    prod_lwlb,F,BANKED
            btfss   STATUS,C,ACCESS
            goto    nextbit
    ;   restore BSR
            movff   bsr_temp,BSR        ; restore source BSR
            return

;******************************************************************************
; 16-bit by 16-bit unsigned integer division routine
;******************************************************************************
div16by16U:
;
;   Division : accB(16 bits) / accA(16 bits) -> accB(16 bits) with
;                                               Remainder in accC (16 bits)
;      (a) Load the Denominator in location accAhi & accAlo ( 16 bits )
;      (b) Load the Numerator in location accBhi & accBlo ( 16 bits )
;      (c) CALL D_div
;      (d) The 16 bit result is in location accBhi & accBlo
;      (e) The 16 bit Remainder is in locations accChi & accClo
;
;   Performance :
;               Program Memory  :       037
;               Clock Cycles    :       310
;
            movff   BSR,bsr_temp        ; save source BSR
    ;   Set BSR to math variable bank
            movlb   MATH_GPR            ; set BSR to math variable bank
    ;   load division operands
            movff   POSTINC0,accAlo     ; load denominator variable L:H
            movff   POSTINC0,accAhi
            movff   POSTINC1,accBlo     ; load numerator variable L:H
            movff   POSTINC1,accBhi

            movlw   .16                 ; for 16 shifts
            movwf   math_temp,BANKED
            movff   accBhi,accDhi       ; move ACCb to ACCd
            movff   accBlo,accDlo
            clrf    accBhi,BANKED
            clrf    accBlo,BANKED
            clrf    accChi,BANKED
            clrf    accClo,BANKED
dloop:
            bcf     STATUS,C,ACCESS
            rlcf    accDlo,F,BANKED
            rlcf    accDhi,F,BANKED
            rlcf    accClo,F,BANKED
            rlcf    accChi,F,BANKED
            movff   accAhi,WREG
            subwf   accChi,W,BANKED     ; check if a>c
            btfss   STATUS,Z,ACCESS
            goto    nochk
            movff   accAlo,WREG
            subwf   accClo,W,BANKED     ; if msb equal then check lsb
nochk:
            btfss   STATUS,C,ACCESS     ; carry set if c>a
            goto    nogo
            movff   accAlo,WREG         ; c-a into c
            subwf   accClo,F,BANKED
            btfss   STATUS,C,ACCESS
            decf    accChi,F,BANKED
            movff   accAhi,WREG
            subwf   accChi,F,BANKED
            bsf     STATUS,C,ACCESS     ; shift a 1 into b (result)
nogo:
            rlcf    accBlo,F,BANKED
            rlcf    accBhi,F,BANKED
            decfsz  math_temp,F,BANKED  ; loop untill all bits checked
            goto    dloop
    ;   restore BSR
            movff   bsr_temp,BSR        ; restore source BSR
            return

;******************************************************************************
; 16-bit - 16-bit unsigned integer subtraction routine
;******************************************************************************
sub16U:
;
;   Subtraction: DST(16-bit) = DST(16-bit) - SRC(16-bit)
;
;   DTS is replaced, SRC is preserved
;   Carry is set correctly, Zero flag is not valid
;
            movff   BSR,bsr_temp        ; save source BSR
    ;   Set BSR to math variable bank
            movlb   MATH_GPR            ; set BSR to math variable bank
    ;   load division operands
            movff   POSTINC0,sub_dst_lb ; load minuend variable L:H
            movff   POSTINC0,sub_dst_hb
            movff   POSTINC1,sub_src_lb ; load subtrahend variable L:H
            movff   POSTINC1,sub_src_hb
    ;   perform subtraction
            movff   sub_src_lb,WREG     ; get low byte of subtrahend
            subwf   sub_dst_lb,F,BANKED ; subtract DST(low) - SRC(low)
            movff   sub_src_hb,WREG     ; get high byte of subtrahend
            btfss   STATUS,C,ACCESS     ; if there was a borrow, rather than
            incf    sub_src_hb,W,BANKED ;  dec high byte of DST we inc SRC
            subwf   sub_dst_hb,F,BANKED ; sub high byte and we're done
    ;   restore BSR
            movff   bsr_temp,BSR        ; restore source BSR
            return

;******************************************************************************
; 16-bit + 16-bit unsigned integer addition routine
;******************************************************************************
add16U:
;
;   Addition: DST(16-bit) = DST(16-bit) + SRC(16-bit)
;
;   DTS is replaced, SRC is preserved
;   Carry is set correctly
;
            movff   BSR,bsr_temp        ; save source BSR
    ;   Set BSR to math variable bank
            movlb   MATH_GPR            ; set BSR to math variable bank
    ;   load division operands
            movff   POSTINC0,add_dst_lb ; load minuend variable L:H
            movff   POSTINC0,add_dst_hb
            movff   POSTINC1,add_src_lb ; load subtrahend variable L:H
            movff   POSTINC1,add_src_hb
    ;   perform addition
            movff   add_src_lb,WREG     ; get low byte of source
            addwf   add_dst_lb,F,BANKED ; add DST(low) + SRC(low)
            movff   add_src_hb,WREG     ; get high byte of source
            btfsc   STATUS,C,ACCESS     ; check for carry on low byte add
            incf    add_src_hb,W,BANKED ; inc high byte for carry
            addwf   add_dst_hb,F,BANKED ; add DST(high) + SRC(high)
    ;   restore BSR
            movff   bsr_temp,BSR        ; restore source BSR
            return

            end

**** Linker Script file ****
// File: CF_DB_Inst.lkr
// Linker script for program CF_DB_Inst.ASM
LIBPATH .
CODEPAGE    NAME=page           START=0x0       END=0x7FFF
CODEPAGE    NAME=idlocs         START=0x200000  END=0x200007    PROTECTED
CODEPAGE    NAME=config         START=0x300000  END=0x30000D    PROTECTED
CODEPAGE    NAME=devid          START=0x3FFFFE  END=0x3FFFFF    PROTECTED
CODEPAGE    NAME=eedata         START=0xF00000  END=0xF000FF    PROTECTED
ACCESSBANK  NAME=accessram      START=0x0       END=0x7F
DATABANK    NAME=gpr0           START=0x80      END=0xFF
DATABANK    NAME=gpr1           START=0x100     END=0x1FF
DATABANK    NAME=gpr2           START=0x200     END=0x2FF
DATABANK    NAME=gpr3           START=0x300     END=0x3FF
DATABANK    NAME=gpr4           START=0x400     END=0x4FF
DATABANK    NAME=gpr5           START=0x500     END=0x5FF
ACCESSBANK  NAME=accesssfr      START=0xF80     END=0xFFF       PROTECTED
SECTION     NAME=acs_vars       RAM=accessram
SECTION     NAME=prog_vars      RAM=gpr3
SECTION     NAME=cfops_vars     RAM=gpr3
SECTION     NAME=pt_vars        RAM=gpr3
SECTION     NAME=bs_bio_vars    RAM=gpr3
SECTION     NAME=math_vars      RAM=gpr3
SECTION     NAME=pt_prm_tbl     ROM=page
SECTION     NAME=bs_prm_tbl     ROM=page
SECTION     NAME=pgm_strings    ROM=page
****  PIC to CF Card Interface  ****
    CF-PIC Interface – Using PC Card Common Memory Mode
Pin     Name        Action                                  
1       GND         tie to ground
2-6     D03-D07     I/O, connect to PIC, PORTD[03:07]
7       -CE1        byte/word mode, connect to PIC, PORTC[0]
8       A10         tie to ground
9       -OE         Output Enable strobe, connect to PIC, PORTC[1]
10-12   A09-A07     tie to ground
13      VCC         tie to +5V
14-17   A06-A03     tie to ground
18-20   A02-A00     register address lines, connect to PIC, PORTE[0:2]
21-23   D00-D02     I/O, connect to PIC, PORTD[00:02]
24      WP          leave unconnected
25      -CD2        leave unconnected
26      -CD1        CF Present, connect to PIC, PORTC[5], also to 10K resistor to +5V
27-31   D11-D16     leave unconnected
32      -CE2        byte/word mode, connect to +5V
33      -VS1        leave unconnected
34      -IORD       leave unconnected
35      -IOWR       leave unconnected
36      -WE         I/O write strobe, connect to PIC, PORTC[2]
37      READY       CF card ready for new cmd, connect to PIC, PORTC[3]
38      VCC         connect to +5V
39      -CSEL       tie to ground
40      -VS2        leave unconnected
41      RESET       leave unconnected
42      -WAIT       CF busy, connect to PIC, PORTC[6]
43      -INPACK     leave unconnected
44      -REG        tie to +5V
45      BVD2        leave unconnected
46      BVD1        leave unconnected
47-49   D08-D10     leave unconnected
50      GND         tie to ground