* I/O Opcode 3 - WRITE
*
* This routine will store a record into the file buffer providing
* the file has been open for writing.  If the buffer is full, it
* will call FLUSH to write it out to disk and then start a new
* buffer.
*

WRITE0 ANDI R12,>FF00
       AI   R12,24
       LDCR @B02,4           Select RAM bank 2
       BLWP @IFO             See if file is open
       JEQ  WRITE2

WRITE1 BL   @DSRERR
       DATA >0700            File error

* If the file is open for input mode, error.  All other modes
* allow write access.
WRITE2 CB   @56(R5),@B02     Input mode
       JNE  WRITE3
       BL   @DSRERR
       DATA >0200            Bad open attribute?

* Now check to see if we're doing sequential or random I/O

WRITE3 LDCR @ZERO,4
       MOVB @PABBUF+1,R1
       ANDI R1,>0100
       JEQ  WRITE4

* We're handling relative I/O now!  7-2-95

       B    @WRIT50

* The first thing we do is see if we're working on an AU.  If
* not, we assume this is the first record being written to this
* file.  If the data chain is empty, we allocate an AU for it.
* Otherwise, we use the AU that's already allocated.

WRITE4 LDCR @B02,4           Select RAM bank 2
       C    @50(R5),@ZERO    See if current AU=0
       JEQ  WRT4Z
       B    @WRIT10
WRT4Z
* Let's see if this file has any AU's allocated for it already.
*
       MOV  @4(R5),R1        Get FDR address
       LDCR @2(R5),4         Select bank with FDR
       MOV  @40(R1),R7       Get 1st AU in chain
       JEQ  WRIT4A
       LDCR @B02,4
       MOV  R7,@50(R5)       Save the AU

* If we're in update mode and the file has fixed length records,
* we should read in the data to the buffer.

       CB   @ZERO,@56(R5)    See if update mode
       JNE  WRIT6B
       LDCR @2(R5),4
       MOVB @12(R1),R0       Get file flags
       ANDI R0,8000
       JNE  WRIT6B
       LDCR @B04,4           We have to read it in!
       MOV  R6,R3
       SRL  R3,8
       SLA  R3,1
       MPY  @SAUTBL(R3),R7
       LI   R3,SECBUF
       SRL  R8,1
       JNC  WRT4A
       AI   R3,>100
WRT4A  SRL  R7,1
       JNC  WRT4B
       AI   R8,>8000
WRT4B  LDCR @ZERO,4
       BLWP @BANKIT
       DATA SCSIRD
       JEQ  WRT4C
       BL   @DSRERR
       DATA >0600            Device error

WRT4C  LDCR @B02,4
       MOV  @6(R5),R7        Get Buffer address
       MOVB @3(R5),R8        Get buffer bank
WRT4D  LDCR @ZERO,4
       MOV  *R3+,R0
       LDCR R8,4
       MOV  R0,*R7+
       CI   R3,SECBUF+512
       JNE  WRT4D
       JMP  WRIT6B

* OK.  Let's go out and allocate the 1st AU for the file

WRIT4A MOV  R5,R0            Save pointer to cache entry

       CLR  R7               No preferred starting AU
       LI   R8,1             We want 1 AU
       LI   R3,256*8         AU boundry
       LDCR @ZERO,4          Select RAM bank 0
       BLWP @ALLOC
       JEQ  WRITE5
       BL   @DSRERR
       DATA >0600            Device error

WRITE5 CI   R4,0             Make sure we got one
       JNE  WRITE6

       BL   @DSRERR
       DATA >0400            Out of space

WRITE6 LDCR @B02,4           Select RAM bank 2
       MOV  R5,R2
       MOV  R0,R5            Restore pointer

       MOV  R2,@50(R5)       Save current AU

* Put the AU in the data chain

       MOV  @4(R5),R1        Get pointer to FDR
       LDCR @2(R5),4
       MOV  R2,@40(R1)       Store AU in data chain
       MOV  R2,@42(R1)
       INC  @34(R1)          Increment # of AU's in FDR
       LDCR @B04,4           Set # of sectors allocated
       MOV  R6,R3
       SRL  R3,8
       SLA  R3,1
       MOV  @SAUTBL(R3),R3
       LDCR @B02,4
       LDCR @2(R5),4
       MOV  R3,@14(R1)

WRIT6B LDCR @B02,4
       CLR  @52(R5)          Sector within AU = 0
       CLR  @58(R5)          Buffer size = 256
       MOV  @6(R5),@54(R5)   Pointer within buffer

       LDCR @2(R5),4         Select bank with FDR

       MOVB @12(R1),R3       If variable length records,
       ANDI R3,>8000         Set sector # to one
       JEQ  WRT6A
       LI   R3,1
       LDCR @B02,4
       MOV  R3,@62(R5)

* See if we can use a 512 byte buffer
*
WRT6A  LDCR @B04,4           Select RAM bank 4
       MOV  R6,R3
       SRL  R3,8
       SLA  R3,1
       MOV  @SAUTBL(R3),R1
       CI   R1,1
       JEQ  WRITE8
       ANDI R1,>0001
       JEQ  WRITE7
       ANDI R2,>0001
       JNE  WRITE8

WRITE7 LDCR @B02,4           Select RAM bank 2
       SETO @58(R5)
WRITE8 B    @WRIT30


* Here we check to see if there is enough room in the buffer
* to hold the record.  In the case of variable length files,
* we must also account for the record size byte and the
* end of sector marker.
*
WRIT10 LDCR @B02,4
       MOV  @4(R5),R1
       LDCR @2(R5),4
       MOVB @17(R1),R2       Get record size
       MOVB @12(R1),R4       Get file flags

       LDCR @ZERO,4
       CB   @PABBUF+5,R2     Make sure we're not trying
       JL   WRIT11           to write more characters than
       JEQ  WRIT11           the file allows

       BL   @DSRERR
       DATA >0700            File error

WRIT11 SRL  R2,8
       ANDI R4,>8000         Check for fixed or variable
       JEQ  WRIT12

       MOVB @PABBUF+5,R2     Get character count from PAB
       SRL  R2,8
       INCT R2               Add in length byte and EOS marker

WRIT12

* We will now see if there's enough room in the buffer

       LDCR @B02,4            Select RAM bank 2
       MOV  @54(R5),R1        Get pointer within buffer
       S    @6(R5),R1         Subtract the base address
       CI   R1,>200
       JEQ  WRIT14
       CI   R1,>100
       JNE  WRT12A
       C    @58(R5),@ZERO     See if buffer size is 256
       JEQ  WRIT14            If so, read next sector
WRT12A
       MOV  @54(R5),R1
       ANDI R1,>00FF
       A    R2,R1
       CI   R1,>100
       JH   WRIT13
       B    @WRIT30

* There is not enough room in the buffer to add one more
* record.  First we check to see if the buffer size is 512
* bytes.  If so, we see if we are in the first half and simply
* adjust a couple of pointers to go to the second half of
* the buffer.  Otherwise, we write the buffer out to disk
* and start a new one.

WRIT13

* If we have variable length records,
* we want to increment our sector #
       LDCR @B02,4
       MOV  @4(R5),R1        Get address of FDR
       LDCR @2(R5),4         Select bank with FDR
       MOV  @12(R1),R0       Get file flags
       LDCR @B02,4           Select Bank 2 again
       ANDI R0,>8000
       JEQ  WRT13A
       INC  @62(R5)          Increment sector #
WRT13A
       MOV  @58(R5),R1       Check for 512 byte buffer
       JEQ  WRIT14

       MOV  @54(R5),R1       Get pointer in buffer
       ANDI R1,>0100         Check what half we're in
       JNE  WRIT14

       MOV  @54(R5),R1       Just go to 2nd half of buffer
       ANDI R1,>FF00
       AI   R1,>100
       MOV  R1,@54(R5)

       B    @WRIT30

* Here we have to write the buffer out to disk.  We then
* go to the next sector in the AU or next AU.  If there
* are no more AU's, we have to allocate a new one and
* put it at the end of the data chain
*
* If we are in update mode writing fixed length records,
* we read the old data into the buffer.

WRIT14
       BL   @FLUSH

       LDCR @B04,4           Select RAM bank 4
       MOV  R6,R3            Get number of sectors / AU
       SRL  R3,8
       SLA  R3,1
       MOV  @SAUTBL(R3),R3

       LDCR @B02,4           Select RAM bank 2
       INC  @52(R5)          Go to next sector in AU
       MOV  @6(R5),@54(R5)   Reset pointer within buffer
       C    @58(R5),@ZERO    If buffer size is 512 bytes,
       JEQ  WRIT15           then add another sector
       INC  @52(R5)
WRIT15 C    @52(R5),R3       See if we need to go to next AU
       JL   WRT18A

       S    R3,@52(R5)

* We now have to search the data chain to see where our
* AU is.  If it's at the end of the file, we have to
* allocate a new AU.

       MOV  @50(R5),R2       Get current AU
       MOV  @4(R5),R1        Get FDR address
       AI   R1,42            Point to data chain (+2)
       LDCR @2(R5),4

WRIT16 C    *R1,@ZERO        Are we at the end?
       JEQ  WRIT17
       C    *R1,R2           Is this our AU?
       JEQ  WRIT19
       AI   R1,4
       JMP  WRIT16

WRIT17 INC  R2               Just go to next AU
WRIT18 LDCR @B02,4
       MOV  R2,@50(R5)

       CLR  @58(R5)          Set buffer size to 256
WRT18A

* Here we check to see if we're in update mode
* and have fixed length records.  If so, we want
* to read the sector first

       LDCR @B02,4
       CB   @ZERO,@56(R5)    See if update mode
       JNE  WRT18F
       LDCR @2(R5),4
       MOVB @12(R1),R0       Get file flags
       ANDI R0,8000
       JNE  WRT18F
       LDCR @B04,4           We have to read it in!
       MOV  R6,R3
       SRL  R3,8
       SLA  R3,1
       MPY  @SAUTBL(R3),R7
       LI   R3,SECBUF
       SRL  R8,1
       JNC  WRT18B
       AI   R3,>100
WRT18B SRL  R7,1
       JNC  WRT18C
       AI   R8,>8000
WRT18C LDCR @ZERO,4
       BLWP @BANKIT
       DATA SCSIRD
       JEQ  WRT18D
       BL   @DSRERR
       DATA >0600            Device error

WRT18D LDCR @B02,4
       MOV  @6(R5),R7        Get Buffer address
       MOVB @3(R5),R8        Get buffer bank
WRT18E LDCR @ZERO,4
       MOV  *R3+,R0
       LDCR R8,4
       MOV  R0,*R7+
       CI   R3,SECBUF+512
       JNE  WRT18E
WRT18F B    @WRIT30

WRIT19 MOV  @2(R1),R2        Get next AU
       JNE  WRIT18

* It looks like we have to allocate another AU for the
* file.  Here we go.

       MOV  R5,R0            Save pointer
       MOV  *R1,R7           Set preferred AU
       INC  R7
       LI   R8,1             We want 1 AU
       LI   R3,256           In case we don't get the one we want
       LDCR @ZERO,4
       BLWP @ALLOC
       JEQ  WRIT20
       BL   @DSRERR
       DATA >0600            Device error

WRIT20 CI   R4,0
       JNE  WRIT21
       BL   @DSRERR
       DATA >0400            Out of space error

WRIT21 MOV  R5,R2            Save the AU
       MOV  R0,R5            Restore pointer
       LDCR @B02,4
       MOV  @4(R5),R8        Get FDR address
       LDCR @2(R5),4
       INC  @34(R8)          Increment # of AUs allocated
       LDCR @B04,4           Set # of sectors allocated
       MOV  R6,R3
       SRL  R3,8
       SLA  R3,1
       MOV  @SAUTBL(R3),R3
       LDCR @B02,4
       LDCR @2(R5),4
       A    R3,@14(R8)
       C    R7,R2            Did we get the AU we wanted?
       JEQ  WRIT23

       INCT R1               If not, add a new entry to chain
       MOV  R2,*R1+
WRIT23 MOV  R2,*R1

       LDCR @B02,4
       MOV  R2,@50(R5)       Store current AU
       CLR  @58(R5)          Set buffer size to 256

* Now see if we can use a 512 byte buffer

       B    @WRT6A


**
*
* Here is the infamous WRIT30.  Here is where we store the
* record in the file buffer and set the buffer modified flag
*
**

WRIT30 LDCR @ZERO,4
       MOVB @PABBUF+3,@VDPWA Set VDP read address to user's buffer
       NOP
       MOVB @PABBUF+2,@VDPWA

       LDCR @B02,4
       MOV  @54(R5),R1       Get pointer in buffer
       MOV  @4(R5),R2        Get FDR address
       SETO @60(R5)          Set buffer modified flag

* If we are doing variable length files, then we have to
* put the byte count in first

       LDCR @2(R5),4         Select bank with FDR
       MOVB @12(R2),R7       Get file flags
       ANDI R7,>8000
       JEQ  WRIT31

       LDCR @ZERO,4
       MOVB @PABBUF+5,R4     Get character count
       LDCR @B02,4
       LDCR @3(R5),4
       MOVB R4,*R1+
       JEQ  WRIT34
       JMP  WRIT32

WRIT31 MOVB @17(R2),R4       Get fixed record size from FDR

WRIT32 LDCR @B02,4
       SRL  R4,8

       LDCR @3(R5),4         Get buffer bank swapped in

WRIT33 MOVB @VDPRD,*R1+
       DEC  R4
       JNE  WRIT33

* If we're doing variable length files, put in a new
* end of sector marker after the record.

       CI   R7,0
       JEQ  WRIT36
WRIT34 MOVB @B255,*R1
       MOV  R1,R7            Set end of file offset
       SWPB R7
       LDCR @B02,4
       MOV  @62(R5),R8       Get sector number
       SWPB R8
       LDCR @2(R5),4
       MOVB R7,@16(R2)       Save end of file offset
       MOV  R8,@18(R2)       Save # of level 3 records

WRIT35 LDCR @B02,4
       MOV  R1,@54(R5)       Save pointer within buffer

       LDCR @ZERO,4
       B    @DSRRT

* For fixed length files, we need to increment the record #
* and if it's greater than the last record written, update
* the FDR.

WRIT36 LDCR @B02,4
       INC  @62(R5)          Increment the record #
       MOV  @62(R5),R0
       MOV  @4(R5),R2        Get FDR address
       LDCR @2(R5),4         Select BANK with FDR
       MOV  @18(R2),R3       Get # of level 3 records written
       SWPB R3
       C    R0,R3
       JL   WRIT35
       SWPB R0
       MOV  R0,@18(R2)       Update Level 3 field in FDR
       JMP  WRIT35


**
*
*  Here is where we handle relative I/O.  If we are trying
*  to write past the end of file, we need to allocate some
*  more AUs
*
**

WRIT50

* Step 1: Make sure the file was opened for relative access

       LDCR @B02,4       Select RAM bank 2
       MOVB @57(R5),R0
       JNE  WRIT51

       BL   @DSRERR
       DATA >0700        File error

WRIT51 LDCR @ZERO,4
       MOV  @PABADR,R10       Increment record number in
       AI   R10,6             caller's PAB
       ORI  R10,>4000
       SWPB R10
       MOVB R10,@VDPWA
       SWPB R10
       MOVB R10,@VDPWA
       MOV  @PABBUF+6,R10
       INC  R10
       MOVB R10,@VDPWD
       SWPB R10
       MOVB R10,@VDPWD

       MOV  @PABBUF+6,R10     Get record # to write
       LDCR @B02,4
       MOV  @4(R5),R1         Get FDR address
       LDCR @2(R5),4
       MOV  @18(R1),R2        See if we're writing past EOF
       SWPB R2
       C    R10,R2
       JHE  WRIT53            If so, extend the file

WRIT52 BL   @POSIT
       B    @WRIT30

WRIT53

* Here we're writing past the end of the file.  Let's begin
* by computing the number of AU's that will be needed.

       CLR  R9
*      DEC  R10
       MOVB @13(R1),R0       Get # of records/sector
       MOV  @34(R1),R2       Get # of AU's allocated
       SRL  R0,8
       DIV  R0,R9            R9 now has # of sectors needed

       LDCR @B04,4
       MOV  R6,R3
       SRL  R3,8
       SLA  R3,1
       CLR  R8
       DIV  @SAUTBL(R3),R8   R8 now has # of AU's we need
       INC  R8
       S    R2,R8            Subtract the # of AU's we have

WRIT54
       CI   R8,0
       JNE  WRIT55
       LDCR @B04,4           Get # of sectors / AU
       MOV  R6,R3
       SRL  R3,8
       SLA  R3,1
       MOV  @SAUTBL(R3),R3

       LDCR @ZERO,4
       MOV  @PABBUF+6,R10    Update # of level 3 records
       LDCR @B02,4
       MOV  @4(R5),R1        Get FDR address
       LDCR @2(R5),4
       INC  R10
       SWPB R10
       MOV  R10,@18(R1)
       MOV  @34(R1),R7       Multiply AU's by secotrs/AU
       MPY  R3,R7            to get # of sectors allocated
       MOV  R8,@14(R1)
       JMP  WRIT52

* Here is where we allocate some more AU's for the file

WRIT55
       CLR  R7               No preferred starting AU
       LI   R3,256           AU boundry
       LDCR @ZERO,4
       MOV  R5,R0            Save R5
       BLWP @ALLOC
       JEQ  WRIT56

       BL   @DSRERR
       DATA >0600            Device error

WRIT56 CI   R4,0
       JNE  WRIT57
       BL   @DSRERR
       DATA >0400            Out of space

WRIT57
       MOV  R5,R2            Save starting AU
       MOV  R0,R5            Restore R5

* Now put the new AU's in the data chain

       LDCR @B02,4
       MOV  @4(R5),R1        Get FDR address
       LDCR @2(R5),4
       A    R4,@34(R1)       Update # of AU's allocated
       S    R4,R8            Subtract from # needed
       AI   R1,40            Point to data chain
WRIT58
       C    *R1,@ZERO
       JEQ  WRIT59
       AI   R1,4
       JMP  WRIT58

WRIT59 DECT R1
       MOV  *R1,R10
       INC  R10
       C    R2,R10
       JNE  WRIT60
       A    R4,*R1
       JMP  WRIT54

WRIT60 INCT R1
       MOV  R2,*R1+
       MOV  R2,*R1
       A    R4,*R1
       DEC  *R1
       JMP  WRIT54
