This is a complex job.
To break it up, I would tend to use classes. Each class has a setup() and loop() method which are called by the main setup() and loop()
The "Motor" class is set up with the two motor pins. It presents three methods - move(milliseconds), start(), and stop(). It's job is to work the motor, which may involve series of digitalwrites if it's a stepper.
The "Button" class is set up with a button and a reference to a motor. It watches a button pin and calls the motor methods appropriately. It's job is to note when the button was pressed, decide that a hold is in progress, start and stop it's motor appropriately.
The scaffolding looks like this:
class Motor {
const int pin1;
const int pin2;
public:
Motor(int pin1, int pin2) :
pin1(pin1), pin2(pin2) {
}
void setup() {
}
void loop() {
}
void moveMs(unsigned long ms) {
}
void start() {
}
void stop() {
}
};
class Button {
const int pin;
Motor &motor;
public:
Button(int pin, Motor &motor):
pin(pin), motor(motor) {
}
void setup() {
}
void loop() {
}
};
Motor motor[] {
Motor(2, 3),
Motor(4, 5),
Motor(6, 7),
Motor(8, 9),
};
Button button[] {
Button(10, motor[0]),
Button(11, motor[1]),
Button(12, motor[2]),
Button(13, motor[3]),
};
void setup() {
for (int i = 0; i < 4; i++) {
motor[i].setup();
button[i].setup();
}
}
void loop() {
for (int i = 0; i < 4; i++) {
motor[i].loop();
button[i].loop();
}
}
Every component gets a slice of time, and every component cares only about its own little world. Components can call methods on other components to signal them. Those other methods generally should not do things that take a long time: they should just setup the variables so that when loop() gets called, it will be doing the right thing.
The main body of the sketch initialises the components, assigns pins, wires them together and calls setup() and loop() for each one.
(EDIT)
Now, your motors are in sets of two. So I'd build another class "MotorSet". Its job would be to make sure that we are not running both the up and the down motor simultaneously. It would have move, start, and stop same as the motor() class, but with an additional parameter indicating direction. When a move was required it would make sure that the other motor of the pair was stopped (and handle the other motor not stopping immediately, eg there might be a decceleration delay). Button takes a MotorSet and an indication of whether it it the up or the down button for that set, and passes that though as the direction.
The other way to do this would be to make "Motor" aware of its partner, but this potentially creates loops in the control graph. Bad idea. I mean - in this instance it would work (because all a motor is doing is calling "stop" on its mirror), but its bad design.