

seti $printhelp = $printhelp
seti $help = $help
if $help = 1
  echo 'WD patch ram test'

  if $printhelp = 1
    exit 0
  endif
  echo 'Hit ENTER to continue...'
  userinput $choicestring
  previousscript
endif
echo ""

include good_subroutines

# set a blank string variable to compare with user input
# make sure not to set this anyplace else
sets $null = ""

# set this to itself for use with the menu system
# make sure not to set this anyplace else
# if it was not previously set it will = 0
seti $using_menu = $using_menu



# set these for later, and to make it easy to change if needed
seti $rom_read_block_size = 65536
seti $ram_read_block_size = 65536

seti $ram_start = 0x8000000
seti $ram_size = 0x40000
seti $max_ram_size = 0x2000000

if $ram_size < $ram_read_block_size
  seti $ram_read_block_size = $ram_size
endif
seti $divide_check = $rom_read_block_size % 512
if $divide_check != 0
  echo "rom_read_block_size is not dividable by 512"
  previousscript
endif
seti $divide_check = $ram_read_block_size % 512
if $divide_check != 0
  echo "ram_read_block_size is not dividable by 512"
  previousscript
endif


gosub find_ram_size

gosub dump_ram

gosub process_ram


previousscript
end





subroutine find_ram_size
  gosub enable_vsc

  seti $find_ram_read_size = 0x200
  scratchpadsize $find_ram_read_size
  seti $position = $ram_start
  seti $remainder = $find_ram_read_size
  if $remainder != 0
    seti $write_offset = 0
    seti $read_size = $remainder
    hex
    echo "position=0x" $position "  rsize=0x" $read_size
    decimal
    # vsc to prepare for ram read
    #echo "prepare to read ram"
    buffersize 0x200
    clearbuffer
    seti $offsetl = $position & 0xff
    seti $offsetm1 = $position > 8
    seti $offsetm1 = $offsetm1 & 0xff
    seti $offsetm2 = $position > 16
    seti $offsetm2 = $offsetm2 & 0xff
    seti $offseth = $position > 24
    seti $offseth = $offseth & 0xff
    seti $sizel = $read_size & 0xff
    seti $sizem1 = $read_size > 8
    seti $sizem1 = $sizem1 & 0xff
    seti $sizem2 = $read_size > 16
    seti $sizem2 = $sizem2 & 0xff
    seti $sizeh = $read_size > 24
    seti $sizeh = $sizeh & 0xff
    setbuffer 0
      13 00 01 00 $offsetl $offsetm1 $offsetm2 $offseth $sizel $sizem1 $sizem2 $sizeh
    endbuffer
    setwritepio
    ata28cmd 0xd6 0x01 0xbe 0x4f 0xc2 0xa0 0xb0
    # check if command failed
    gosub status_check
    # vsc to get the ram
    seti $sectors = $read_size / 512
    buffersize $read_size
    clearbuffer
    setreadpio
    #echo "reading ram"
    ata28cmd 0xd5 $sectors 0xbf 0x4f 0xc2 0xa0 0xb0
    # check if command failed
    gosub status_check
    # copy the buffer to the scratchpad
    copybuffertoscratchpad 0 $write_offset $read_size
  endif

  printscratchpad 0 32
  seti $check1 = scratchpad 0 qw
  seti $check2 = scratchpad 8 qw

  seti $read_size = $find_ram_read_size
  while 1 = 1
    seti $position = $ram_start + $ram_size
    hex
    echo "position=0x" $position "  size=0x" $read_size
    decimal
    # vsc to prepare for ram read
    #echo "prepare to read ram"
    buffersize 0x200
    clearbuffer
    seti $offsetl = $position & 0xff
    seti $offsetm1 = $position > 8
    seti $offsetm1 = $offsetm1 & 0xff
    seti $offsetm2 = $position > 16
    seti $offsetm2 = $offsetm2 & 0xff
    seti $offseth = $position > 24
    seti $offseth = $offseth & 0xff
    seti $sizel = $read_size & 0xff
    seti $sizem1 = $read_size > 8
    seti $sizem1 = $sizem1 & 0xff
    seti $sizem2 = $read_size > 16
    seti $sizem2 = $sizem2 & 0xff
    seti $sizeh = $read_size > 24
    seti $sizeh = $sizeh & 0xff
    setbuffer 0
      13 00 01 00 $offsetl $offsetm1 $offsetm2 $offseth $sizel $sizem1 $sizem2 $sizeh
    endbuffer
    setwritepio
    ata28cmd 0xd6 0x01 0xbe 0x4f 0xc2 0xa0 0xb0
    # check if command failed
    gosub status_check

    # vsc to get the ram
    seti $sectors = $read_size / 512
    buffersize $read_size
    clearbuffer
    setreadpio
    #echo "reading ram"
    ata28cmd 0xd5 $sectors 0xbf 0x4f 0xc2 0xa0 0xb0
    # check if command failed
    gosub status_check

    seti $cmp1 = buffer 0 qw
    seti $cmp2 = buffer 8 qw
    if $cmp1 = $check1
      if $cmp2 = $check2
        hex
        echo "ram size is 0x" $ram_size
        decimal
        break
      endif
    endif

    seti $ram_size = $ram_size * 2
    if $ram_size > $max_ram_size
      hex
      echo "ram size not found, using default maximum size of 0x" $max_ram_size
      decimal
      seti $ram_size = $max_ram_size
      break
    endif
  done

  gosub disable_vsc
endsubroutine





subroutine dump_ram
  gosub enable_vsc

  scratchpadsize $ram_size
  seti $position = $ram_start
  seti $read_size = $ram_read_block_size
  seti $block_count = $ram_size / $ram_read_block_size
  seti $remainder = $ram_size % $ram_read_block_size
  seti $count = 0
  while $count < $block_count
    hex
    echo "position=0x" $position "  size=0x" $read_size
    decimal
    # vsc to prepare for ram read
    #echo "prepare to read ram"
    buffersize 0x200
    clearbuffer
    seti $offsetl = $position & 0xff
    seti $offsetm1 = $position > 8
    seti $offsetm1 = $offsetm1 & 0xff
    seti $offsetm2 = $position > 16
    seti $offsetm2 = $offsetm2 & 0xff
    seti $offseth = $position > 24
    seti $offseth = $offseth & 0xff
    seti $sizel = $read_size & 0xff
    seti $sizem1 = $read_size > 8
    seti $sizem1 = $sizem1 & 0xff
    seti $sizem2 = $read_size > 16
    seti $sizem2 = $sizem2 & 0xff
    seti $sizeh = $read_size > 24
    seti $sizeh = $sizeh & 0xff
    setbuffer 0
      13 00 01 00 $offsetl $offsetm1 $offsetm2 $offseth $sizel $sizem1 $sizem2 $sizeh
    endbuffer
    setwritepio
    ata28cmd 0xd6 0x01 0xbe 0x4f 0xc2 0xa0 0xb0
    # check if command failed
    gosub status_check
    # send again
    ata28cmd 0xd6 0x01 0xbe 0x4f 0xc2 0xa0 0xb0
    # check if command failed
    gosub status_check

    # vsc to get the ram
    seti $sectors = $read_size / 512
    buffersize $read_size
    clearbuffer
    setreadpio
    #echo "reading ram"
    ata28cmd 0xd5 $sectors 0xbf 0x4f 0xc2 0xa0 0xb0
    # check if command failed
    gosub status_check
    seti $write_offset = $count * $read_size
    # copy the buffer to the scratchpad
    copybuffertoscratchpad 0 $write_offset $read_size
    # write the buffer to file
    #writebuffer $file 0 $write_offset $read_size
    seti $count = $count + 1
    seti $position = $position + $ram_read_block_size
  done

  if remainder != 0
    seti $write_offset = $write_offset + $read_size
    seti $read_size = $remainder
    hex
    echo "position=0x" $position "  rsize=0x" $read_size
    decimal
    # vsc to prepare for ram read
    echo "prepare to read ram"
    buffersize 0x200
    clearbuffer
    seti $offsetl = $position & 0xff
    seti $offsetm1 = $position > 8
    seti $offsetm1 = $offsetm1 & 0xff
    seti $offsetm2 = $position > 16
    seti $offsetm2 = $offsetm2 & 0xff
    seti $offseth = $position > 24
    seti $offseth = $offseth & 0xff
    seti $sizel = $read_size & 0xff
    seti $sizem1 = $read_size > 8
    seti $sizem1 = $sizem1 & 0xff
    seti $sizem2 = $read_size > 16
    seti $sizem2 = $sizem2 & 0xff
    seti $sizeh = $read_size > 24
    seti $sizeh = $sizeh & 0xff
    setbuffer 0
      13 00 01 00 $offsetl $offsetm1 $offsetm2 $offseth $sizel $sizem1 $sizem2 $sizeh
    endbuffer
    setwritepio
    ata28cmd 0xd6 0x01 0xbe 0x4f 0xc2 0xa0 0xb0
    # check if command failed
    gosub status_check
    # send again
    ata28cmd 0xd6 0x01 0xbe 0x4f 0xc2 0xa0 0xb0
    # check if command failed
    gosub status_check

    # vsc to get the ram
    seti $sectors = $read_size / 512
    buffersize $read_size
    clearbuffer
    setreadpio
    echo "reading ram"
    ata28cmd 0xd5 $sectors 0xbf 0x4f 0xc2 0xa0 0xb0
    # check if command failed
    gosub status_check
    # copy the buffer to the scratchpad
    copybuffertoscratchpad 0 $write_offset $read_size
    # write the buffer to file
    #writebuffer $file 0 $write_offset $read_size
  endif

  gosub disable_vsc
  echo "ram dump successful"
endsubroutine





subroutine process_ram
  echo "processing ram"

  # search for modules and check them, starting from the end of the last rom entry
  echo "Searching for modules."
  echo "Note that it is normal for some module checksums to be bad,"
  echo "as they are not full size in the ROM."
  sets $header_check = "ROYL"
  seti $position = 0
  seti $search_end = $ram_size - 4
  while $position < $search_end
    sets $header = scratchpad $position 4
    if $header = $header_check
      # process the module
      seti $working_offset = $position
      #echo "Header:"
      #printscratchpad $working_offset 4
      seti $working_offset = $position + 8
      seti $mod_id = scratchpad $working_offset w
      hex
      #echo "Module ID = 0x" $mod_id

      # find how many sectors the module contains
      seti $working_offset = $position + 0xa
      seti $mod_length_sectors = scratchpad $working_offset
      seti $working_offset = $position + 0xb
      seti $tempnum = scratchpad $working_offset
      seti $tempnum = $tempnum > 8
      seti $mod_length_sectors = $mod_length_sectors + $tempnum

      #echo "Size in sectors = 0x" $mod_length_sectors
      seti $working_offset = $position + 0xc
      seti $checksum = scratchpad $working_offset dw
      #echo "32 bit checksum = 0x" $checksum
      seti $working_offset = $position + 0x10
      #echo "Mod version:"
      #printscratchpad $working_offset 8
      decimal

      seti $module_size = $mod_length_sectors * 512
      seti $calculated_checksum = 0
      seti $count = 0
      while $count < $module_size
        seti $working_offset = $position + $count
        if $working_offset > $search_end
          echo "end of module is past end of rom"
          break
        endif
        seti $dword = scratchpad $working_offset dw
        seti $calculated_checksum = $calculated_checksum + $dword
        seti $count = $count + 4
        # skip the actual checksum bytes
        if $count = 0xc
          seti $count = $count + 4
        endif
      done
      seti $calculated_checksum = $calculated_checksum & 0xffffffff
      seti $calculated_checksum = 0xffffffff - $calculated_checksum
      seti $calculated_checksum = $calculated_checksum + 1

      hex
      if $checksum = $calculated_checksum
        sets $checkstring = " calulated=0x" $calculated_checksum " (good)"
      else
        sets $checkstring = " calulated=0x" $calculated_checksum " (bad)"
      endif
      echo "pos=0x" $position " ModID=0x" $mod_id " size=0x" $mod_length_sectors $checkstring
      decimal
      if $mod_id = 2
        seti $module_position = $position
        seti $module_length = $mod_length_sectors * 512
        gosub patch_module
      endif
    endif
    seti $position = $position + 512
  done
endsubroutine





subroutine patch_module
  if 1 = 1
    gosub enable_vsc
    seti $wposition = $module_position
    seti $wposition = $wposition + 0x8000000
    seti $write_size = $module_length
    hex
    echo "wposition=0x" $wposition "  wsize=0x" $write_size
    decimal
    # vsc to prepare for ram write
    echo "prepare to write ram"
    buffersize 0x200
    clearbuffer
    seti $offsetl = $wposition & 0xff
    seti $offsetm1 = $wposition > 8
    seti $offsetm1 = $offsetm1 & 0xff
    seti $offsetm2 = $wposition > 16
    seti $offsetm2 = $offsetm2 & 0xff
    seti $offseth = $wposition > 24
    seti $offseth = $offseth & 0xff
    seti $sizel = $write_size & 0xff
    seti $sizem1 = $write_size > 8
    seti $sizem1 = $sizem1 & 0xff
    seti $sizem2 = $write_size > 16
    seti $sizem2 = $sizem2 & 0xff
    seti $sizeh = $write_size > 24
    seti $sizeh = $sizeh & 0xff
    setbuffer 0
      13 00 02 00 $offsetl $offsetm1 $offsetm2 $offseth $sizel $sizem1 $sizem2 $sizeh
    endbuffer
    hex
    echo $offsetl " " $offsetm1 " " $offsetm2 " " $offseth "  " $sizel " " $sizem1 " " $sizem2 " " $sizeh
    decimal
    setwritepio
    ata28cmd 0xd6 0x01 0xbe 0x4f 0xc2 0xa0 0xb0
    # check if command failed
    gosub status_check
    # vsc to get the ram
    seti $sectors = $read_size / 512
    buffersize $module_length
  clearbuffer
  copyscratchpadtobuffer $module_position 0 $module_length
  #printbuffer 0 $module_length
  #gosub process_mod02
  seti $pcount = 26
  seti $poffset = $pcount * 4
  seti $pcount = $pcount + 1
  seti $poffset = $poffset + 0x32
  seti $record_offset = buffer $poffset w
  seti $poffset = $poffset + 2
  seti $record_size = buffer $poffset w
  echo ""
  echo "Data record #" $pcount ":"
  printbuffer $record_offset $record_size
  seti $byte_offset = $record_offset + 2
  setbuffer $byte_offset
    00
  endbuffer

  setbuffer $record_offset
    00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    00 00 00 00 00 00 00 00 00 00 00
  endbuffer

  echo "Data record #" $pcount " after patch:"
  printbuffer $record_offset $record_size

  # calculate new checksum and insert it
  seti $calculated_checksum = 0
  gosub calculate_checksum_buffer
  hex
  echo "Calculated Checksum = 0x" $calculated_checksum
  decimal
  seti $b1 = $calculated_checksum & 0xff
  seti $calculated_checksum = $calculated_checksum > 8
  seti $b2 = $calculated_checksum & 0xff
  seti $calculated_checksum = $calculated_checksum > 8
  seti $b3 = $calculated_checksum & 0xff
  seti $calculated_checksum = $calculated_checksum > 8
  seti $b4 = $calculated_checksum & 0xff
  setbuffer 0x0c
    $b1 $b2 $b3 $b4
  endbuffer

    setwritepio
    echo "writing ram"
    ata28cmd 0xd6 $mod_length_sectors 0xbf 0x4f 0xc2 0xa0 0xb0
    # check if command failed
    gosub status_check
    gosub disable_vsc
  endif


endsubroutine





subroutine process_mod02
  # process the module
  echo "Header:"
  printbuffer 0 4
  seti $mod_id = buffer 8 w
  echo "Module ID = " $mod_id
  echo "Size in sectors = " $mod_length_sectors
  seti $checksum = buffer 0xc dw
  hex
  echo "32 bit checksum = 0x" $checksum
  decimal
  echo "Mod version:"
  printbuffer 0x10 8
  seti $total_records = buffer 0x30 w
  echo "Total records = " $total_records

  # process the data records
  seti $count = 0
  while $count < $total_records
    seti $offset = $count * 4
    seti $count = $count + 1
    seti $offset = $offset + 0x32
    seti $record_offset = buffer $offset w
    seti $offset = $offset + 2
    seti $record_size = buffer $offset w
    echo ""
    echo "Data record #" $count ":"
    printbuffer $record_offset $record_size
  done
endsubroutine





subroutine calculate_checksum_buffer
seti $calculated_checksum = 0
  seti $count = 0
  while $count < $module_length
    seti $dword = buffer $count dw
    seti $calculated_checksum = $calculated_checksum + $dword
    seti $count = $count + 4
    # skip the actual checksum bytes
    if $count = 0xc
      seti $count = $count + 4
    endif
  done
  seti $calculated_checksum = $calculated_checksum & 0xffffffff
  seti $calculated_checksum = 0xffffffff - $calculated_checksum
  seti $calculated_checksum = $calculated_checksum + 1
endsubroutine





subroutine status_check
  seti $command_failed = 0
  gosub check_command_status
  if $command_failed = 1
    echo "Command failed!"
    gosub show_sense_data
    gosub show_ata_return_status
    previousscript
  endif
endsubroutine



subroutine enable_vsc
  # enable vendor specific commands
  echo "enable vsc"
  buffersize 0
  setreadpio
  ata28cmd 0x45 0x0b 0x00 0x44 0x57 0xa0 0x80
  # check if command failed
  gosub status_check
endsubroutine



subroutine disable_vsc
  # disable vendor specific commands
  echo "disable vsc"
  buffersize 0
  setreadpio
  ata28cmd 0x44 0x0b 0x00 0x44 0x57 0xa0 0x80
  # check if command failed
  gosub status_check
endsubroutine



