diff --git a/doc/protocol.txt b/doc/protocol.txt index 0cfe3ec..2a803a1 100644 --- a/doc/protocol.txt +++ b/doc/protocol.txt @@ -19,6 +19,7 @@ $07 1 byte opcode $01 = VOLUME CATALOG REQUEST $02 = READ T/S REQUEST $03 = WRITE T/S REQUEST + $04 = INIT VOLUME REQUEST FOR CAPABILITIES REQUEST No further data @@ -50,6 +51,16 @@ $4C null byte (i.e. filename can be 55 bytes long, MUST be at least 1 null at t FOR WRITE $4d.. sector data +FOR INIT VOLUME +$00..$3c volume name (up to 56 chars, null padded) +$37 null byte +$38 system architecture ID +$39 file system ID +$3A..$3B number of tracks +$3C..$3D sector length (in bytes) +$3E..$3F ?? + + FOR ALL RESPONSES $07 1 byte opcode @@ -106,6 +117,16 @@ $0A..$49 volume name (null padded 64 bytes) FOR READ $4A.. sector data + +FOR INIT VOLUME +$00..$3c volume name (up to 56 chars, null padded) +$37 null byte +$38 system architecture ID +$39 file system ID +$3A..$3B number of tracks +$3C..$3D sector length (in bytes) + + FOR ERROR @@ -131,3 +152,4 @@ $06..$0b bytes $04..$09 from original request $0c..$4c error message (null padded 63 bytes) $4d null byte + diff --git a/server/lib/tndp.rb b/server/lib/tndp.rb index efe6f0f..e149205 100644 --- a/server/lib/tndp.rb +++ b/server/lib/tndp.rb @@ -25,10 +25,12 @@ module TNDP 0x01 =>"VOLUME CATALOG REQUEST", 0x02 =>"READ SECTOR REQUEST", 0x03 =>"WRITE SECTOR REQUEST", + 0x04=>"INIT VOLUME REQUEST", 0x80 =>"CAPABILITIES RESPONSE", 0x81 =>"VOLUME CATALOG RESPONSE", 0x82 =>"READ SECTOR RESPONSE", 0x83 =>"WRITE SECTOR RESPONSE", + 0x84=>"INIT VOLUME RESPONSE", 0xFF =>"ERROR RESPONSE", } @@ -357,6 +359,52 @@ SECTOR LENGTH: $#{"%04x" % sector_length}" end end + class CreateVolumeRequestMessage < BaseMessage + attr_reader :system_architecture,:volume_name,:track_count,:sector_length + OPCODE=0x04 + def initialize(args={}) + args[:opcode]=OPCODE if args[:opcode].nil? + [:system_architecture,:volume_name,:track_count,:sector_length].each do |arg| + raise "#{arg} must be specified in a #{self.class}" if args[arg].nil? + end + @system_architecture=args[:system_architecture] + @track_count=args[:track_count] + @sector_length=args[:sector_length] + @volume_name=args[:volume_name] + + super(args) + end + + def to_s + system_architecture_id=TNDP::SYSTEM_ARCHITECTURES[system_architecture] + + super+" +VOLUME NAME: #{volume_name} +ARCHITECTURE: #{system_architecture} [$#{"%02X"%system_architecture_id}] +TRACK COUNT: $#{"%04X"%track_count} +SECTOR LENGTH: $#{"%04x" % sector_length}" + end + + def to_buffer + super+[volume_name[0,MAX_VOLUME_NAME_LENGTH],TNDP::SYSTEM_ARCHITECTURES[system_architecture],track_count,sector_length].pack("Z#{TNDP::MAX_VOLUME_NAME_LENGTH+1}Cnn") + end + + def self.from_buffer(buffer) + signature,version_id,transaction_id,opcode,volume_name,system_architecture_id,track_count,sector_length=buffer.unpack("Z4CnCZ#{TNDP::MAX_VOLUME_NAME_LENGTH+1}Cnn") + self.new({:signature=>signature,:version=>version_id,:transaction_id=>transaction_id,:opcode=>opcode,:volume_name=>volume_name, + :system_architecture=>TNDP::SYSTEM_ARCHITECTURES.key_by_value(system_architecture_id), :track_count=>track_count,:sector_length=>sector_length}) + end + end + + class CreateVolumeResponseMessage < CreateVolumeRequestMessage + OPCODE=0x84 + def initialize(args={}) + args[:opcode]=OPCODE + super(args) + end + end + + class ErrorResponseMessage < BaseMessage OPCODE=0xFF attr_reader :errorcode,:error_description,:original_data_elements diff --git a/server/lib/tndp_server.rb b/server/lib/tndp_server.rb index 48c1bac..d8b9544 100644 --- a/server/lib/tndp_server.rb +++ b/server/lib/tndp_server.rb @@ -19,6 +19,10 @@ require 'LibRipXplore' "AppleCPM"=>:cpm, } +VOLUME_CREATION_PARAMATERS={ + :apple2=>["0.chr*request.track_count*request.sector_length*16",RawDisk,A2DskPhysicalOrder,256], + :c64=>["0.chr*((17*21)+(7*19)+(6*18)+((request.track_count-30)*17))*256",RawDisk,D64,256], +} class TNDPServer LISTENING_PORT=6502 @@ -78,7 +82,21 @@ class TNDPServer response=TNDP::SectorReadResponseMessage.new({:track_no=>track_no,:sector_no=>sector_no,:sector_length=>sector_length,:volume_name=>request.volume_name,:sector_data=>sector_data}) end end - end + end + when TNDP::CreateVolumeRequestMessage::OPCODE + volume_file="#{@root_directory}/#{request.volume_name}" + volume_creation_paramaters=VOLUME_CREATION_PARAMATERS[request.system_architecture] + if volume_creation_paramaters.nil? then + response=TNDP::ErrorResponseMessage.create_error_response(data,TNDP::ErrorCodes::ARCHITECTURE_NOT_SUPPORTED,"create volume requests for #{request.system_architecture} not supported") + elsif request.sector_length!=volume_creation_paramaters[3] then + response=TNDP::ErrorResponseMessage.create_error_response(data,TNDP::ErrorCodes::INVALID_SECTOR_LENGTH,"create volume requests for #{request.system_architecture} should have sector length #{volume_creation_paramaters[3]}") + + else + file_bytes=eval(volume_creation_paramaters[0],binding) + volume=FileSystemImage.new(file_bytes,volume_creation_paramaters[1],volume_creation_paramaters[2],volume_file) + volume.save_as(volume_file) + response=TNDP::CreateVolumeResponseMessage.new({:volume_name=>request.volume_name,:system_architecture=>request.system_architecture,:track_count=>request.track_count,:sector_length=>request.sector_length} ) + end else response=TNDP::ErrorResponseMessage.create_error_response(data,TNDP::ErrorCodes::UNKNOWN_OPCODE,"unknown opcode $#{"%02X" % request.opcode}") end diff --git a/server/test/tc_test_server.rb b/server/test/tc_test_server.rb index 8299c00..ce5230b 100644 --- a/server/test/tc_test_server.rb +++ b/server/test/tc_test_server.rb @@ -36,6 +36,29 @@ class TestServer 0,"should be at least 1 apple2 image") assert(capabilities_response_msg.supported_architectures[:c64]>0,"should be at least 1 C64 image") + [ + [:apple2,'SCRATCH_TEST.DSK',35,256], + [:apple2,'SCRATCH_TEST2.DO',40,256], + [:c64,'SCRATCH_TEST.D64',35,256], + [:c64,'SCRATCH_TEST2.D64',40,256], + ].each do |a| + #try to make a new blank disk + system_architecture=a[0] + image_name=a[1] + track_count=a[2] + sector_length=a[3] + image_full_path="#{TEST_IMAGES_DIR}\\#{image_name}" + File.delete(image_full_path) if File.exist?(image_full_path) + create_volume_request_msg=TNDP::CreateVolumeRequestMessage.new({:volume_name=>image_name,:system_architecture=>system_architecture,:track_count=>track_count,:sector_length=>sector_length}) + create_volume_response_msg=send_request_and_get_response(create_volume_request_msg) + assert_equal(TNDP::CreateVolumeResponseMessage::OPCODE,create_volume_response_msg.opcode,"init volume request message should have correct opcode") + assert_equal(system_architecture,create_volume_response_msg.system_architecture) + assert(File.exist?(image_full_path),"file just created should exist at #{image_full_path}") + file_system_image=RipXplore.best_fit_from_filename(image_full_path) + assert_equal(track_count,file_system_image.track_count,"file just created should have correct number of tracks") + end + raise "done" + #test every combination of host and file system [[:apple2,:apple_dos_33],[:apple2,:prodos],[:apple2,:any],[:c64,:cbm_dos],[:any,:any],[:any,:prodos]].each do |a| desired_system_architecture=a[0] desired_file_system=a[1]