* QL WORLD DIY TOOLKIT JULY 1989 - MEM device driver,
* Version 1.0, copyright Luca Pivato & Simon N Goodwin.
*
mem_ptr    equ      24              Current MEM pointer 
buff_id    equ      28              Word ID of buffer
chan_link  equ      30              Channel list pointer
buff_addr  equ      34              Buffer start address
buff_flag  equ      38              Word persistence flag
*
start      lea.l    serio_ptrs,a0   Set up SERIO linkage
           lea.l    io_ready,a2     
           move.l   a2,(a0)+
           lea.l    fetch_byte,a2
           move.l   a2,(a0)+
           lea.l    send_byte,a2    
           move.l   a2,(a0)+
           lea.l    io_pointer,a0   Set up device linkage
           lea.l    io_code,a2
           move.l   a2,(a0)+
           lea.l    open_code,a2
           move.l   a2,(a0)+
           lea.l    close_code,a2
           move.l   a2,(a0)+
           lea.l    linkage,a0      Link the MEM device
           moveq    #32,d0          MT.LIOD key
           trap     #1
           rts                      Return D0 to caller   
*           
linkage    dc.l    0                Link to next device
io_pointer dc.l    0                Pointer to I/O code
           dc.l    0                Pointer to OPEN code
           dc.l    0                Pointer to CLOSE code
*
buff_ptr   dc.l    0                Start of buffer list
chan_ptr   dc.l    0                Start of channel list
*
* Code to handle TRAP #3 calls: IO.PEND, FBYTE, FLINE,
* FSTRG, SBYTE, SSTRG, EXTOP, POSAB, POSRE, LOAD, SAVE
*
io_code    cmp.b   #9,d0            Call to FS.EXTOP?
           beq     vector_a2
           move.l  mem_ptr(a0),a5   A5 -> MEM address
           cmp.b   #66,d0           Call to FS.POSAB?
           beq.s   set_abspos
           cmp.b   #67,d0           Call to FS.POSRE?
           beq.s   set_relpos
           move.w  234,a2           Use IO.SERIO vector
           jsr     (a2)             Do other serial I/O
serio_ptrs dc.l    0                Pointer for IO.PEND
           dc.l    0                Pointer for IO.FETCH
           dc.l    0                Pointer for IO.SEND
           rts                      Return from SERIO
*
set_abspos tst.l   d1               Negative parameter?
           bpl.s   use_posn
           move.l  buff_addr(a0),d1 Get default base
use_posn   move.l  d1,a5            Set position
           bra.s   set_memptr
set_relpos adda.l  d1,a5            Offset position
           move.l  a5,d1            Return new value
           bra.s   set_memptr
send_byte  move.b  d1,(a5)+
           bra.s   set_memptr
fetch_byte move.b  (a5)+,d1
set_memptr move.l  a5,mem_ptr(a0)   Reset MEM pointer
io_ready   moveq   #0,d0
           rts
*
* Code to handle TRAP #2 calls: OPEN and CLOSE
*
open_code  subq.l  #6,a7            Make space on stack
           move.l  a7,a3            A3 -> parameters
           move.w  290,a2           Use IO.NAME vector
           jsr     (a2)
           bra.s   exit_open        Name faulty
           bra.s   exit_open        Parameters faulty
           bra.s   open_ok          Name parsed OK
name_spec  dc.w    3                Length of name
           dc.b    'MEM'
           dc.w    3                Max. 3 parameters
           dc.w    -1,-1            Buffer number
           dc.w    ' _',-1          _ Buffer size
           dc.w    2,'PT'           Permanent/Temporary
*
open_ok    move.l  a3,a5            A5 -> parameters
           moveq   #40,d1           40 bytes needed
           move.w  192,a2           MM.ALCHP vector
           jsr     (a2)             Allocate memory
           beq.s   do_buffers       Go on if RAM permits
exit_open  addq.l  #6,a7            Deallocate stack space
           tst.l   d0               Error code is in D0
           rts                      
*
do_buffers move.w  (a5),d7          Get buffer ID
           move.w  d7,buff_id(a0)   Keep buffer ID
           bmi.s   exit_open        No buffer, exit
set_flag   move.w  4(a5),buff_flag(a0) Keep P/T flag
           move.l  a0,a4            Save channel base
           lea.l   buff_ptr,a3      Search buffer list
           bsr.s   scanner          Does the buffer exist?
           beq.s   new_one          No it doesn't
           lea.l   6(a3),a0         A0 -> Start of buffer
           moveq   #0,d0            OPENed without errors 
           bra.s   set_addr         Tell the channel
*
new_one    move.w  2(a5),d1         Was a size specified?
           bmi.s   no_size          If not, complain!
           addq.w  #8,d1            Allow for a header
           ext.l   d1               D1 = total buffer size
           moveq   #0,d2            Permanent allocation
           moveq   #24,d0           MT.ALCHP key
           trap    #1               Try to allocate memory
           tst.l   d0               Did that work?
           bne.s   no_room          If not, complain
           move.l  a0,d2            Keep buffer base
           move.w  d7,(a0)+         Record buffer ID
           lea.l   buff_ptr,a3      Extend the buffer list
           move.l  (a3),(a0)+       
           move.l  d2,(a3)          Start with new buffer
           sub.w   #24,d1           Ignore header bytes
           move.w  d1,(a0)+         Store buffer length
set_addr   move.l  a0,buff_addr(a4) Record buffer start
           move.l  a0,mem_ptr(a4)   Initialise pointer
           lea.l   chan_ptr,a3      Extend channel list
           move.l  (a3),chan_link(a4)
           lea.l   buff_id(a4),a2
           move.l  a2,(a3)
           move.l  a4,a0            A0 -> Channel block
           bra.s   exit_open
*
no_room    move.l  a4,a0            Retrieve channel
           bsr.s   lose_chan
           moveq   #-3,d0           No room for the buffer
           bra.s   exit_open        Report OUT OF MEMORY
no_size    bsr.s   lose_chan
           moveq   #-15,d0          Size needed but absent
           bra.s   exit_open        Report BAD PARAMETER
*
scanner    move.l  a3,a2            Find the ID in D7.W
           move.l  (a2),d0          Try the next link
           beq.s   not_found        No such luck, quit
           move.l  d0,a3            A3 -> Buffer ID.W
           cmp.w   (a3)+,d7         Is it what we want?         
           bne.s   scanner          If no, try the next
found_it   tst.l   d0               Yes, return A2 & A3   
not_found  rts                      Return D0=0 if absent
*
close_code lea.l   buff_id(a0),a4   Get list position
           lea.l   chan_ptr,a3      Purge channel list
           bsr.s   purge_list
           move.w  (a4),d7          Is a buffer in use?
           bmi.s   lose_chan        No, just zap channel
           subq.w  #1,buff_flag(a0) Is buffer permanent?
           beq.s   lose_chan        Yes, just zap channel
           lea.l   chan_ptr,a3      Is the buffer busy?
           bsr.s   scanner          Search channel list
           bne.s   lose_chan        If busy keep buffer
           move.l  buff_addr(a0),a4 Find buffer
           subq.l  #8,a4            Include header
           lea.l   buff_ptr,a3      Scan buffer list
           bsr.s   purge_list       Un-link buffer
           exg     a4,a0            Swap pointers over
           moveq   #25,d0           MT.RECHP key
           trap    #1               Deallocate buffer
           move.l  a4,a0            Restore channel base
lose_chan  move.w  194,a2           MM.RECHP vector 
vector_a2  jmp     (a2)             Deallocate channel
*
purge_list move.l  a3,a2            Remove link to (A4) 
           move.l  (a2),d0          
           beq.s   bizarre          No more - abort!
           move.l  d0,a3            A3 -> Buffer ID.W
           addq.l  #2,a3            Skip buffer ID
           cmp.l   a4,d0            Have we found it?
           bne.s   purge_list       No, look further
           move.l  (a3),(a2)        De-list the entry
bizarre    rts
*
           end
