''Dracblade driver for talking to a ram chip via three latches
'' Modified code from Cluso's triblade
' DoCmd(command_, hub_address, ram_address, block_length)
' R - read bytes at address n up (n to n+block_length) where n =0 to 65535 (ie lower 64k of the sram chip)
' W - write bytes at address n up
' I - initialise
' N - Led on
' F - Led off
' H - set high latch to value in ramaddress A16 to A23 (will include the led)
DB_IN            = %00001001_01110000_00000000_00000000 'maske: dbus-eingabe

VAR

' communication params(5) between cog driver code - only "command" and "errx" are modified by the driver
   long  command, hubaddrs, ramaddrs, blocklen, errx, cog ' rendezvous between spin and assembly (can be used cog to cog)
'        command  = R, W, N, F H =0 when operation completed by cog
'        hubaddrs = hub address for data buffer
'        ramaddrs = ram address for data ($0000 to $FFFF)
'        blocklen = ram buffer length for data transfer
'        errx     = returns =0 (false=good), else <>0 (true & error code)
'        cog      = cog no of driver (set by spin start routine)
   
PUB start : err_
' Initialise the Drac Ram driver. No actual changes to ram as the read/write routines handle this
  command := "I"
  cog := 1 + cognew(@tbp2_start, @command)
  if cog == 0
    err_ := $FF                 ' error = no cog
  else
    repeat while command        ' driver cog sets =0 when done
    err_ := errx                ' driver cog sets =0 if no error, else xx = error code

PUB stop
   if cog
      cogstop(cog~ - 1)      

PUB DoCmd(command_, hub_address, ram_address, block_length) : err_
' Do the command: R, W, N, F, H
  hubaddrs := hub_address       ' hub address start
  ramaddrs := ram_address       ' ram address start
  blocklen := block_length      ' block length
  command  := command_          ' must be last !!
' Wait for command to complete and get status
  repeat while command          ' driver cog sets =0 when done
  err_ := errx                  ' driver cog sets =0 if no error, else xx = error code
  dira:=db_in
  'dira[24]:=1

PUB rendezvous
  return @command

DAT
'' +--------------------------------------------------------------------------+
'' | Dracblade Ram Driver (with grateful acknowlegements to Cluso)            |
'' +--------------------------------------------------------------------------+
                        org     0
tbp2_start    ' setup the pointers to the hub command interface (saves execution time later
                                      '  +-- These instructions are overwritten as variables after start
comptr                  mov     comptr, par     ' -|  hub pointer to command                
hubptr                  mov     hubptr, par     '  |  hub pointer to hub address            
ramptr                  add     hubptr, #4      '  |  hub pointer to ram address            
lenptr                  mov     ramptr, par     '  |  hub pointer to length                 
errptr                  add     ramptr, #8      '  |  hub pointer to error status           
cmd                     mov     lenptr, par     '  |  command  I/R/W/G/P/Q                  
hubaddr                 add     lenptr, #12     '  |  hub address                           
ramaddr                 mov     errptr, par     '  |  ram address                           
len                     add     errptr, #16     '  |  length                                
err                     nop                     ' -+  error status returned (=0=false=good) 


' Initialise hardware (unlike the triblade, just tristates everything and read/write set the pins)
init                    mov     err, #0                  ' reset err=false=good
                        mov     dira,zero                ' tristate the pins

done                    wrlong  err, errptr             ' status  =0=false=good, else error x
                        wrlong  zero, comptr            ' command =0 (done)
' wait for a command (pause short time to reduce power)
pause                   mov     ctr, delay      wz      ' if =0 no pause
              if_nz     add     ctr, cnt
              if_nz     waitcnt ctr, #0                 ' wait for a short time (reduces power)
                        rdlong  cmd, comptr     wz      ' command ?
              if_z      jmp     #pause                  ' not yet
' decode command
                        cmp     cmd, #"R"       wz      ' R = read block
              if_z      jmp     #rdblock
                        cmp     cmd, #"W"       wz      ' W = write block
              if_z      jmp     #wrblock
                        cmp     cmd, #"N"       wz      ' N= led on
              if_z      jmp     #led_turn_on
                        cmp     cmd, #"F"       wz      ' F = led off
              if_z      jmp     #led_turn_off
                        cmp     cmd, #"H"       wz      ' H sets the high latch
              if_z      jmp     #sethighlatch
                        mov     err, cmd                ' error = cmd (unknown command)
                        jmp     #done


tristate                mov     dira,zero                ' all inputs to zero
                        jmp     #done

' turn led on
led_turn_on             or      HighLatch,ledpin        ' set the led pin high
                        jmp     #OutputHighLatch         ' send this out

led_turn_off            andn    HighLatch,ledpin        ' set the led pin low
                        jmp     #OutputHighLatch         ' send this out

' set high address bytes with command H, pass value in third variable of the DoCmd
' 4 bytes - masks off all but bits 16 to 23

sethighlatch            call #ram_open                  ' gets address value in 'address'
                        shr  address,#16                ' shift right by 16 places
                        and  address,#$FF               ' ensure rest of bits zero
                        mov  HighLatch,address          ' put value into HighLatch
                        jmp  #OutputHighLatch           ' and output it

'---------------------------------------------------------------------------------------------------------
'Memory Access Functions

rdblock                 call    #ram_open               ' get variables from hub variables
rdloop                  call    #read_memory_byte       ' read byte from address into data_8
                        wrbyte  data_8,hubaddr          ' write data_8 to hubaddr ie copy byte to hub
                        add     hubaddr,#1              ' add 1 to hub address
                        add     address,#1              ' add 1 to ram address
                        djnz    len,#rdloop             ' loop until done
                        jmp     #init                   ' reinitialise

wrblock                 call    #ram_open                        
wrloop                  rdbyte  data_8, hubaddr         ' copy byte from hub
                        call    #write_memory_byte      ' write byte from data_8 to address
                        add     hubaddr,#1              ' add 1 to hub address
                        add     address,#1              ' add 1 to ram address
                        djnz    len,#wrloop             ' loop until done
                        jmp     #init                   ' reinitialise

ram_open                rdlong  hubaddr, hubptr         ' get hub address
                        rdlong  ramaddr, ramptr         ' get ram address
                        rdlong  len, lenptr             ' get length
                        mov     err, #5                 ' err=5
                        mov     address,ramaddr         ' cluso's variable 'ramaddr' to dracblade variable 'address'
ram_open_ret            ret
  
read_memory_byte        call #RamAddress                ' sets up the latches with the correct ram address
                        mov dira,LatchDirection2        ' for reads so P0-P7 tristate till do read
                        mov outa,GateHigh               ' actually ReadEnable but they are the same
                        nop
                        nop
                        andn outa,GateHigh              ' set gate low
                        nop
                        nop
                        mov data_8, ina                 ' read SRAM
                        and data_8, #$FF                ' extract 8 bits
                        nop                             ' no nop doesn't work, one does, so put in two to be sure
                        nop
                        or  outa,GateHigh               ' set the gate high again
                        nop                             ' no nop doesn't work, one does, so put in two to be sure
                        nop
read_memory_byte_ret    ret

write_memory_byte       call #RamAddress                ' sets up the latches with the correct ram address
                        mov outx,data_8                 ' get the byte to output
                        and outx, #$FF                  ' ensure upper bytes=0
                        or outx,WriteEnable             ' or with correct 138 address
                        mov outa,outx                   ' send it out
                        andn outa,GateHigh              ' set gate low
                        nop                             ' another NOP
                        nop
                        or outa,GateHigh                ' set it high again
                        nop                             ' no nop doesn't work, one does, so put in two to be sure
                        nop
write_memory_byte_ret   ret

RamAddress ' sets up the ram latches. Assumes high latch A16-A18 low so only accesses 64k of ram
                        mov dira,LatchDirection         ' set up the pins for programming latch chips
                        mov outx,address                ' get the address into a temp variable
                        and outx,#$FF                   ' mask the low byte
                        or  outx,LowAddress             ' or with 138 low address
                        mov outa,outx                   ' send it out
                        nop                             ' no nop doesn't work, one does, so put in two to be sure
                        nop
                        andn outa,GateHigh              ' set gate low
                        nop
                        nop                            ' ?? a NOP
                        or outa,GateHigh                ' set it high again  
                        nop                             ' no nop doesn't work, one does, so put in two to be sure
                        nop                                ' now repeat for the middle byte
                        mov outx,address                ' get the address into a temp variable
                        shr outx,#8                     ' shift right by 8 places
                        and outx,#$FF                   ' mask the low byte
                        or  outx,MiddleAddress          ' or with 138 middle address
                        mov outa,outx                   ' send it out
                        andn outa,GateHigh              ' set gate low
                        nop
                        nop
                        or outa,GateHigh                ' set it high again
                        nop                             ' no nop doesn't work, one does, so put in two to be sure
                        nop
                        'mov outx,address                ' get the address into a temp variable
                        'shr outx,#8                     ' shift right by 8 places
                        'and outx,#$FF                   ' mask the low byte
                        'or  outx,HighAddress          ' or with 138 middle address
                        'mov outa,outx                   ' send it out
                        'andn outa,GateHigh              ' set gate low

'                        or outa,GateHigh                ' set it high again


RamAddress_ret          ret

OutputHighLatch ' sends out HighLatch to the 374 that does A16-19, led and the 4 spare outputs
                        mov     dira,latchdirection     ' setup active pins 138 and bus
                        mov     outa,HighLatch          ' send out HighLatch
                        or      outa,HighAddress        ' or with the high address
                        andn    outa,GateHigh           ' set gate low
                        or      outa,GateHigh           ' set the gate high again
OutputHighLatch_ret     jmp     #tristate               ' set pins tristate


{
dir_138_read	long    %00000001_01110000_00000000_00000000	' for reads so data lines are tristate till the read
dir_138_write	long	%00000001_01110000_00000000_11111111	' for writes so data lines are outputs
address_low	long	%00000000_00100000_00000000_00000000	' low address latch = xxxx010x and gate low xxxxxxx0
address_mid	long	%00000000_00110000_00000000_00000000	' middle address latch = xxxx011x and gate low xxxxxxx0
address_high	long	%00000000_01000000_00000000_00000000	' high address latch = xxxx100x and gate low xxxxxxx0
write_enable	long	%00000000_00010000_00000000_00000000	' write enable xxxx001x and gate low xxxxxxx0
}

delay                   long    80                                    ' waitcnt delay to reduce power (#80 = 1uS approx)
ctr                     long    0                                     ' used to pause execution (lower power use) & byte counter
GateHigh                long    %00000001_00000000_00000000_00000000  ' HC138 gate high, all others must be low
Outx                    long    0                                     ' for temp use, same as n in the spin code
LatchDirection          long    %00000001_01110000_00000000_11111111 ' 138 active, gate active and 8 data lines active
LatchDirection2         long    %00000001_01110000_00000000_00000000 ' for reads so data lines are tristate till the read
LowAddress              long    %00000001_00100000_00000000_00000000 ' low address latch = xxxx010x and gate high xxxxxxx1
MiddleAddress           long    %00000001_00110000_00000000_00000000 ' middle address latch = xxxx011x and gate high xxxxxxx1
HighAddress             long    %00000001_01000000_00000000_00000000 ' high address latch = xxxx100x and gate high xxxxxxx1
'ReadEnable long    %00000000_00000000_00000001_00000000 ' /RD = xxxx000x and gate high xxxxxxx1
                                                        ' commented out as the same as GateHigh
WriteEnable             long    %00000001_00010000_00000000_00000000 ' /WE = xxxx001x and gate high xxxxxxx1
Zero                    long    %00000000_00000000_00000000_00000000 ' for tristating all pins
data_8                  long    %00000000_00000000_00000000_00000000 ' so code compatability with zicog driver
address                 long    %00000000_00000000_00000000_00000000 ' address for ram chip
ledpin                  long    %00000000_00000000_00000000_00000000 ' to turn on led
HighLatch               long    %00000000_01000000_00000000_00000000 ' static value for the 374 latch that does the led, hA16-A19 and the other 4 outputs
