diff --git a/Components/6845/CRTC6845.hpp b/Components/6845/CRTC6845.hpp index fd7008041..67d065a25 100644 --- a/Components/6845/CRTC6845.hpp +++ b/Components/6845/CRTC6845.hpp @@ -112,7 +112,7 @@ template class CRTC6845 { void run_for(Cycles cycles) { auto cyles_remaining = cycles.as_integral(); while(cyles_remaining--) { - // check for end of visible characters + // Check for end of visible characters. if(character_counter_ == registers_[1]) { // TODO: consider skew in character_is_visible_. Or maybe defer until perform_bus_cycle? character_is_visible_ = false; @@ -122,23 +122,26 @@ template class CRTC6845 { perform_bus_cycle_phase1(); bus_state_.refresh_address = (bus_state_.refresh_address + 1) & 0x3fff; - // check for end-of-line + bus_state_.cursor = is_cursor_line_ && + bus_state_.refresh_address == ((registers_[15] | (registers_[14] << 8))&0x3fff); + + // Check for end-of-line. if(character_counter_ == registers_[0]) { character_counter_ = 0; do_end_of_line(); character_is_visible_ = true; } else { - // increment counter + // Increment counter. character_counter_++; } - // check for start of horizontal sync + // Check for start of horizontal sync. if(character_counter_ == registers_[2]) { hsync_counter_ = 0; bus_state_.hsync = true; } - // check for end of horizontal sync; note that a sync time of zero will result in an immediate + // Check for end of horizontal sync; note that a sync time of zero will result in an immediate // cancellation of the plan to perform sync if this is an HD6845S or UM6845R; otherwise zero // will end up counting as 16 as it won't be checked until after overflow. if(bus_state_.hsync) { @@ -176,10 +179,14 @@ template class CRTC6845 { } inline void do_end_of_line() { - // check for end of vertical sync + // Check for cursor disable. + // TODO: this is handled differently on the EGA, should I ever implement that. + is_cursor_line_ &= bus_state_.row_address != (registers_[11] & 0x1f); + + // Check for end of vertical sync. if(bus_state_.vsync) { vsync_counter_ = (vsync_counter_ + 1) & 15; - // on the UM6845R and AMS40226, honour the programmed vertical sync time; on the other CRTCs + // On the UM6845R and AMS40226, honour the programmed vertical sync time; on the other CRTCs // always use a vertical sync count of 16. switch(personality_) { case HD6845S: @@ -199,12 +206,12 @@ template class CRTC6845 { do_end_of_frame(); } } else { - // advance vertical counter + // Advance vertical counter. if(bus_state_.row_address == registers_[9]) { bus_state_.row_address = 0; line_address_ = end_of_line_address_; - // check for entry into the overflow area + // Check for entry into the overflow area. if(line_counter_ == registers_[4]) { if(registers_[5]) { line_counter_ = 0; @@ -215,13 +222,13 @@ template class CRTC6845 { } else { line_counter_ = (line_counter_ + 1) & 0x7f; - // check for start of vertical sync + // Check for start of vertical sync. if(line_counter_ == registers_[7]) { bus_state_.vsync = true; vsync_counter_ = 0; } - // check for end of visible lines + // Check for end of visible lines. if(line_counter_ == registers_[6]) { line_is_visible_ = false; } @@ -234,6 +241,9 @@ template class CRTC6845 { bus_state_.refresh_address = line_address_; character_counter_ = 0; character_is_visible_ = (registers_[1] != 0); + + // Check for cursor enable. + is_cursor_line_ |= bus_state_.row_address == (registers_[10] & 0x1f); } inline void do_end_of_frame() { @@ -266,6 +276,8 @@ template class CRTC6845 { int display_skew_mask_ = 1; unsigned int character_is_visible_shifter_ = 0; + + bool is_cursor_line_ = false; }; } diff --git a/Machines/PCCompatible/KeyboardMapper.hpp b/Machines/PCCompatible/KeyboardMapper.hpp index f942d5229..d4765852e 100644 --- a/Machines/PCCompatible/KeyboardMapper.hpp +++ b/Machines/PCCompatible/KeyboardMapper.hpp @@ -84,18 +84,54 @@ class KeyboardMapper: public MachineTypes::MappedKeyboardMachine::KeyboardMapper case k::ForwardSlash: return 53; case k::RightShift: return 54; + case k::KeypadAsterisk: return 55; + case k::LeftOption: case k::RightOption: return 56; case k::Space: return 57; case k::CapsLock: return 58; + case k::F1: return 59; + case k::F2: return 60; + case k::F3: return 61; + case k::F4: return 62; + case k::F5: return 63; + case k::F6: return 64; + case k::F7: return 65; + case k::F8: return 66; + case k::F9: return 67; + case k::F10: return 68; + case k::NumLock: return 69; case k::ScrollLock: return 70; + case k::Keypad7: return 71; + case k::Up: + case k::Keypad8: return 72; + case k::Keypad9: return 73; + case k::KeypadMinus: return 74; + + case k::Left: + case k::Keypad4: return 75; + case k::Keypad5: return 76; + case k::Right: + case k::Keypad6: return 77; + case k::KeypadPlus: return 78; + + case k::Keypad1: return 79; + case k::Down: + case k::Keypad2: return 80; + case k::Keypad3: return 81; + + case k::Keypad0: return 82; + case k::KeypadDecimalPoint: return 83; + /* TODO: SysReq = 84 */ + + case k::F11: return 87; + case k::F12: return 88; + default: return MachineTypes::MappedKeyboardMachine::KeyNotMapped; } - - // TODO: extended functions, including all F keys and cursors. } }; diff --git a/Machines/PCCompatible/PCCompatible.cpp b/Machines/PCCompatible/PCCompatible.cpp index 0a71e7a5f..aa2e3c61e 100644 --- a/Machines/PCCompatible/PCCompatible.cpp +++ b/Machines/PCCompatible/PCCompatible.cpp @@ -410,15 +410,22 @@ class KeyboardController { } reset_delay_ -= cycles.as(); if(reset_delay_ <= 0) { + input_.clear(); post(0xaa); } } uint8_t read() { pic_.apply_edge<1>(false); + if(input_.empty()) { + return 0; + } - const uint8_t key = input_; - input_ = 0; + const uint8_t key = input_.front(); + input_.erase(input_.begin()); + if(!input_.empty()) { + pic_.apply_edge<1>(true); + } return key; } @@ -426,7 +433,7 @@ class KeyboardController { if(mode_ == Mode::NoIRQsIgnoreInput) { return; } - input_ = value; + input_.push_back(value); pic_.apply_edge<1>(true); } @@ -438,7 +445,7 @@ class KeyboardController { Reset = 0b00, } mode_; - uint8_t input_ = 0; + std::vector input_; PIC &pic_; int reset_delay_ = 0; @@ -542,41 +549,50 @@ class MDA { } } - // TODO: cursor. if(pixels) { - const uint8_t attributes = ram[((state.refresh_address << 1) + 1) & 0xfff]; - const uint8_t glyph = ram[((state.refresh_address << 1) + 0) & 0xfff]; - uint8_t row = font[(glyph * 14) + state.row_address]; + static constexpr uint8_t high_intensity = 0x0d; + static constexpr uint8_t low_intensity = 0x09; + static constexpr uint8_t off = 0x00; - const uint8_t intensity = (attributes & 0x08) ? 0x0d : 0x09; - uint8_t blank = 0; - - // Handle irregular attributes. - // Cf. http://www.seasip.info/VintagePC/mda.html#memmap - switch(attributes) { - case 0x00: case 0x08: case 0x80: case 0x88: - row = 0; - break; - case 0x70: case 0x78: case 0xf0: case 0xf8: - row ^= 0xff; - blank = intensity; - break; - } - - if(((attributes & 7) == 1) && state.row_address == 13) { - // Draw as underline. - std::fill(pixel_pointer, pixel_pointer + 9, intensity); + if(state.cursor) { + pixel_pointer[0] = pixel_pointer[1] = pixel_pointer[2] = pixel_pointer[3] = + pixel_pointer[4] = pixel_pointer[5] = pixel_pointer[6] = pixel_pointer[7] = + pixel_pointer[8] = low_intensity; } else { - // Draw according to ROM contents, possibly duplicating final column. - pixel_pointer[0] = (row & 0x80) ? intensity : 0; - pixel_pointer[1] = (row & 0x40) ? intensity : 0; - pixel_pointer[2] = (row & 0x20) ? intensity : 0; - pixel_pointer[3] = (row & 0x10) ? intensity : 0; - pixel_pointer[4] = (row & 0x08) ? intensity : 0; - pixel_pointer[5] = (row & 0x04) ? intensity : 0; - pixel_pointer[6] = (row & 0x02) ? intensity : 0; - pixel_pointer[7] = (row & 0x01) ? intensity : 0; - pixel_pointer[8] = (glyph >= 0xc0 && glyph < 0xe0) ? pixel_pointer[7] : blank; + const uint8_t attributes = ram[((state.refresh_address << 1) + 1) & 0xfff]; + const uint8_t glyph = ram[((state.refresh_address << 1) + 0) & 0xfff]; + uint8_t row = font[(glyph * 14) + state.row_address]; + + const uint8_t intensity = (attributes & 0x08) ? high_intensity : low_intensity; + uint8_t blank = off; + + // Handle irregular attributes. + // Cf. http://www.seasip.info/VintagePC/mda.html#memmap + switch(attributes) { + case 0x00: case 0x08: case 0x80: case 0x88: + row = 0; + break; + case 0x70: case 0x78: case 0xf0: case 0xf8: + row ^= 0xff; + blank = intensity; + break; + } + + if(((attributes & 7) == 1) && state.row_address == 13) { + // Draw as underline. + std::fill(pixel_pointer, pixel_pointer + 9, intensity); + } else { + // Draw according to ROM contents, possibly duplicating final column. + pixel_pointer[0] = (row & 0x80) ? intensity : off; + pixel_pointer[1] = (row & 0x40) ? intensity : off; + pixel_pointer[2] = (row & 0x20) ? intensity : off; + pixel_pointer[3] = (row & 0x10) ? intensity : off; + pixel_pointer[4] = (row & 0x08) ? intensity : off; + pixel_pointer[5] = (row & 0x04) ? intensity : off; + pixel_pointer[6] = (row & 0x02) ? intensity : off; + pixel_pointer[7] = (row & 0x01) ? intensity : off; + pixel_pointer[8] = (glyph >= 0xc0 && glyph < 0xe0) ? pixel_pointer[7] : blank; + } } pixel_pointer += 9; } diff --git a/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift b/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift index 5397eb4be..cefc57dac 100644 --- a/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift +++ b/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift @@ -359,12 +359,28 @@ class MachineDocument: /// Synthesies appropriate key up and key down events upon any change in modifiers. func flagsChanged(_ newModifiers: NSEvent) { if let machine = self.machine { - machine.setKey(VK_Shift, characters: nil, isPressed: newModifiers.modifierFlags.contains(.shift)) - machine.setKey(VK_Control, characters: nil, isPressed: newModifiers.modifierFlags.contains(.control)) - machine.setKey(VK_Command, characters: nil, isPressed: newModifiers.modifierFlags.contains(.command)) - machine.setKey(VK_Option, characters: nil, isPressed: newModifiers.modifierFlags.contains(.option)) + if newModifiers.modifierFlags.contains(.shift) != shiftIsDown { + shiftIsDown = newModifiers.modifierFlags.contains(.shift) + machine.setKey(VK_Shift, characters: nil, isPressed: shiftIsDown) + } + if newModifiers.modifierFlags.contains(.control) != controlIsDown { + controlIsDown = newModifiers.modifierFlags.contains(.control) + machine.setKey(VK_Control, characters: nil, isPressed: controlIsDown) + } + if newModifiers.modifierFlags.contains(.command) != commandIsDown { + commandIsDown = newModifiers.modifierFlags.contains(.command) + machine.setKey(VK_Command, characters: nil, isPressed: commandIsDown) + } + if newModifiers.modifierFlags.contains(.option) != optionIsDown { + optionIsDown = newModifiers.modifierFlags.contains(.option) + machine.setKey(VK_Option, characters: nil, isPressed: optionIsDown) + } } } + private var shiftIsDown = false + private var controlIsDown = false + private var commandIsDown = false + private var optionIsDown = false /// Forwards mouse movement events to the mouse. func mouseMoved(_ event: NSEvent) {