From 570aa229ce3c7791d51e8b75be022be49dee70f8 Mon Sep 17 00:00:00 2001 From: thrust26 Date: Sat, 30 Jan 2021 11:25:51 +0100 Subject: [PATCH] derived Genesis and Booster from Joystick controller class --- src/emucore/Booster.cxx | 110 +++------------------------------------ src/emucore/Booster.hxx | 37 +++---------- src/emucore/Genesis.cxx | 87 +++---------------------------- src/emucore/Genesis.hxx | 37 +++---------- src/emucore/Joystick.cxx | 109 +++++++++++++++++++++++++------------- src/emucore/Joystick.hxx | 50 ++++++++++++++++-- 6 files changed, 148 insertions(+), 282 deletions(-) diff --git a/src/emucore/Booster.cxx b/src/emucore/Booster.cxx index 1fd8d2faf..be48bc4e8 100644 --- a/src/emucore/Booster.cxx +++ b/src/emucore/Booster.cxx @@ -15,36 +15,21 @@ // this file, and for a DISCLAIMER OF ALL WARRANTIES. //============================================================================ -#include "Event.hxx" #include "Booster.hxx" // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - BoosterGrip::BoosterGrip(Jack jack, const Event& event, const System& system) - : Controller(jack, event, system, Controller::Type::BoosterGrip) + : Joystick(jack, event, system, Controller::Type::BoosterGrip) { if(myJack == Jack::Left) { - myUpEvent = Event::JoystickZeroUp; - myDownEvent = Event::JoystickZeroDown; - myLeftEvent = Event::JoystickZeroLeft; - myRightEvent = Event::JoystickZeroRight; - myFireEvent = Event::JoystickZeroFire; myTriggerEvent = Event::JoystickZeroFire5; myBoosterEvent = Event::JoystickZeroFire9; - myXAxisValue = Event::PaddleZeroAnalog; - myYAxisValue = Event::PaddleOneAnalog; } else { - myUpEvent = Event::JoystickOneUp; - myDownEvent = Event::JoystickOneDown; - myLeftEvent = Event::JoystickOneLeft; - myRightEvent = Event::JoystickOneRight; - myFireEvent = Event::JoystickOneFire; myTriggerEvent = Event::JoystickOneFire5; myBoosterEvent = Event::JoystickOneFire9; - myXAxisValue = Event::PaddleTwoAnalog; - myYAxisValue = Event::PaddleThreeAnalog; } setPin(AnalogPin::Five, MAX_RESISTANCE); @@ -52,98 +37,17 @@ BoosterGrip::BoosterGrip(Jack jack, const Event& event, const System& system) } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void BoosterGrip::update() +void BoosterGrip::updateButtons() { - // Digital events (from keyboard or joystick hats & buttons) - setPin(DigitalPin::One, myEvent.get(myUpEvent) == 0); - setPin(DigitalPin::Two, myEvent.get(myDownEvent) == 0); - setPin(DigitalPin::Three, myEvent.get(myLeftEvent) == 0); - setPin(DigitalPin::Four, myEvent.get(myRightEvent) == 0); bool firePressed = myEvent.get(myFireEvent) != 0; - // The CBS Booster-grip has two more buttons on it. These buttons are // connected to the inputs usually used by paddles. - setPin( - AnalogPin::Five, - (myEvent.get(myTriggerEvent) != 0) ? MIN_RESISTANCE : MAX_RESISTANCE - ); - setPin( - AnalogPin::Nine, - (myEvent.get(myBoosterEvent) != 0) ? MIN_RESISTANCE : MAX_RESISTANCE - ); + bool triggerPressed = myEvent.get(myTriggerEvent) != 0; + bool boosterPressed = myEvent.get(myBoosterEvent) != 0; - // Axis events (usually generated by the Stelladaptor) - int xaxis = myEvent.get(myXAxisValue); - int yaxis = myEvent.get(myYAxisValue); - if(xaxis > 16384-4096) - { - setPin(DigitalPin::Four, false); - // Stelladaptor sends "half moved right" for L+R pushed together - if(xaxis < 16384+4096) - setPin(DigitalPin::Three, false); - } - else if(xaxis < -16384) - setPin(DigitalPin::Three, false); - if(yaxis > 16384-4096) - { - setPin(DigitalPin::Two, false); - // Stelladaptor sends "half moved down" for U+D pushed together - if(yaxis < 16384+4096) - setPin(DigitalPin::One, false); - } - else if(yaxis < -16384) - setPin(DigitalPin::One, false); + updateMouseButtons(firePressed, boosterPressed); - // Mouse motion and button events - if(myControlID > -1) - { - // The following code was taken from z26 - #define MJ_Threshold 2 - int mousex = myEvent.get(Event::MouseAxisXMove), - mousey = myEvent.get(Event::MouseAxisYMove); - if(mousex || mousey) - { - if((!(abs(mousey) > abs(mousex) << 1)) && (abs(mousex) >= MJ_Threshold)) - { - if(mousex < 0) - setPin(DigitalPin::Three, false); - else if (mousex > 0) - setPin(DigitalPin::Four, false); - } - - if((!(abs(mousex) > abs(mousey) << 1)) && (abs(mousey) >= MJ_Threshold)) - { - if(mousey < 0) - setPin(DigitalPin::One, false); - else if(mousey > 0) - setPin(DigitalPin::Two, false); - } - } - // Get mouse button state - firePressed = firePressed - || myEvent.get(Event::MouseButtonLeftValue); - if(myEvent.get(Event::MouseButtonRightValue)) - setPin(AnalogPin::Nine, MIN_RESISTANCE); - } setPin(DigitalPin::Six, !getAutoFireState(firePressed)); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool BoosterGrip::setMouseControl( - Controller::Type xtype, int xid, Controller::Type ytype, int yid) -{ - // Currently, the booster-grip takes full control of the mouse, using both - // axes for its two degrees of movement, and the left/right buttons for - // fire and booster, respectively - if(xtype == Controller::Type::BoosterGrip && ytype == Controller::Type::BoosterGrip && - xid == yid) - { - myControlID = ((myJack == Jack::Left && xid == 0) || - (myJack == Jack::Right && xid == 1) - ) ? xid : -1; - } - else - myControlID = -1; - - return true; + setPin(AnalogPin::Five, triggerPressed ? MIN_RESISTANCE : MAX_RESISTANCE); + setPin(AnalogPin::Nine, boosterPressed ? MIN_RESISTANCE : MAX_RESISTANCE); } diff --git a/src/emucore/Booster.hxx b/src/emucore/Booster.hxx index 7cb7f8a5b..eed210d38 100644 --- a/src/emucore/Booster.hxx +++ b/src/emucore/Booster.hxx @@ -18,8 +18,7 @@ #ifndef BOOSTERGRIP_HXX #define BOOSTERGRIP_HXX -#include "Control.hxx" -#include "Event.hxx" +#include "Joystick.hxx" /** The standard Atari 2600 joystick controller fitted with the @@ -28,7 +27,7 @@ @author Bradford W. Mott */ -class BoosterGrip : public Controller +class BoosterGrip : public Joystick { public: /** @@ -42,45 +41,21 @@ class BoosterGrip : public Controller ~BoosterGrip() override = default; public: - /** - Update the entire digital and analog pin state according to the - events currently set. - */ - void update() override; - /** Returns the name of this controller. */ string name() const override { return "BoosterGrip"; } + private: /** - Determines how this controller will treat values received from the - X/Y axis and left/right buttons of the mouse. Since not all controllers - use the mouse the same way (or at all), it's up to the specific class to - decide how to use this data. - - In the current implementation, the left button is tied to the X axis, - and the right one tied to the Y axis. - - @param xtype The controller to use for x-axis data - @param xid The controller ID to use for x-axis data (-1 for no id) - @param ytype The controller to use for y-axis data - @param yid The controller ID to use for y-axis data (-1 for no id) - - @return Whether the controller supports using the mouse + Update the button pin states. */ - bool setMouseControl( - Controller::Type xtype, int xid, Controller::Type ytype, int yid) override; + void updateButtons() override; private: // Pre-compute the events we care about based on given port // This will eliminate test for left or right port in update() - Event::Type myUpEvent, myDownEvent, myLeftEvent, myRightEvent, - myFireEvent, myBoosterEvent, myTriggerEvent, - myXAxisValue, myYAxisValue; - - // Controller to emulate in normal mouse axis mode - int myControlID{-1}; + Event::Type myBoosterEvent, myTriggerEvent; private: // Following constructors and assignment operators not supported diff --git a/src/emucore/Genesis.cxx b/src/emucore/Genesis.cxx index 499a31493..fc5459d82 100644 --- a/src/emucore/Genesis.cxx +++ b/src/emucore/Genesis.cxx @@ -15,102 +15,31 @@ // this file, and for a DISCLAIMER OF ALL WARRANTIES. //============================================================================ -#include "Event.hxx" #include "Genesis.hxx" // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Genesis::Genesis(Jack jack, const Event& event, const System& system) - : Controller(jack, event, system, Controller::Type::Genesis) + : Joystick(jack, event, system, Controller::Type::Genesis) { if(myJack == Jack::Left) - { - myUpEvent = Event::JoystickZeroUp; - myDownEvent = Event::JoystickZeroDown; - myLeftEvent = Event::JoystickZeroLeft; - myRightEvent = Event::JoystickZeroRight; - myFire1Event = Event::JoystickZeroFire; - myFire2Event = Event::JoystickZeroFire5; - } + myButtonCEvent = Event::JoystickZeroFire5; else - { - myUpEvent = Event::JoystickOneUp; - myDownEvent = Event::JoystickOneDown; - myLeftEvent = Event::JoystickOneLeft; - myRightEvent = Event::JoystickOneRight; - myFire1Event = Event::JoystickOneFire; - myFire2Event = Event::JoystickOneFire5; - } + myButtonCEvent = Event::JoystickOneFire5; setPin(AnalogPin::Five, MIN_RESISTANCE); - setPin(AnalogPin::Nine, MIN_RESISTANCE); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void Genesis::update() +void Genesis::updateButtons() { - // Digital events (from keyboard or joystick hats & buttons) - setPin(DigitalPin::One, myEvent.get(myUpEvent) == 0); - setPin(DigitalPin::Two, myEvent.get(myDownEvent) == 0); - setPin(DigitalPin::Three, myEvent.get(myLeftEvent) == 0); - setPin(DigitalPin::Four, myEvent.get(myRightEvent) == 0); - bool firePressed = myEvent.get(myFire1Event) != 0; - + bool firePressed = myEvent.get(myFireEvent) != 0; // The Genesis has one more button (C) that can be read by the 2600 // However, it seems to work opposite to the BoosterGrip controller, // in that the logic is inverted - setPin(AnalogPin::Five, - (myEvent.get(myFire2Event) == 0) ? MIN_RESISTANCE : MAX_RESISTANCE - ); + bool buttonCPressed = myEvent.get(myButtonCEvent) != 0; - // Mouse motion and button events - if(myControlID > -1) - { - // The following code was taken from z26 - #define MJ_Threshold 2 - int mousex = myEvent.get(Event::MouseAxisXMove), - mousey = myEvent.get(Event::MouseAxisYMove); - if(mousex || mousey) - { - if((!(abs(mousey) > abs(mousex) << 1)) && (abs(mousex) >= MJ_Threshold)) - { - if(mousex < 0) - setPin(DigitalPin::Three, false); - else if(mousex > 0) - setPin(DigitalPin::Four, false); - } + updateMouseButtons(firePressed, buttonCPressed); - if((!(abs(mousex) > abs(mousey) << 1)) && (abs(mousey) >= MJ_Threshold)) - { - if(mousey < 0) - setPin(DigitalPin::One, false); - else if(mousey > 0) - setPin(DigitalPin::Two, false); - } - } - // Get mouse button state - firePressed = firePressed - || myEvent.get(Event::MouseButtonLeftValue); - if(myEvent.get(Event::MouseButtonRightValue)) - setPin(AnalogPin::Five, MAX_RESISTANCE); - } setPin(DigitalPin::Six, !getAutoFireState(firePressed)); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool Genesis::setMouseControl( - Controller::Type xtype, int xid, Controller::Type ytype, int yid) -{ - // Currently, the Genesis controller takes full control of the mouse, using - // both axes for its two degrees of movement, and the left/right buttons for - // 'B' and 'C', respectively - if(xtype == Controller::Type::Genesis && ytype == Controller::Type::Genesis && xid == yid) - { - myControlID = ((myJack == Jack::Left && xid == 0) || - (myJack == Jack::Right && xid == 1) - ) ? xid : -1; - } - else - myControlID = -1; - - return true; + setPin(AnalogPin::Five, buttonCPressed ? MAX_RESISTANCE : MIN_RESISTANCE); } diff --git a/src/emucore/Genesis.hxx b/src/emucore/Genesis.hxx index 5b9c94ff7..88fbda1d8 100644 --- a/src/emucore/Genesis.hxx +++ b/src/emucore/Genesis.hxx @@ -18,9 +18,7 @@ #ifndef GENESIS_HXX #define GENESIS_HXX -#include "bspf.hxx" -#include "Control.hxx" -#include "Event.hxx" +#include "Joystick.hxx" /** The standard Sega Genesis controller works with the 2600 console for @@ -30,7 +28,7 @@ @author Stephen Anthony */ -class Genesis : public Controller +class Genesis : public Joystick { public: /** @@ -44,44 +42,21 @@ class Genesis : public Controller ~Genesis() override = default; public: - /** - Update the entire digital and analog pin state according to the - events currently set. - */ - void update() override; - /** Returns the name of this controller. */ string name() const override { return "Genesis"; } + private: /** - Determines how this controller will treat values received from the - X/Y axis and left/right buttons of the mouse. Since not all controllers - use the mouse the same way (or at all), it's up to the specific class to - decide how to use this data. - - In the current implementation, the left button is tied to the X axis, - and the right one tied to the Y axis. - - @param xtype The controller to use for x-axis data - @param xid The controller ID to use for x-axis data (-1 for no id) - @param ytype The controller to use for y-axis data - @param yid The controller ID to use for y-axis data (-1 for no id) - - @return Whether the controller supports using the mouse + Update the button pin states. */ - bool setMouseControl( - Controller::Type xtype, int xid, Controller::Type ytype, int yid) override; + void updateButtons() override; private: // Pre-compute the events we care about based on given port // This will eliminate test for left or right port in update() - Event::Type myUpEvent, myDownEvent, myLeftEvent, myRightEvent, - myFire1Event, myFire2Event; - - // Controller to emulate in normal mouse axis mode - int myControlID{-1}; + Event::Type myButtonCEvent; private: // Following constructors and assignment operators not supported diff --git a/src/emucore/Joystick.cxx b/src/emucore/Joystick.cxx index 673f10720..41c657d83 100644 --- a/src/emucore/Joystick.cxx +++ b/src/emucore/Joystick.cxx @@ -15,89 +15,133 @@ // this file, and for a DISCLAIMER OF ALL WARRANTIES. //============================================================================ -#include "Event.hxx" #include "Joystick.hxx" // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Joystick::Joystick(Jack jack, const Event& event, const System& system, bool altmap) - : Controller(jack, event, system, Controller::Type::Joystick) + : Joystick(jack, event, system, Controller::Type::Joystick, altmap) +{ +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Joystick::Joystick(Jack jack, const Event& event, const System& system, + Controller::Type type, bool altmap) + : Controller(jack, event, system, type) { if(myJack == Jack::Left) { if(!altmap) { - myUpEvent = Event::JoystickZeroUp; - myDownEvent = Event::JoystickZeroDown; - myLeftEvent = Event::JoystickZeroLeft; + myUpEvent = Event::JoystickZeroUp; + myDownEvent = Event::JoystickZeroDown; + myLeftEvent = Event::JoystickZeroLeft; myRightEvent = Event::JoystickZeroRight; - myFireEvent = Event::JoystickZeroFire; + myFireEvent = Event::JoystickZeroFire; } else { - myUpEvent = Event::JoystickTwoUp; - myDownEvent = Event::JoystickTwoDown; - myLeftEvent = Event::JoystickTwoLeft; + myUpEvent = Event::JoystickTwoUp; + myDownEvent = Event::JoystickTwoDown; + myLeftEvent = Event::JoystickTwoLeft; myRightEvent = Event::JoystickTwoRight; - myFireEvent = Event::JoystickTwoFire; + myFireEvent = Event::JoystickTwoFire; } - myXAxisValue = Event::PaddleZeroAnalog; - myYAxisValue = Event::PaddleOneAnalog; + myXAxisValue = Event::PaddleZeroAnalog; // TODO + myYAxisValue = Event::PaddleOneAnalog; // TODO } else { if(!altmap) { - myUpEvent = Event::JoystickOneUp; - myDownEvent = Event::JoystickOneDown; - myLeftEvent = Event::JoystickOneLeft; + myUpEvent = Event::JoystickOneUp; + myDownEvent = Event::JoystickOneDown; + myLeftEvent = Event::JoystickOneLeft; myRightEvent = Event::JoystickOneRight; - myFireEvent = Event::JoystickOneFire; + myFireEvent = Event::JoystickOneFire; } else { - myUpEvent = Event::JoystickThreeUp; - myDownEvent = Event::JoystickThreeDown; - myLeftEvent = Event::JoystickThreeLeft; + myUpEvent = Event::JoystickThreeUp; + myDownEvent = Event::JoystickThreeDown; + myLeftEvent = Event::JoystickThreeLeft; myRightEvent = Event::JoystickThreeRight; - myFireEvent = Event::JoystickThreeFire; + myFireEvent = Event::JoystickThreeFire; } - myXAxisValue = Event::PaddleTwoAnalog; - myYAxisValue = Event::PaddleThreeAnalog; + myXAxisValue = Event::PaddleTwoAnalog; // TODO + myYAxisValue = Event::PaddleThreeAnalog; // TODO } } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void Joystick::update() +{ + updateButtons(); + + updateDigitalAxes(); + updateAnalogAxes(); + updateMouseAxes(); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void Joystick::updateButtons() +{ + bool firePressed = myEvent.get(myFireEvent) != 0; + + // The joystick uses both mouse buttons for the single joystick button + updateMouseButtons(firePressed, firePressed); + + setPin(DigitalPin::Six, !getAutoFireState(firePressed)); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void Joystick::updateMouseButtons(bool& pressedLeft, bool& pressedRight) +{ + if(myControlID > -1) + { + pressedLeft |= (myEvent.get(Event::MouseButtonLeftValue) != 0); + pressedRight |= (pressedRight || myEvent.get(Event::MouseButtonRightValue) != 0); + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void Joystick::updateDigitalAxes() { // Digital events (from keyboard or joystick hats & buttons) setPin(DigitalPin::One, myEvent.get(myUpEvent) == 0); setPin(DigitalPin::Two, myEvent.get(myDownEvent) == 0); setPin(DigitalPin::Three, myEvent.get(myLeftEvent) == 0); setPin(DigitalPin::Four, myEvent.get(myRightEvent) == 0); - bool firePressed = myEvent.get(myFireEvent) != 0; +} +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void Joystick::updateAnalogAxes() +{ // Axis events (usually generated by the Stelladaptor) int xaxis = myEvent.get(myXAxisValue); int yaxis = myEvent.get(myYAxisValue); - if(xaxis > 16384-4096) + if(xaxis > 16384 - 4096) { setPin(DigitalPin::Four, false); // Stelladaptor sends "half moved right" for L+R pushed together - if(xaxis < 16384+4096) + if(xaxis < 16384 + 4096) setPin(DigitalPin::Three, false); } else if(xaxis < -16384) setPin(DigitalPin::Three, false); - if(yaxis > 16384-4096) + if(yaxis > 16384 - 4096) { setPin(DigitalPin::Two, false); // Stelladaptor sends "half moved down" for U+D pushed together - if(yaxis < 16384+4096) + if(yaxis < 16384 + 4096) setPin(DigitalPin::One, false); } else if(yaxis < -16384) setPin(DigitalPin::One, false); +} +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void Joystick::updateMouseAxes() +{ // Mouse motion and button events if(myControlID > -1) { @@ -105,6 +149,7 @@ void Joystick::update() #define MJ_Threshold 2 int mousex = myEvent.get(Event::MouseAxisXMove), mousey = myEvent.get(Event::MouseAxisYMove); + if(mousex || mousey) { if((!(abs(mousey) > abs(mousex) << 1)) && (abs(mousex) >= MJ_Threshold)) @@ -123,12 +168,7 @@ void Joystick::update() setPin(DigitalPin::Two, false); } } - // Get mouse button state - firePressed = firePressed - || myEvent.get(Event::MouseButtonLeftValue) - || myEvent.get(Event::MouseButtonRightValue); } - setPin(DigitalPin::Six, !getAutoFireState(firePressed)); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -136,9 +176,8 @@ bool Joystick::setMouseControl( Controller::Type xtype, int xid, Controller::Type ytype, int yid) { // Currently, the joystick takes full control of the mouse, using both - // axes for its two degrees of movement, and both mouse buttons for the - // single joystick button - if(xtype == Controller::Type::Joystick && ytype == Controller::Type::Joystick && xid == yid) + // axes for its two degrees of movement + if(xtype == myType && ytype == myType && xid == yid) { myControlID = ((myJack == Jack::Left && xid == 0) || (myJack == Jack::Right && xid == 1) diff --git a/src/emucore/Joystick.hxx b/src/emucore/Joystick.hxx index f67c397c0..f47051189 100644 --- a/src/emucore/Joystick.hxx +++ b/src/emucore/Joystick.hxx @@ -18,7 +18,6 @@ #ifndef JOYSTICK_HXX #define JOYSTICK_HXX -#include "bspf.hxx" #include "Event.hxx" #include "Control.hxx" @@ -41,9 +40,23 @@ class Joystick : public Controller @param system The system using this controller @param altmap If true, use alternative mapping */ - Joystick(Jack jack, const Event& event, const System& system, bool altmap = false); + Joystick(Jack jack, const Event& event, const System& system, + bool altmap = false); + ~Joystick() override = default; + protected: + /** + Create a new controller plugged into the specified jack + + @param jack The jack the controller is plugged into + @param event The event object to use for events + @param system The system using this controller + @param type The controller type + */ + Joystick(Jack jack, const Event& event, const System& system, + Controller::Type type, bool altmap = false); + public: /** Update the entire digital and analog pin state according to the @@ -88,17 +101,48 @@ class Joystick : public Controller static int deadZoneValue(int deadzone); inline static int deadzone() { return _DEAD_ZONE; } + protected: + /** + Update the button pin states. + */ + virtual void updateButtons(); + + /** + Update the button states from the mouse button events currently set. + */ + void updateMouseButtons(bool& pressedLeft, bool& pressedRight); + + protected: + Event::Type myFireEvent; + private: // Pre-compute the events we care about based on given port // This will eliminate test for left or right port in update() Event::Type myUpEvent, myDownEvent, myLeftEvent, myRightEvent, - myXAxisValue, myYAxisValue, myFireEvent; + myXAxisValue, myYAxisValue; // Controller to emulate in normal mouse axis mode int myControlID{-1}; static int _DEAD_ZONE; + private: + /** + Update the axes pin states according to the keyboard + or joystick hats & buttons events currently set. + */ + void updateDigitalAxes(); + + /** + Update the axes pin states according to the axes value events currently set. + */ + void updateAnalogAxes(); + + /** + Update the axes pin states according to mouse events currently set. + */ + void updateMouseAxes(); + private: // Following constructors and assignment operators not supported Joystick() = delete;