3 #include "core/misc.hpp"
4 #include "core/movie.hpp"
5 #include "core/rom.hpp"
12 #define FLAG_SYNC CONTROL_FRAME_SYNC
14 //std::ofstream debuglog("movie-debugging-log", std::ios::out | std::ios::app);
18 void hash_string(uint8_t* res
, const std::string
& s
) throw(std::bad_alloc
)
22 std::copy(s
.begin(), s
.end(), t
.begin());
26 uint8_t* enlarge(std::vector
<uint8_t>& v
, size_t amount
) throw(std::bad_alloc
)
33 inline void write64(uint8_t* buffer
, uint64_t value
) throw()
35 buffer
[0] = value
>> 56;
36 buffer
[1] = value
>> 48;
37 buffer
[2] = value
>> 40;
38 buffer
[3] = value
>> 32;
39 buffer
[4] = value
>> 24;
40 buffer
[5] = value
>> 16;
41 buffer
[6] = value
>> 8;
45 inline void write32(uint8_t* buffer
, uint32_t value
) throw()
47 buffer
[0] = value
>> 24;
48 buffer
[1] = value
>> 16;
49 buffer
[2] = value
>> 8;
53 inline uint32_t read32(const uint8_t* buffer
) throw()
55 return (static_cast<uint32_t>(buffer
[0]) << 24) |
56 (static_cast<uint32_t>(buffer
[1]) << 16) |
57 (static_cast<uint32_t>(buffer
[2]) << 8) |
58 (static_cast<uint32_t>(buffer
[3]));
61 inline uint64_t read64(const uint8_t* buffer
) throw()
63 return (static_cast<uint64_t>(buffer
[0]) << 56) |
64 (static_cast<uint64_t>(buffer
[1]) << 48) |
65 (static_cast<uint64_t>(buffer
[2]) << 40) |
66 (static_cast<uint64_t>(buffer
[3]) << 32) |
67 (static_cast<uint64_t>(buffer
[4]) << 24) |
68 (static_cast<uint64_t>(buffer
[5]) << 16) |
69 (static_cast<uint64_t>(buffer
[6]) << 8) |
70 (static_cast<uint64_t>(buffer
[7]));
73 inline void write16s(uint8_t* buffer
, int16_t x
) throw()
75 uint16_t y
= static_cast<uint16_t>(x
);
80 void hash_subframe(sha256
& ctx
, const controls_t
& ctrl
) throw()
82 uint8_t buf
[2 * TOTAL_CONTROLS
];
83 for(unsigned i
= 0; i
< TOTAL_CONTROLS
; i
++)
84 write16s(buf
+ 2 * i
, ctrl(i
));
85 ctx
.write(buf
, 2 * TOTAL_CONTROLS
);
88 //Hashes frame and returns starting subframe of next frame.
89 uint64_t hash_frame(sha256
& ctx
, std::vector
<controls_t
>& input
, uint64_t first_subframe
,
90 uint32_t bound
) throw()
93 //Ignore this frame completely.
94 if(first_subframe
>= input
.size())
95 return first_subframe
;
97 while(first_subframe
< input
.size() && !input
[first_subframe
](CONTROL_FRAME_SYNC
))
99 return first_subframe
;
101 if(first_subframe
>= input
.size()) {
102 //Hash an empty frame.
103 hash_subframe(ctx
, controls_t(true));
104 return first_subframe
;
107 uint64_t subframes_to_hash
= 1;
108 uint64_t last_differing
= 1;
110 controls_t prev
= input
[first_subframe
];
111 prev(CONTROL_FRAME_SYNC
) = 0;
113 while(first_subframe
+ subframes_to_hash
< input
.size() && !input
[first_subframe
+ subframes_to_hash
]
114 (CONTROL_FRAME_SYNC
)) {
115 if(!(input
[first_subframe
+ subframes_to_hash
] == prev
))
116 last_differing
= subframes_to_hash
+ 1;
117 prev
= input
[first_subframe
+ subframes_to_hash
];
120 next
= first_subframe
+ subframes_to_hash
;
121 subframes_to_hash
= last_differing
;
122 for(uint64_t i
= 0; i
< subframes_to_hash
&& i
< bound
; i
++)
123 hash_subframe(ctx
, input
[first_subframe
+ i
]);
127 void hash_movie(uint8_t* res
, uint64_t current_frame
, uint32_t* pollcounters
,
128 std::vector
<controls_t
>& input
) throw(std::bad_alloc
)
131 //If current_frame == 0, hash is empty.
137 uint64_t current_subframe
= 0;
138 for(uint64_t i
= 1; i
< current_frame
; i
++)
139 current_subframe
= hash_frame(ctx
, input
, current_subframe
, 0x7FFFFFFF);
140 //Current frame is special.
141 for(size_t i
= 0; i
< TOTAL_CONTROLS
; i
++) {
142 uint32_t polls
= pollcounters
[i
] & 0x7FFFFFFF;
143 uint32_t last_seen
= 0;
144 for(size_t j
= 0; j
< polls
; j
++) {
145 if(current_subframe
+ j
< input
.size() && !input
[current_subframe
+ j
]
146 (CONTROL_FRAME_SYNC
))
147 last_seen
= input
[current_subframe
+ j
](i
);
149 write16s(buf
, last_seen
);
157 void movie::set_all_DRDY() throw()
159 for(size_t i
= 0; i
< TOTAL_CONTROLS
; i
++)
160 pollcounters
[i
] |= 0x80000000UL
;
163 std::string
movie::rerecord_count() throw(std::bad_alloc
)
168 void movie::rerecord_count(const std::string
& count
) throw(std::bad_alloc
)
173 std::string
movie::project_id() throw(std::bad_alloc
)
178 void movie::project_id(const std::string
& id
) throw(std::bad_alloc
)
183 bool movie::readonly_mode() throw()
188 short movie::next_input(unsigned port
, unsigned controller
, unsigned index
) throw(std::bad_alloc
, std::logic_error
)
190 return next_input(ccindex2(port
, controller
, index
));
193 void movie::set_controls(controls_t controls
) throw()
195 current_controls
= controls
;
198 uint32_t movie::count_changes(uint64_t first_subframe
) throw()
200 if(first_subframe
>= movie_data
.size())
203 while(first_subframe
+ ret
< movie_data
.size() && !movie_data
[first_subframe
+ ret
](CONTROL_FRAME_SYNC
))
208 controls_t
movie::get_controls() throw()
211 return current_controls
;
213 //Before the beginning? Somebody screwed up (but return released / neutral anyway)...
214 if(current_frame
== 0)
216 //Otherwise find the last valid frame of input.
217 uint32_t changes
= count_changes(current_frame_first_subframe
);
219 return c
; //End of movie.
220 for(size_t i
= 0; i
< TOTAL_CONTROLS
; i
++) {
221 uint32_t polls
= pollcounters
[i
] & 0x7FFFFFFF;
222 uint32_t index
= (changes
> polls
) ? polls
: changes
- 1;
223 c(i
) = movie_data
[current_frame_first_subframe
+ index
](i
);
228 uint64_t movie::get_current_frame() throw()
230 return current_frame
;
233 uint64_t movie::get_lag_frames() throw()
238 uint64_t movie::get_frame_count() throw()
240 return frames_in_movie
;
243 void movie::next_frame() throw(std::bad_alloc
)
245 //If all poll counters are zero for all real controls, this frame is lag.
246 bool this_frame_lag
= true;
247 for(size_t i
= MAX_SYSTEM_CONTROLS
; i
< TOTAL_CONTROLS
; i
++)
248 if(pollcounters
[i
] & 0x7FFFFFFF)
249 this_frame_lag
= false;
250 //Hack: Reset frames must not be considered lagged, so we abuse pollcounter bit for reset to mark those.
251 if(pollcounters
[CONTROL_SYSTEM_RESET
] & 0x7FFFFFFF)
252 this_frame_lag
= false;
253 //Oh, frame 0 must not be considered lag.
254 if(current_frame
&& this_frame_lag
) {
256 //debuglog << "Frame " << current_frame << " is lag" << std::endl << std::flush;
258 //If in read-write mode, write a dummy record for the frame. Force sync flag.
259 //As index should be movie_data.size(), it is correct afterwards.
260 controls_t c
= current_controls
;
261 c(CONTROL_FRAME_SYNC
) = 1;
262 movie_data
.push_back(c
);
267 //Reset the poll counters and DRDY flags.
268 for(unsigned i
= 0; i
< TOTAL_CONTROLS
; i
++)
271 //Increment the current frame counter and subframe counter. Note that first subframe is undefined for
272 //frame 0 and 0 for frame 1.
274 current_frame_first_subframe
= current_frame_first_subframe
+
275 count_changes(current_frame_first_subframe
);
277 current_frame_first_subframe
= 0;
281 bool movie::get_DRDY(unsigned controlindex
) throw(std::logic_error
)
283 if(controlindex
>= TOTAL_CONTROLS
)
284 throw std::logic_error("movie::get_DRDY: Bad index");
285 return ((pollcounters
[controlindex
] & 0x80000000UL
) != 0);
288 bool movie::get_DRDY(unsigned port
, unsigned controller
, unsigned index
) throw(std::logic_error
)
290 return get_DRDY(ccindex2(port
, controller
, index
));
293 short movie::next_input(unsigned controlindex
) throw(std::bad_alloc
, std::logic_error
)
295 //Check validity of index.
296 if(controlindex
== FLAG_SYNC
)
298 if(controlindex
>= TOTAL_CONTROLS
)
299 throw std::logic_error("movie::next_input: Invalid control index");
301 //Clear the DRDY flag.
302 pollcounters
[controlindex
] &= 0x7FFFFFFF;
305 //In readonly mode...
306 //If at the end of the movie, return released / neutral (but also record the poll)...
307 if(current_frame_first_subframe
>= movie_data
.size()) {
308 pollcounters
[controlindex
]++;
311 //Before the beginning? Somebody screwed up (but return released / neutral anyway)...
312 if(current_frame
== 0)
314 //Otherwise find the last valid frame of input.
315 uint32_t changes
= count_changes(current_frame_first_subframe
);
316 uint32_t polls
= (pollcounters
[controlindex
]++) & 0x7FFFFFFF;
317 uint32_t index
= (changes
> polls
) ? polls
: changes
- 1;
318 //debuglog << "Frame=" << current_frame << " Subframe=" << polls << " control=" << controlindex << " value=" << movie_data[current_frame_first_subframe + index](controlindex) << " fetchrow=" << current_frame_first_subframe + index << std::endl << std::flush;
319 return movie_data
[current_frame_first_subframe
+ index
](controlindex
);
322 //Before the beginning? Somebody screwed up (but return released / neutral anyway)...
323 //Also, frame 0 must not be added to movie file.
324 if(current_frame
== 0)
326 //If at movie end, insert complete input with frame sync set (this is the first subframe).
327 if(current_frame_first_subframe
>= movie_data
.size()) {
328 controls_t c
= current_controls
;
329 c(CONTROL_FRAME_SYNC
) = 1;
330 movie_data
.push_back(c
);
331 //current_frame_first_subframe should be movie_data.size(), so it is right.
332 pollcounters
[controlindex
]++;
334 assert(pollcounters
[controlindex
] == 1);
335 //debuglog << "Frame=" << current_frame << " Subframe=" << (pollcounters[controlindex] - 1) << " control=" << controlindex << " value=" << movie_data[current_frame_first_subframe](controlindex) << " fetchrow=" << current_frame_first_subframe << std::endl << std::flush;
336 return movie_data
[current_frame_first_subframe
](controlindex
);
338 short new_value
= current_controls(controlindex
);
339 //Fortunately, we know this frame is the last one in movie_data.
340 uint32_t pollcounter
= pollcounters
[controlindex
] & 0x7FFFFFFF;
341 uint64_t fetchrow
= movie_data
.size() - 1;
342 if(current_frame_first_subframe
+ pollcounter
< movie_data
.size()) {
343 //The index is within existing size. Change the value and propagate to all subsequent
345 for(uint64_t i
= current_frame_first_subframe
+ pollcounter
; i
< movie_data
.size(); i
++)
346 movie_data
[i
](controlindex
) = new_value
;
347 fetchrow
= current_frame_first_subframe
+ pollcounter
;
348 } else if(new_value
!= movie_data
[movie_data
.size() - 1](controlindex
)) {
349 //The index is not within existing size and value does not match. We need to create a new
350 //subframes(s), copying the last subframe.
351 while(current_frame_first_subframe
+ pollcounter
>= movie_data
.size()) {
352 controls_t c
= movie_data
[movie_data
.size() - 1];
353 c(CONTROL_FRAME_SYNC
) = 0;
354 movie_data
.push_back(c
);
356 fetchrow
= current_frame_first_subframe
+ pollcounter
;
357 movie_data
[current_frame_first_subframe
+ pollcounter
](controlindex
) = new_value
;
359 pollcounters
[controlindex
]++;
360 //debuglog << "Frame=" << current_frame << " Subframe=" << (pollcounters[controlindex] - 1) << " control=" << controlindex << " value=" << new_value << " fetchrow=" << fetchrow << std::endl << std::flush;
365 movie::movie() throw(std::bad_alloc
)
372 current_frame_first_subframe
= 0;
373 for(unsigned i
= 0; i
< TOTAL_CONTROLS
; i
++) {
375 current_controls(i
) = 0;
381 void movie::load(const std::string
& rerecs
, const std::string
& project_id
, const std::vector
<controls_t
>& input
)
382 throw(std::bad_alloc
, std::runtime_error
)
384 if(input
.size() > 0 && !input
[0](CONTROL_FRAME_SYNC
))
385 throw std::runtime_error("First subframe MUST have frame sync flag set");
389 if(i(CONTROL_FRAME_SYNC
))
393 _project_id
= project_id
;
395 current_frame_first_subframe
= 0;
396 for(unsigned i
= 0; i
< TOTAL_CONTROLS
; i
++) {
403 std::vector
<controls_t
> movie::save() throw(std::bad_alloc
)
408 void movie::commit_reset(long delay
) throw(std::bad_alloc
)
410 if(readonly
|| delay
< 0)
412 //If this frame is lagged, we need to write entry for it.
413 bool this_frame_lag
= true;
414 for(size_t i
= MAX_SYSTEM_CONTROLS
; i
< TOTAL_CONTROLS
; i
++)
415 if(pollcounters
[i
] & 0x7FFFFFFF)
416 this_frame_lag
= false;
417 //Hack: Reset frames must not be considered lagged, so we abuse pollcounter bit for reset to mark those.
418 if(pollcounters
[CONTROL_SYSTEM_RESET
] & 0x7FFFFFFF)
419 this_frame_lag
= false;
421 controls_t c
= current_controls
;
422 c(CONTROL_FRAME_SYNC
) = 1;
423 movie_data
.push_back(c
);
425 //Current_frame_first_subframe is correct.
427 //Also set poll counters on reset cycles to avoid special cases elsewhere.
428 pollcounters
[CONTROL_SYSTEM_RESET
] = 1;
429 pollcounters
[CONTROL_SYSTEM_RESET_CYCLES_HI
] = 1;
430 pollcounters
[CONTROL_SYSTEM_RESET_CYCLES_LO
] = 1;
431 //Current frame is always last in rw mode.
432 movie_data
[current_frame_first_subframe
](CONTROL_SYSTEM_RESET
) = 1;
433 movie_data
[current_frame_first_subframe
](CONTROL_SYSTEM_RESET_CYCLES_HI
) = delay
/ 10000;
434 movie_data
[current_frame_first_subframe
](CONTROL_SYSTEM_RESET_CYCLES_LO
) = delay
% 10000;
437 unsigned movie::next_poll_number()
440 for(unsigned i
= 0; i
< TOTAL_CONTROLS
; i
++)
441 if(max
< (pollcounters
[i
] & 0x7FFFFFFF))
442 max
= (pollcounters
[i
] & 0x7FFFFFFF);
446 void movie::readonly_mode(bool enable
) throw(std::bad_alloc
)
448 bool was_in_readonly
= readonly
;
450 if(was_in_readonly
&& !readonly
) {
452 //Transitioning to readwrite mode, we have to adjust the length of the movie data.
453 if(current_frame
== 0) {
454 //WTF... At before first frame. Blank the entiere movie.
459 //Fun special case: Current frame is not in movie (current_frame_first_subframe >= movie_data.size()).
460 //In this case, we have to extend the movie data.
461 if(current_frame_first_subframe
>= movie_data
.size()) {
462 //Yes, this will insert one extra frame... But we will lose it later if it is not needed.
463 while(frames_in_movie
< current_frame
) {
465 movie_data
.push_back(c
);
468 current_frame_first_subframe
= movie_data
.size() - 1;
471 //We have to take the part up to furthest currently readable subframe. Also, we need to propagate
472 //forward values with smaller poll counters.
473 uint64_t next_frame_first_subframe
= current_frame_first_subframe
+
474 count_changes(current_frame_first_subframe
);
475 uint64_t max_readable_subframes
= current_frame_first_subframe
;
476 for(size_t i
= 0; i
< TOTAL_CONTROLS
; i
++) {
477 uint32_t polls
= pollcounters
[i
] & 0x7FFFFFFF;
478 if(current_frame_first_subframe
+ polls
>= next_frame_first_subframe
)
479 max_readable_subframes
= next_frame_first_subframe
;
480 else if(current_frame_first_subframe
+ polls
> max_readable_subframes
)
481 max_readable_subframes
= current_frame_first_subframe
+ polls
;
483 movie_data
.resize(max_readable_subframes
);
484 next_frame_first_subframe
= max_readable_subframes
;
485 for(size_t i
= 1; i
< TOTAL_CONTROLS
; i
++) {
486 uint32_t polls
= pollcounters
[i
] & 0x7FFFFFFF;
489 for(uint64_t j
= current_frame_first_subframe
+ polls
; j
< next_frame_first_subframe
; j
++)
490 movie_data
[j
](i
) = movie_data
[current_frame_first_subframe
+ polls
- 1](i
);
492 frames_in_movie
= current_frame
- ((current_frame_first_subframe
>= movie_data
.size()) ? 1 : 0);
496 //Save state of movie code.
497 std::vector
<uint8_t> movie::save_state() throw(std::bad_alloc
)
499 //debuglog << "--------------------------------------------" << std::endl;
500 //debuglog << "SAVING STATE:" << std::endl;
501 std::vector
<uint8_t> ret
;
502 hash_string(enlarge(ret
, 32), _project_id
);
503 write64(enlarge(ret
, 8), current_frame
);
504 //debuglog << "Current frame is " << current_frame << std::endl;
505 //debuglog << "Poll counters: ";
506 for(unsigned i
= 0; i
< TOTAL_CONTROLS
; i
++) {
507 uint32_t v
= pollcounters
[i
];
509 if(v
& 0x80000000UL
) {
513 write32(enlarge(ret
, 4), v
);
515 //debuglog << std::endl;
517 uint64_t v
= lag_frames
;
518 //debuglog << "Lag frame count: " << lag_frames << std::endl;
519 write64(enlarge(ret
, 8), v
);
521 hash_movie(enlarge(ret
, 32), current_frame
, pollcounters
, movie_data
);
523 sha256::hash(hash
, ret
);
524 memcpy(enlarge(ret
, 32), hash
, 32);
525 //debuglog << "--------------------------------------------" << std::endl;
530 //Restore state of movie code. Throws if state is invalid. Flag gives new state of readonly flag.
531 size_t movie::restore_state(const std::vector
<uint8_t>& state
, bool ro
) throw(std::bad_alloc
, std::runtime_error
)
533 //Check the whole-data checksum.
536 if(state
.size() != 112+4*TOTAL_CONTROLS
)
537 throw std::runtime_error("Movie save data corrupt: Wrong length");
538 sha256::hash(tmp
, &state
[0], state
.size() - 32);
539 if(memcmp(tmp
, &state
[state
.size() - 32], 32))
540 throw std::runtime_error("Movie save data corrupt: Checksum does not match");
541 //debuglog << "--------------------------------------------" << std::endl;
542 //debuglog << "RESTORING STATE:" << std::endl;
544 hash_string(tmp
, _project_id
);
545 if(memcmp(tmp
, &state
[ptr
], 32))
546 throw std::runtime_error("Save is not from this movie");
548 //Read current frame.
549 uint64_t tmp_curframe
= read64(&state
[ptr
]);
550 uint64_t tmp_firstsubframe
= 0;
551 for(uint64_t i
= 1; i
< tmp_curframe
; i
++)
552 tmp_firstsubframe
= tmp_firstsubframe
+ count_changes(tmp_firstsubframe
);
554 //Read poll counters and drdy flags.
555 uint32_t tmp_pollcount
[TOTAL_CONTROLS
];
556 for(unsigned i
= 0; i
< TOTAL_CONTROLS
; i
++) {
557 uint32_t v
= read32(&state
[ptr
]);
559 tmp_pollcount
[i
] = v
;
561 uint64_t tmp_lagframes
= read64(&state
[ptr
]);
562 tmp_lagframes
&= 0x7FFFFFFFFFFFFFFFULL
;
564 hash_movie(tmp
, tmp_curframe
, tmp_pollcount
, movie_data
);
565 if(memcmp(tmp
, &state
[ptr
], 32))
566 throw std::runtime_error("Save is not from this movie");
568 //Ok, all checks pass. Copy the state. Do this in readonly mode so we can use normal routine to switch
571 current_frame
= tmp_curframe
;
572 current_frame_first_subframe
= tmp_firstsubframe
;
573 memcpy(pollcounters
, tmp_pollcount
, sizeof(tmp_pollcount
));
574 lag_frames
= tmp_lagframes
;
576 //debuglog << "Current frame is " << current_frame << std::endl;
577 //debuglog << "Poll counters: ";
578 for(unsigned i
= 0; i
< TOTAL_CONTROLS
; i
++) {
579 uint32_t v
= pollcounters
[i
];
581 if(v
& 0x80000000UL
) {
586 //debuglog << std::endl;
588 //debuglog << "Lag frame count: " << lag_frames << std::endl;
591 //debuglog << "--------------------------------------------" << std::endl;
594 //Move to readwrite mode if needed.
599 long movie::get_reset_status() throw()
601 if(current_frame
== 0 || current_frame_first_subframe
>= movie_data
.size())
602 return -1; //No resets out of range.
603 if(!movie_data
[current_frame_first_subframe
](CONTROL_SYSTEM_RESET
))
604 return -1; //Not a reset.
605 //Also set poll counters on reset cycles to avoid special cases elsewhere.
606 pollcounters
[CONTROL_SYSTEM_RESET
] = 1;
607 pollcounters
[CONTROL_SYSTEM_RESET_CYCLES_HI
] = 1;
608 pollcounters
[CONTROL_SYSTEM_RESET_CYCLES_LO
] = 1;
609 long hi
= movie_data
[current_frame_first_subframe
](CONTROL_SYSTEM_RESET_CYCLES_HI
);
610 long lo
= movie_data
[current_frame_first_subframe
](CONTROL_SYSTEM_RESET_CYCLES_LO
);
611 return hi
* 10000 + lo
;
614 uint64_t movie::frame_subframes(uint64_t frame
) throw()
616 if(frame
< cached_frame
)
618 uint64_t p
= cached_subframe
;
619 for(uint64_t i
= cached_frame
; i
< frame
; i
++)
620 p
= p
+ count_changes(p
);
621 cached_frame
= frame
;
623 return count_changes(p
);
626 void movie::clear_caches() throw()
632 controls_t
movie::read_subframe(uint64_t frame
, uint64_t subframe
) throw()
634 if(frame
< cached_frame
)
636 uint64_t p
= cached_subframe
;
637 for(uint64_t i
= cached_frame
; i
< frame
; i
++)
638 p
= p
+ count_changes(p
);
639 cached_frame
= frame
;
641 uint64_t max
= count_changes(p
);
643 return controls_t(true);
646 return movie_data
[p
+ subframe
];
650 movie_logic::movie_logic() throw()
654 movie
& movie_logic::get_movie() throw()
659 long movie_logic::new_frame_starting(bool dont_poll
) throw(std::bad_alloc
, std::runtime_error
)
662 controls_t c
= update_controls(false);
663 if(!mov
.readonly_mode()) {
667 if(c(CONTROL_SYSTEM_RESET
)) {
668 long hi
= c(CONTROL_SYSTEM_RESET_CYCLES_HI
);
669 long lo
= c(CONTROL_SYSTEM_RESET_CYCLES_LO
);
670 mov
.commit_reset(hi
* 10000 + lo
);
673 return mov
.get_reset_status();
676 short movie_logic::input_poll(bool port
, unsigned dev
, unsigned id
) throw(std::bad_alloc
, std::runtime_error
)
678 if(dev
>= MAX_CONTROLLERS_PER_PORT
|| id
>= CONTROLLER_CONTROLS
)
680 if(!mov
.get_DRDY(port
? 1 : 0, dev
, id
)) {
681 mov
.set_controls(update_controls(true));
684 int16_t in
= mov
.next_input(port
? 1 : 0, dev
, id
);
685 //debuglog << "BSNES asking for (" << port << "," << dev << "," << id << ") (frame " << mov.get_current_frame()
686 // << ") giving " << in << std::endl;