Skip to content

Commit f7efec1

Browse files
committed
[cpu] Add relevant details to event messages (mainly for debugging).
1 parent 4a7420a commit f7efec1

File tree

6 files changed

+110
-47
lines changed

6 files changed

+110
-47
lines changed

cpu/src/control.rs

Lines changed: 69 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -246,46 +246,65 @@ fn test_sequence_flags_current_flag_state() {
246246
assert_eq!(flags.drain_flag_changes(), vec![u6!(0o52)]);
247247
}
248248

249+
/// ControlRegisterDiagnostics is only for generating debug
250+
/// information. They must not be used for control/execution
251+
/// purposes.
252+
#[derive(Debug)]
253+
pub struct CurrentInstructionDiagnostics {
254+
pub current_instruction: Instruction,
255+
pub instruction_address: Address,
256+
}
257+
249258
#[derive(Debug)]
250259
pub struct ControlRegisters {
260+
pub diagnostic_only: CurrentInstructionDiagnostics,
261+
251262
// Arithmetic Element registers (A-E) are actually in V-memory.
263+
/// Contents of the simulated N register.
252264
pub n: Instruction,
253265
pub n_sym: Option<SymbolicInstruction>,
266+
267+
/// The P register is the program counter for the current sequence.
254268
pub p: Address,
269+
270+
/// Contents of the simulaterd Q register.
255271
pub q: Address,
256272

257-
// The k register (User guide 4-3.1) holds the current sequence
258-
// number (User guide 5-24). k is Option<SequenceNumber> in order
259-
// to allow the (emulated) control unit to recognise a CODABO
260-
// button as indicating a need to change sequence from the control
261-
// unit's initial state to sequence 0.
262-
//
263-
// This likely doesn't reflect the actual operation of the TX-2
264-
// very well, and better understanding of the real operation of
265-
// the machine will likely change this.
266-
//
267-
// I think that perhaps section 12-2.6.2 of Volume 2 of the
268-
// technical manual may explain how the real TX-2 avoided this
269-
// problem, but I don't think I understand what that section says.
270-
// The text is:
271-
//
272-
// """12-2.6.2 XPS FLIP-FLOP LOGIC. This flip-floop inhibits the
273-
// X Memory strobe pulse into X when the register selected has the
274-
// same address or the current program counter, is not register 0,
275-
// and this is the first reference to this register since the last
276-
// sequence change. In this case all the cores of the register
277-
// are clearered and only "junk" (with a 50-50 chance of a bad
278-
// parity) would be strobed into X. If XPS¹, then a clear pulse
279-
// is substituted for the strobe pulse.
280-
//
281-
// The flip-flop is set whenever a sequence change occurs, and is
282-
// cleared the first time thereafter that the program counter
283-
// register is referenced during a PK cycle (if ever). See Fig
284-
// 12-8."""
273+
/// The k register (User guide 4-3.1) holds the current sequence
274+
/// number (User guide 5-24). k is Option<SequenceNumber> in order
275+
/// to allow the (emulated) control unit to recognise a CODABO
276+
/// button as indicating a need to change sequence from the control
277+
/// unit's initial state to sequence 0.
278+
///
279+
/// This likely doesn't reflect the actual operation of the TX-2
280+
/// very well, and better understanding of the real operation of
281+
/// the machine will likely change this.
282+
///
283+
/// I think that perhaps section 12-2.6.2 of Volume 2 of the
284+
/// technical manual may explain how the real TX-2 avoided this
285+
/// problem, but I don't think I understand what that section says.
286+
/// The text is:
287+
///
288+
/// """12-2.6.2 XPS FLIP-FLOP LOGIC. This flip-floop inhibits the
289+
/// X Memory strobe pulse into X when the register selected has the
290+
/// same address or the current program counter, is not register 0,
291+
/// and this is the first reference to this register since the last
292+
/// sequence change. In this case all the cores of the register
293+
/// are clearered and only "junk" (with a 50-50 chance of a bad
294+
/// parity) would be strobed into X. If XPS¹, then a clear pulse
295+
/// is substituted for the strobe pulse.
296+
///
297+
/// The flip-flop is set whenever a sequence change occurs, and is
298+
/// cleared the first time thereafter that the program counter
299+
/// register is referenced during a PK cycle (if ever). See Fig
300+
/// 12-8."""
285301
pub k: Option<SequenceNumber>,
286302

287-
spr: Address, // Start Point Register
303+
/// Start Point Register
304+
spr: Address,
288305

306+
/// Index registers.
307+
///
289308
/// Index register 0 is the Toggle Start point.
290309
/// Index registers 40-77 are program counters for the sequences.
291310
///
@@ -294,14 +313,21 @@ pub struct ControlRegisters {
294313
/// 3-68 of the User Handbook (section 3-3.1) as being signed
295314
/// integers.
296315
pub index_regs: [Signed18Bit; 0o100], // AKA the X memory
297-
f_memory: [SystemConfiguration; 32], // the F memory
316+
317+
/// the F memory
318+
f_memory: [SystemConfiguration; 32],
319+
320+
/// The flags; these indicate which sequences are runnable.
298321
flags: SequenceFlags,
299322
current_sequence_is_runnable: bool,
323+
324+
/// prev_hold is set when the instruction we most previously
325+
/// executed had the "hold" bit set.
326+
prev_hold: bool,
300327
// TODO: we may be able to eliminate prev_hold by moving the logic
301328
// that's currently at the beginning of fetch_instruction() so
302329
// that it occurs at the end of execute_instruction() instead.
303330
// See the comment at the top of fetch_instruction().
304-
prev_hold: bool,
305331
}
306332

307333
impl ControlRegisters {
@@ -317,6 +343,10 @@ impl ControlRegisters {
317343
};
318344

319345
let mut result = ControlRegisters {
346+
diagnostic_only: CurrentInstructionDiagnostics {
347+
current_instruction: Instruction::invalid(),
348+
instruction_address: Address::from(u18!(0o777_777)),
349+
},
320350
n: Instruction::invalid(), // not a valid instruction
321351
n_sym: None,
322352
p: Address::default(),
@@ -921,6 +951,7 @@ impl ControlUnit {
921951
// Calculate the address from which we will fetch the
922952
// instruction, and the increment the program counter.
923953
let p_physical_address = Address::from(self.regs.p.split().0);
954+
self.regs.diagnostic_only.instruction_address = p_physical_address;
924955
event!(
925956
Level::TRACE,
926957
"Fetching instruction from physical address {p_physical_address:>012o}"
@@ -931,7 +962,7 @@ impl ControlUnit {
931962
} else {
932963
MetaBitChange::None
933964
};
934-
let instruction_word = match mem.fetch(ctx, &p_physical_address, &meta_op) {
965+
let instruction_word: Unsigned36Bit = match mem.fetch(ctx, &p_physical_address, &meta_op) {
935966
Ok((inst, extra_bits)) => {
936967
if extra_bits.meta && self.trap.trap_on_marked_instruction() {
937968
self.raise_trap();
@@ -975,6 +1006,10 @@ impl ControlUnit {
9751006
p_physical_address
9761007
);
9771008
}
1009+
// Several things update the N register, so we update
1010+
// `current_instruction` directly here instead of inside
1011+
// update_n_register().
1012+
self.regs.diagnostic_only.current_instruction = Instruction::from(instruction_word);
9781013
self.update_n_register(instruction_word)?;
9791014
Ok(())
9801015
}
@@ -1632,8 +1667,8 @@ impl ControlUnit {
16321667
if let Some(current_seq) = self.regs.k {
16331668
event!(
16341669
Level::DEBUG,
1635-
"dismissing current sequence (reason: {})",
1636-
reason
1670+
"dismissing current sequence {current_seq:o} (reason: {reason}) while executing instruction from {:o}",
1671+
self.regs.diagnostic_only.instruction_address,
16371672
);
16381673
self.regs.flags.lower(&current_seq);
16391674
self.regs.current_sequence_is_runnable = false;

cpu/src/control/op_io.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -162,8 +162,12 @@ impl ControlUnit {
162162
}
163163
_ => devices.connect(ctx, regs.k, &unit, mode, alarm_unit)?,
164164
};
165-
if let Some(FlagChange::Raise) = maybe_flag_change {
166-
regs.flags.raise(&unit);
165+
match maybe_flag_change {
166+
Some(FlagChange::Raise(reason)) => {
167+
event!(Level::DEBUG, "unit {unit:o} raised its flag: {reason}");
168+
regs.flags.raise(&unit);
169+
}
170+
None => (),
167171
}
168172
Ok(())
169173
}

cpu/src/io.rs

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ fn make_unit_report_word(
191191
report = report | IO_MASK_AVAIL;
192192
}
193193
// A unit can raise but not lower its flag.
194-
if current_flag || status.change_flag == Some(FlagChange::Raise) {
194+
if current_flag || matches!(&status.change_flag, Some(FlagChange::Raise(_))) {
195195
report = report | IO_MASK_FLAG;
196196
}
197197
report | Unsigned36Bit::from(unit).shl(18) | Unsigned36Bit::from(status.special).shl(24)
@@ -456,7 +456,7 @@ impl DeviceManager {
456456
let (extended_unit_status, flag): (Option<ExtendedConnectedUnitStatus>, bool) =
457457
if unit.connected {
458458
let unit_status = unit.poll(ctx, alarmer)?;
459-
let flag: bool = matches!(unit_status.change_flag, Some(FlagChange::Raise));
459+
let flag: bool = matches!(unit_status.change_flag, Some(FlagChange::Raise(_)));
460460
let ext_status = ExtendedConnectedUnitStatus {
461461
buffer_available_to_cpu: unit_status.buffer_available_to_cpu,
462462
inability: unit_status.inability,
@@ -609,6 +609,7 @@ impl DeviceManager {
609609
let mut alarm: Option<Alarm> = None;
610610
let mut next_poll: Option<Duration> = None;
611611

612+
event!(Level::DEBUG, "poll_queue is: {:?}", &self.poll_queue);
612613
loop {
613614
match self.poll_queue.peek() {
614615
None => {
@@ -642,8 +643,8 @@ impl DeviceManager {
642643
self.mark_device_changed(devno);
643644

644645
event!(
645-
Level::TRACE,
646-
"poll: next poll is now due; due={:?}, now={:?}",
646+
Level::DEBUG,
647+
"poll: next poll is now due for unit {devno:o}; due={:?}, now={:?}",
647648
poll_time,
648649
system_time
649650
);
@@ -673,8 +674,11 @@ impl DeviceManager {
673674
let unit_status = attached.poll(ctx, alarm_unit)?;
674675
event!(Level::TRACE, "unit {devno:02o} status is {unit_status:?}");
675676
self.poll_queue.push(devno, unit_status.poll_after);
676-
if let Some(FlagChange::Raise) = unit_status.change_flag {
677-
event!(Level::DEBUG, "unit {devno:02o} has raised its flag");
677+
if let Some(FlagChange::Raise(reason)) = unit_status.change_flag {
678+
event!(
679+
Level::DEBUG,
680+
"unit {devno:02o} has raised its flag: {reason}"
681+
);
678682
raised_flags |= 1 << u8::from(devno);
679683
}
680684
if alarm.is_none() {
@@ -777,6 +781,10 @@ impl DeviceManager {
777781
match self.devices.get_mut(device) {
778782
Some(attached) => {
779783
if attached.in_maintenance {
784+
event!(
785+
Level::INFO,
786+
"attempt to connect in-maintenance unit {device:o}, raising IOSAL"
787+
);
780788
Err(Alarm {
781789
sequence: Some(*device),
782790
details: AlarmDetails::IOSAL {
@@ -786,8 +794,18 @@ impl DeviceManager {
786794
},
787795
})
788796
} else {
797+
event!(Level::DEBUG, "connecting unit {device:o}");
798+
// If the unit being connected is an OUTPUT unit,
799+
// and the unit was not already connected, then
800+
// its flag is raised (Users Handbook page 4-7).
789801
let flag_change = if attached.is_disconnected_output_unit() {
790-
Some(FlagChange::Raise)
802+
event!(
803+
Level::DEBUG,
804+
"Connecting previously-unconnected output unit {device:o}, so raising its flag"
805+
);
806+
Some(FlagChange::Raise(
807+
"attaching a previously-disconnected output unit",
808+
))
791809
} else {
792810
None
793811
};
@@ -797,6 +815,10 @@ impl DeviceManager {
797815
}
798816
}
799817
None => {
818+
event!(
819+
Level::WARN,
820+
"attempt to connect nonexistent unit {device:o}"
821+
);
800822
alarm_unit.fire_if_not_masked(Alarm {
801823
sequence: calling_sequence, // NOTE: not the same as *device.
802824
details: AlarmDetails::IOSAL {

cpu/src/io/dev_lincoln_writer.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,9 @@ impl Unit for LincolnWriterOutput {
9898
let change_flag = if !self.connected || transmitting {
9999
None
100100
} else {
101-
Some(FlagChange::Raise)
101+
Some(FlagChange::Raise(
102+
"(still or newly) connected and not already transmitting",
103+
))
102104
};
103105
event!(
104106
Level::TRACE,
@@ -414,7 +416,7 @@ impl Unit for LincolnWriterInput {
414416
"LW input {:o}: connected and data is ready; raising flag",
415417
self.unit
416418
);
417-
Some(FlagChange::Raise) // data is ready, raise flag
419+
Some(FlagChange::Raise("keyboard data is ready")) // data is ready, raise flag
418420
} else {
419421
event!(
420422
Level::TRACE,

cpu/src/io/dev_petr.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -336,7 +336,7 @@ impl Unit for Petr {
336336
UnitStatus {
337337
special: Unsigned12Bit::ZERO,
338338
change_flag: if data_ready {
339-
Some(FlagChange::Raise)
339+
Some(FlagChange::Raise("data is ready"))
340340
} else {
341341
None
342342
},

cpu/src/types.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use base::prelude::*;
66

77
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
88
pub enum FlagChange {
9-
Raise,
9+
Raise(&'static str),
1010
}
1111

1212
/// A value of which bits 0..width are significant (0 being the least significant bit).

0 commit comments

Comments
 (0)