* QL WORLD DIY TOOLKIT - FLEXYNET - PROTOTYPE BYTE I/O ROUTINES.
* Version 1.09, Copyright 1993 Simon N Goodwin, Rights Reserved.
* 6800X QL/ZX-8302 version; generic I/O port versions to follow.
*
tx_control equ      98306         ZX-8302 TXCTRL register
sv_txmode  equ      160           Offset to TXCTRL shadow sysvar
sv_thingl  equ      184           Offset to THING LIST pointer
mode_mask  equ      24            NET sets both mode bits
drive_mode equ      16            MDV mode bit 3=0, bit 4=1
mdv_bit    equ      4             MDV mode control bit number
ser_bit    equ      3             RS-232 SER1/SER2 flag bit
net_outbit equ      7             Network port output bit
net_input  equ      98336         QL IPC read register
net_inbit  equ      0             Network port input bit
*
* Bit input macros; code templates used to read each bit
*
read_high  macro
           move.w   d2,d0
poll\@     btst     #net_inbit,(a0)
           dbne     d0,poll\@
           endm
*
read_low   macro
           move.w   d2,d0    
poll\@     btst     #net_inbit,(a0)
           dbeq     d0,poll\@
           endm
*
* Compare D0 countdown with timeout in D3; result is D4 bit 0
*
decide     macro
           add.b    d4,d4
           cmp.b    d3,d0
           bcc.s    fast\@
           addq.b   #1,d4   
fast\@     
           endm
*
* Wait for a state change & time next bit; abort if necessary
*
up_bit     macro
           read_high
           beq.s \1
           decide
           endm
*
down_bit   macro
           read_low
           bne.s \1
           decide
           endm
*
* Bit output routines; send the most significant bit of D4.B
*           
send_high  macro
           move.l   d6,d3
           move.b   d1,tx_control
           add.b    d4,d4
           bcc.s    leave_hi\@
           add.w    d3,d3
leave_hi\@ dbra     d3,leave_hi\@
           endm
*
send_low   macro
           swap     d3
           move.b   d0,tx_control
           add.b    d4,d4
           bcc.s    leave_lo\@
           add.w    d3,d3
leave_lo\@ dbra     d3,leave_lo\@
           endm
*
* This is the start of the executable code
*
initialise lea.l    define,a1
           movea.w  $110.w,a2       BP.INIT vector
           jmp      (a2)
*
* NETREAD address,length
*
read_bits  lea.l    net_read,a4    Point A4 at reading routine
           bra.s    get_longs      Get parameters from BASIC
*
* NETPOLL address,length
*
netpoll    lea.l    poller,a4
           bra.s    get_longs
*
* NETBEEP rate,pulse_count
*
netbeep    lea.l    squeaker,a4
           bra.s    get_longs
*
* NETSEND address,length
*
send_bits  lea.l    net_send,a4
get_longs  movea.w  $118.w,a2       Pick up CA.GTLIN vector
           jsr      (a2)
           bne.s    exit
           subq.w   #2,d3           Two parameters are needed
           bne.s    bad_param
           movea.l  0(a1,a6.l),a2   Destination or beep rate
           move.l   4(a1,a6.l),d5   Length of message
           bmi.s    bad_param       D5 must be positive
           beq.s    exit            Zero bytes, that's easy!
*          moveq    #0,d0           Redundant; D0=0 if OK so far 
           trap     #1              MT.INF - point A0 at SYSBASE
*           
* Set supervisor mode and disable interrupts for real-time I/O
*
           trap     #0              Supervisor mode
           or.w     #$700,sr        Concentrate; interrupts off!
select_net move.b   sv_txmode(a0),d7
           move.b   d7,d0           Working copy of TXMODE
           andi.b   #mode_mask,d0   Check bits 3 & 4
           beq.s    not_busy
           cmp.b    #mode_mask,d0   In network mode?
           beq.s    ready
           btst     #mdv_bit,d0     Microdrives active
           beq.s    not_busy        Ignore SER bit for now...
*
* Eeek! Microdrives are using the ZX-8302
*
in_use     moveq    #-9,d0          Report IN USE
           bra.s    cheer_up        Return in user mode
bad_param  moveq    #-15,d0         Set Qdos ERR.BP report code
exit       rts
*
not_busy   move.b   d7,d0           Retrieve old mode
           ori.b    #mode_mask,d0
           move.b   d0,tx_control   Set ZX-8302 NET mode
ready      jmp      (a4)
*           
* NETBEEP - the test tone output routine
*
squeaker   move.b   d0,d1
           bset     #net_outbit,d1
           move.l   a2,d6           Pick up time constant
           move.l   d6,d2           Keep a copy
           swap     d2              Check high word
           tst.w    d2              If non-zero, use both
           bne.s    squeak
           swap     d6              Move zero to low word
           swap     d2
           move.w   d2,d6           Match high & low words 
*
squeak     move.b   d1,tx_control   Transmit a HIGH level
           move.l   d6,d3
loop_on    dbra     d3,loop_on      Wait at that level
           swap     d3
           nop                      Improve wave symmetry
           move.b   d0,tx_control   Transmit a LOW level
loop_off   dbra     d3,loop_off     Wait some more
           subq.l   #1,d5           Have we finished?
           bne.s    squeak          No, do it again
*
it_worked  moveq    #0,d0           Signal ERR.OK, it worked
release    move.b   d7,tx_control   Restore old ZX-8302 mode
cheer_up   andi.w   #$d8ff,sr       User mode, interrupts on
           rts
*
* The POLLER stores D5 bytes of bit times from (A2) onwards
*
poller     btst     #0,d5           Is D5 even?
           beq.s    even            
           addq.l   #1,d5           Make it even
even       lea.l    net_input,a0
           move.l   #2500,d3        Wait a few seconds
           move.l   #$FF00FF,d2     Byte timeouts
reader     read_high
           beq.s    check_time      No pulse yet...
           move.b   d0,(a2)+        Store exit count
           read_low
           move.b   d0,(a2)+        Store exit count
           subq.l   #2,d5           Count down in bytes
           bhi.s    reader
           bra.s    it_worked
*
check_time subq.l   #1,d3           Count down timer
           bne.s    reader          Try again!
           bra.s    timeout
*
* NETREAD address,length
*
net_read   bsr      find_thing2     Look for Thing address
           bne.s    release         Not found - give up!
           lea.l    net_input,a0    Point at the hardware
           move.w   4(a4),d2        Find slowest valid time
           move.w   d2,d3
           sub.w    2(a4),d3        Pick up 1 bit RX time
           bls.s    timeout         Too short!
*
start      movem.l  d5/d7,-(a7)     Preserve length and TX_MODE
           lea.l    key_spec,a3     Point at KEYROW(1) command
           moveq    #17,d0          Fetch MT.IPCOM trap key
           trap     #1              This uses D0, D1, D5, D7, A3
           movem.l  (a7)+,d5/d7
           and.b    #8+64,d1        ESC or SPACE bit set?
           beq.s    no_key
timeout    moveq    #-1,d0          Report not complete
           bra.s    release
no_key     btst     #net_inbit,(a0) Wait for block start bit
           beq.s    start           Loop till ESC or net busy
start_poll btst     #net_inbit,(a0)
           bne.s    start_poll      Wait for a LOW level
*
* Receive D5 bytes from the port at (A0) into memory at (A2)
*
read_byte  down_bit timeout
           up_bit   timeout
           down_bit timeout
           up_bit   timeout
           down_bit timeout2
           up_bit   timeout2
           down_bit timeout2
           up_bit   timeout2
           down_bit timeout2
           move.b   d4,(a2)+        Store byte
           read_high                Stop bit, length ignored
           subq.l   #1,d5
           bne      read_byte
finished   bra      it_worked
*
timeout2   bra      timeout         Extra exit; timing error
*
* NETSEND - prepare to transmit bytes
*
net_send   move.b   d0,d6           Save ZX-8302 TX mode
           bsr      find_thing2     Look for Thing address
           bne      release
           move.b   d6,d0           Restore ZX-8302 TX mode
           move.w   (a4),d6         Pick up TX delay
           swap     d6
           move.w   (a4),d6         Even mark/space ratio
           move.b   d0,d1           D0 is NET OFF pattern
           bset     #net_outbit,d1  D1 is NET ON pattern
           move.b   d1,tx_control   Activate net hardware
           move.w   #6000,d2        Wait a while for sync
get_ready  dbra     d2,get_ready
byte_loop  move.b   (a2)+,d4        Pick up data byte
           move.b   d1,tx_control   Send start pulse
           move.l   d6,d3
on_loop    dbra     d3,on_loop
           send_low
           send_high
           send_low
           send_high
           send_low
           send_high
           send_low
           send_high
           swap     d3
           move.b   d0,tx_control   Send stop pulse
off_loop   dbra     d3,off_loop
           subq.l   #1,d5           Count down bytes
           bne      byte_loop
           move.b   d1,tx_control   Terminal bit
last_bit   dbra     d6,last_bit
           move.b   d0,tx_control   De-activate network
           bra      it_worked
*
* NETRATE tx_time% , rx_time% , timeout%    (0 means no change)
*
set_rate   movea.w  $112.w,a2       Pick up CA.GTINT vector
           jsr      (a2)
           bne.s    no_good         Continue only if D0=0
           subq.w   #3,d3           Three parameters are needed
           bne.s    bad_param2
           trap     #0              Supervisor mode, fix A6
           movea.l  a1,a2           Save the RI stack offset
           bsr.s    find_thing
           bne.s    tidy_up
           move.w   0(a2,a6.l),d1   Check first parameter
           beq.s    default_tx
           move.w   d1,(a4)         Store TX speed
default_tx move.w   2(a2,a6.l),d1
           beq.s    default_rx
           move.w   d1,2(a4)        Store RX speed
default_rx move.w   4(a2,a6.l),d1
           beq.s    tidy_up
           move.w   d1,4(a4)        Store Timeout
tidy_up    bra      cheer_up        Revert to USER mode
*
bad_param2 moveq    #-15,d0
no_good    rts
*
* value%=NETVAR%(index%) - read NET time constant 1, 2 etc.
*
net_value  movea.w  $112.w,a2       Pick up CA.GTINT vector
           jsr      (a2)
           bne.s    no_good         Continue only if D0=0
           subq.w   #1,d3           One parameter is needed
           bne.s    bad_param2
           move.w   0(a1,a6.l),d7   Pick up parameter
           ble.s    bad_param2      It must be greater than 0
           add.w    d7,d7           Form word index
           trap     #0              Supervisor mode, fix A6
           bsr.s    find_thing
           bne.s    tidy_up         Oops; return D0 error code
           movea.l  $58(a6),a1      Recover RI stack offset
           move.w   -2(a4,d7.w),0(a1,a6.l)
           moveq    #3,d4           Return an integer
           bra.s    tidy_up
*
* Locate the FlexyNet Thing; create it if necessary
*
find_thing moveq    #0,d0           MT.INF trap key
           trap     #1
find_thing2 movea.l a0,a4           A4 -> System variables
           lea.l    sv_thingl(a4),a5
           lea.l    thing_name,a0   A0 -> required name
           suba.l   a6,a0           Make it relative
           movea.w  $e6.w,a3        Preset UT.CSTR vector
next_thing move.l   (a5),d0         Check list pointer
           beq.s    make_thing      End of list found?
           movea.l  d0,a5           A5 -> Thing base
           lea.l    42(a5),a1       Point at its text
           suba.l   a6,a1           All is relative...
           moveq    #1,d0           Ignore letter case
           jsr      (a3)            Call UT.CSTR
           tst.l    d0              Do names match?
           bne.s    next_thing
got_thing  lea.l    60(a5),a4       A4 -> Thing values
           rts                      Return OK, D0=0, Z set
*
make_thing moveq    #24,d0          MT.ALCHP trap key
           moveq    #60+6,d1        Total space required
           moveq    #0,d2           Owner is SuperBASIC
           movea.l  a2,a5           Save A2 (SEND/READ base)
           trap     #1
           movea.l  a5,a2           Restore A2
           tst.l    d0              Did ALCHP work?
           bne.s    done_thing      If not, return NZ error code
           lea.l    52(a0),a5       Point at Thing data values
           move.l   a5,16(a0)       Set pointer to Thing values
           lea.l    thing_spec,a5   Point at ASCII header
           lea.l    38(a0),a3       Point at destination
           moveq    #(thing_end-thing_spec)/2-1,d1
set_header move.w   (a5)+,(a3)+     Copy one header word
           dbra     d1,set_header   Initialise the rest
           lea.l    sv_thingl(a4),a5
           move.l   (a5),(a0)       Extend the linked list
           move.l   a0,(a5)         Update the list start
           lea.l    60(a0),a4       Make A4 -> Thing's values
           moveq    #0,d0           Set Z flag; ERR.OK
done_thing rts
*
thing_spec dc.b     '1.09'          Version number
thing_name dc.w     8               Name length
           dc.b     'flexynet'      Thing name
           dc.b     'THG%'
           dc.l     2               Shared data follows
thing_data dc.w     5,3,127         TX, RX, & Limit times
thing_end
*
key_spec   dc.b     9,1,0,0,0,0,1,2 KEYROW(1) IPC message
*
define     dc.w     5               Number of procedures
           dc.w     send_bits-*
           dc.b     7,'NETSEND'
           dc.w     netbeep-*
           dc.b     7,'NETBEEP'
           dc.w     netpoll-*
           dc.b     7,'NETPOLL'
           dc.w     read_bits-*
           dc.b     7,'NETREAD'
           dc.w     set_rate-*
           dc.b     7,'NETRATE'
           dc.w     0               End of procedures
           dc.w     1               One function
           dc.w     net_value-*
           dc.b     7,'NETVAR%'
           dc.w     0               End of functions
           end

