void Agent::vmTick() { // Tick the VM associated with this agent. assert(vm); // There must *be* a VM to tick. LifeAssert la(this); // sanity check // If we're out of timeslice, give ourselves some more (depending on the game type). if (!vm->timeslice) { vm->timeslice = (engine.version < 3) ? 1 : 5; } // Keep trying to run VMs while we don't run out of timeslice, end up with a blocked VM, or a VM stops. while (vm && vm->timeslice && !vm->isBlocking() && !vm->stopped()) { assert(vm->timeslice > 0); // Tell the VM to tick (using all available timeslice), catching exceptions as necessary. try { vm->tick(); } catch (invalidAgentException &e) { // try letting the exception script handle it if (!queueScript(255)) unhandledException(e.prettyPrint(), true); else stopScript(); // we still want current script to die } catch (creaturesException &e) { unhandledException(e.prettyPrint(), true); } catch (std::exception &e) { unhandledException(e.what(), true); } // If the VM stopped, it's done. if (vm && vm->stopped()) { world.freeVM(vm); vm = NULL; } } // Zot any remaining timeslice, since we're done now. if (vm) vm->timeslice = 0; // If there's no current VM but there's one on the call stack, a previous VM must have finished, // pop one from the call stack to run next time. if (!vm && !vmstack.empty()) { vm = vmstack.front(); vmstack.pop_front(); } }
void Agent::physicsTickC2() { int dx = velx.getInt(), dy = vely.getInt(); if (dx != 0 || dy != 0) falling = true; if (falling && sufferphysics()) { dy += accg.getInt(); } falling = true; if (dy == 0 && dx == 0) { // nothing to do if (vely.getInt() == 0) { // really no motion falling = false; } else { // y motion cancelled by gravity vely.setInt(dy); } return; } vely.setInt(dy); Point deltapt(0,0); double delta = 1000000000; bool collided = false; if (suffercollisions()) { MetaRoom *m = world.map.metaRoomAt(x, y); if (!m) { if (!displaycore) unhandledException(boost::str(boost::format("out of room system at (%f, %f)") % x % y), false); falling = false; displaycore = true; return; } for (unsigned int i = 0; i < 4; i++) { Point src = boundingBoxPoint(i); findCollisionInDirection(i, m, src, dx, dy, deltapt, delta, collided, true); } } else { deltapt.x = dx; deltapt.y = dy; } if (collided && (velx.getInt() != 0 || vely.getInt() != 0) && moved_last_tick) { if (lastcollidedirection >= 2) // up and down vely.setInt(-(vely.getInt() - (rest.getInt() * vely.getInt()) / 100)); else velx.setInt(-(velx.getInt() - (rest.getInt() * velx.getInt()) / 100)); queueScript(6, 0); } if ((int)deltapt.x == 0 && (int)deltapt.y == 0) { if (!moved_last_tick) { falling = false; velx.setInt(0); vely.setInt(0); } moved_last_tick = false; } else { moved_last_tick = true; moveTo(x + (int)deltapt.x, y + (int)deltapt.y); if (sufferphysics()) { int fricx = (aero.getInt() * velx.getInt()) / 100; int fricy = (aero.getInt() * vely.getInt()) / 100; if (abs(velx.getInt()) > 0 && fricx == 0) fricx = (velx.getInt() < 0) ? -1 : 1; if (abs(vely.getInt()) > 0 && fricy == 0) fricy = (vely.getInt() < 0) ? -1 : 1; velx.setInt(velx.getInt() - fricx); vely.setInt(vely.getInt() - fricy); } } }
// Creatures 2 collision finding void Agent::findCollisionInDirection(unsigned int i, class MetaRoom *m, Point src, int &dx, int &dy, Point &deltapt, double &delta, bool &collided, bool followrooms) { src.x = (int)src.x; src.y = (int)src.y; if (m->wraparound()) { if (src.x > m->x() + m->width() || (dx > 0 && src.x == m->x() + m->width())) src.x -= m->width(); else if (src.x < m->x() || (dx < 0 && src.x == m->x())) src.x += m->width(); } // TODO: caching rooms affects behaviour - work out if that's a problem shared_ptr<Room> room = roomcache[i].lock(); if (!room || !room->containsPoint(src.x, src.y)) { room = bestRoomAt(src.x, src.y, i, m, shared_ptr<Room>()); roomcache[i] = room; } if (!room) { // out of room system if (!displaycore) unhandledException(boost::str(boost::format("out of room system at (%f, %f)") % src.x % src.y), false); falling = false; displaycore = true; return; } int lastdirection = 0; bool steep = abs(dy) > abs(dx); int signdx = dx < 0 ? -1 : 1; int signdy = dy < 0 ? -1 : 1; Line l(Point(0,0),Point(dx,dy)); Point lastpoint(0,0); Vehicle *vehicle = 0; if (invehicle) vehicle = dynamic_cast<Vehicle *>(invehicle.get()); for (int loc = 0; loc <= abs(steep ? dy : dx); loc++) { Point p = steep ? l.pointAtY(loc*signdy) : l.pointAtX(loc*signdx); p.x = (int)p.x; p.y = (int)p.y; if (vehicle) { if (src.x + p.x < vehicle->x + vehicle->cabinleft) { lastdirection = 0; collided = true; break; } if (src.x + p.x > vehicle->x + vehicle->cabinright) { lastdirection = 1; collided = true; break; } if (src.y + p.y < vehicle->y + vehicle->cabintop) { lastdirection = 2; collided = true; break; } if (src.y + p.y > vehicle->y + vehicle->cabinbottom) { lastdirection = 3; collided = true; break; } lastpoint = p; continue; } bool trycollisions = false; bool endofroom = false; if (src.x + p.x < room->x_left) { if (i != 1 && dx < 0) { trycollisions = true; lastdirection = 0; } endofroom = true; } if (src.x + p.x > room->x_right) { if (i != 0 && dx > 0) { trycollisions = true; lastdirection = 1; } endofroom = true; } if (src.y + p.y < room->y_left_ceiling) { if (i != 3 && dy < 0) { trycollisions = true; lastdirection = 2; } endofroom = true; } if (src.y + p.y > room->y_left_floor) { if (i != 2 && dy > 0) { trycollisions = true; lastdirection = 3; } endofroom = true; } // find the next room, if necessary, and work out whether we should move into it or break out if (endofroom) { if (m->wraparound()) { if (dx > 0 && src.x + p.x >= m->x() + m->width()) src.x -= m->width(); else if (dx < 0 && src.x + p.x <= m->x()) src.x += m->width(); } shared_ptr<Room> newroom = bestRoomAt(src.x + p.x, src.y + p.y, i, m, room); bool collision = false; // collide if we're out of the room system if (!newroom) { collision = true; } // collide if there's no new room connected to this one else if (room->doors.find(newroom) == room->doors.end()) { collision = true; } // collide if the PERM between this room and the new room is smaller than or equal to our size else if (size.getInt() > room->doors[newroom]->perm) { collision = true; } if (collision && (trycollisions || (!newroom))) { collided = true; break; } // move to the new room and keep going! room = newroom; } if (room->floorpoints.size() && i == 3 && dy >= 0 && size.getInt() > room->floorvalue.getInt()) { // TODO: Hack! // TODO: we don't check floorYatX isn't returning a 'real' room floor, but floorpoints should cover the whole floor anyway int floory = room->floorYatX(src.x + p.x); // never collide when the top point of an object is below the floor. Point top = boundingBoxPoint(2); // TODO: consider steep floors if (src.y + p.y > floory && top.y < floory) { collided = true; lastdirection = 3; break; } } if ((!followrooms) && endofroom) break; lastpoint = p; } double length2 = (lastpoint.x * lastpoint.x) + (lastpoint.y * lastpoint.y); if (length2 < delta) { // TODO: !followrooms is a horrible way to detect a non-physics call if (collided && followrooms) lastcollidedirection = lastdirection; deltapt = lastpoint; delta = length2; } }
void Agent::physicsTick() { if (engine.version == 1) return; // C1 has no physics, and different attributes. if (carriedby) return; // We don't move when carried, so what's the point? if (engine.version == 2) { // Creatures 2 physics is different. physicsTickC2(); return; } if (!falling) return; // TODO: there are probably all sorts of issues here, untested if (!wasmoved) return; // some agents are created outside INST and get autokilled if we try physics on them before they move if (invehicle) return; // TODO: c2e verhicle physics // set destination point based on velocities float destx = x + velx.getFloat(); float desty = y + vely.getFloat(); if (rotatable()) { // TODO: the real engine seems to reset velx/vely, so i do that here, but why? velx.setFloat(0.0f); vely.setFloat(0.0f); // TODO: which order should these be in? // calculate forwards velocity float forward_x = fvel * sinf(spin * (M_PI * 2)); float forward_y = fvel * -cosf(spin * (M_PI * 2)); // calculate sideways velocity // TODO: this sideways velocity code is untested float sideways_x = svel * cosf(spin * (M_PI * 2)); float sideways_y = svel * -sinf(spin * (M_PI * 2)); // set destination based on forward/sideways velocity destx = x + forward_x + sideways_x; desty = y + forward_y + sideways_y; // modify spin based on angular velocity spin = fmodf(spin + avel, 1.0f); if (spin < 0.0f) spin += 1.0f; } if (sufferphysics()) { // TODO: falling behaviour needs looking at more closely.. // .. but it shouldn't be 'false' by default on non-physics agents, so.. //falling = false; // increase speed according to accg // TODO: should we be changing vely first, instead of after a successful move (below)? desty += accg.getFloat(); } if (suffercollisions()) { float lastdistance = 1000000.0f; bool collided = false; Line wall; // only valid when collided unsigned int collidedirection = 0; // only valid when collided Point bestmove; // iterate through all four points of the bounding box for (unsigned int i = 0; i < 4; i++) { // this mess is because we want to start with the bottom point - DOWN (3) - before the others, for efficiency Point src = boundingBoxPoint((i == 0) ? 3 : i - 1); // store values float srcx = src.x, srcy = src.y; shared_ptr<Room> ourRoom = world.map.roomAt(srcx, srcy); if (!ourRoom) { ourRoom = world.map.roomAt(srcx, srcy); } if (!ourRoom) { if (!displaycore) { // TODO: ugh, displaycore is a horrible thing to use for this // we're out of the room system, physics bug, but let's try MVSFing back in to cover for fuzzie's poor programming skills static bool tryingmove; tryingmove = false; // avoid infinite loop if (!tryingmove && tryMoveToPlaceAround(x, y)) { //std::cout << identify() << " was out of room system due to a physics bug but we hopefully moved it back in.." << std::endl; tryingmove = true; physicsTick(); return; } // didn't work! unhandledException(boost::str(boost::format("out of room system at (%f, %f)") % srcx % srcy), false); } displaycore = true; falling = false; return; // out of room system } Point dest(destx + (srcx - x), desty + (srcy - y)); unsigned int local_collidedirection; Line local_wall; // this changes src to the point at which we end up bool local_collided = world.map.collideLineWithRoomSystem(src, dest, ourRoom, src, local_wall, local_collidedirection, perm); float dist; if (src.x == srcx && src.y == srcy) dist = 0.0f; else { float xdiff = src.x - srcx; float ydiff = src.y - srcy; dist = xdiff*xdiff + ydiff*ydiff; } if (dist >= lastdistance) { assert(i != 0); // this had better not be our first collision! continue; // further away than a previous collision } lastdistance = dist; bestmove.x = x + (src.x - srcx); bestmove.y = y + (src.y - srcy); collidedirection = local_collidedirection; wall = local_wall; collided = local_collided; if (dist == 0.0f) break; // no point checking any more, is there? } // *** do actual movement if (lastdistance != 0.0f) { moveTo(bestmove.x, bestmove.y); if (collided) { lastcollidedirection = collidedirection; queueScript(6, 0, velx, vely); // TODO: include this? .. we need to include SOMETHING, c3 ball checks for <3 if (elas != 0) { if (wall.getType() == HORIZONTAL) { vely.setFloat(-vely.getFloat()); } else if (wall.getType() == VERTICAL) { velx.setFloat(-velx.getFloat()); } else { // line starts always have a lower x value than the end float xdiff = wall.getEnd().x - wall.getStart().x; float ydiff = wall.getEnd().y - wall.getStart().y; float fvelx = velx.getFloat(), fvely = vely.getFloat(); // calculate input/slope angles double inputangle; if (fvelx == 0.0f) { if (fvely > 0.0f) inputangle = M_PI / 2.0; else inputangle = 3 * (M_PI / 2.0); } else { inputangle = atan(fvely / fvelx); } double slopeangle = atan(-ydiff / xdiff); // xdiff != 0 because wall isn't vertical // calculate output angle double outputangle = slopeangle + (slopeangle - inputangle) + M_PI; // turn back into component velocities double vectorlength = sqrt(fvelx*fvelx + fvely*fvely); float xoutput = cos(outputangle) * vectorlength; float youtput = sin(outputangle) * vectorlength; velx.setFloat(xoutput); vely.setFloat(-youtput); } if (elas != 100.0f) { velx.setFloat(velx.getFloat() * (elas / 100.0f)); vely.setFloat(vely.getFloat() * (elas / 100.0f)); } } else vely.setFloat(0); } else if (sufferphysics() && accg != 0) { vely.setFloat(vely.getFloat() + accg.getFloat()); } } else { // TODO: correct? if (sufferphysics()) { if (velx.getFloat() == 0.0f && vely.getFloat() == 0.0f) falling = false; } velx.setFloat(0); vely.setFloat(0); } } else { if (vely.hasDecimal() || velx.hasDecimal()) moveTo(destx, desty); if (sufferphysics()) vely.setFloat(vely.getFloat() + accg.getFloat()); } if (sufferphysics() && (aero != 0)) { // reduce speed according to AERO // TODO: aero should be an integer! velx.setFloat(velx.getFloat() - (velx.getFloat() * (aero.getFloat() / 100.0f))); vely.setFloat(vely.getFloat() - (vely.getFloat() * (aero.getFloat() / 100.0f))); } if (rotatable()) { avel -= avel * admp; fvel -= fvel * fdmp; svel -= svel * sdmp; } }
TEMPLATE_InterruptHandler void TEMPLATED_InterruptHandler::handle( uint64_t * savedRegs, ExceptionType type, ExceptionOrigin origin, IntID id ) { // kout << INFO << "Handling interrupt\n"; // kout << "exception type = " ; // switch(type) // { // case ExceptionType::IRQ: // kout << "IRQ"; // break; // case ExceptionType::SYNC: // kout << "SYNC"; // break; // default: // kout << "Other"; // break; // } // kout << "\n"; // kout << "exception origin = "; // switch(origin) // { // case ExceptionOrigin::CUR_SP_EL0: // case ExceptionOrigin::CUR_SP_ELx: // kout << "Current"; // break; // case ExceptionOrigin::FROM_LOWER_A64: // kout << "Lower"; // break; // default: // kout << "Other"; // break; // } // kout << "\n"; // RegELR_EL1::read().dump(); // RegESR_EL1::read().dump(); // RegFAR_EL1::read().dump(); if(!_allowSyncExcep) { kout << FATAL << "synchronous exception happened while the handler indicates that synchronous exception is not allowed.\n"; asm_wfi_loop(); } _allowSyncExcep=false;// 需要保存状态 _nestedExceps.emplaceBack(savedRegs,type,origin); _allowSyncExcep=true; switch(type) { case ExceptionType::IRQ: { // 这里需要提供intid //The CPU interface has two IARs. Reading the IAR returns the INTID, and advances the interrupt //state machine. In a typical interrupt handler, one of the first steps when handling an interrupt is to //read one of the IARs handleIRQ(id);//NOTE:by reading it, we acknowledged it.So it will change to 1023 after this read break; } case ExceptionType::FIQ: { handleFIQ(id); break; } case ExceptionType::SError: { handleSError(); break; } case ExceptionType::SYNC: { auto esr = RegESR_EL1::make(currentState().esrELx());//ELx的结构都是相同的 switch(esr.EC) { case ExceptionClass::UNDEF_INST: handleUndefinedInstruction(); break; case ExceptionClass::SVC_AA64: handleSVC(static_cast<SvcFunc>(lowerMaskBits(16) & esr.ISS)); break; case ExceptionClass::DATA_ABORT_LOWER_EL: // user error case ExceptionClass::DATA_ABORT_SAME_EL: // system error handleDataAbort(); break; case ExceptionClass::INSTR_ABORT_LOWER_EL: case ExceptionClass::INSTR_ABORT_SAME_EL: handleInstructionAbort(); break; case ExceptionClass::SP_ALIGNMENT_FAULT: handleSPAlignmentFault(); break; case ExceptionClass::PC_ALIGNMENT_FAULT: handlePCAlignmentFault(); break; case ExceptionClass::SERROR_INTERRUPT: handleSError(); break; case ExceptionClass::SMC_AA64: { // FIXME 尚且不清楚reset的机制,这里的测试结果表明 // qemu的reset只是简单地将PC的值置为RVBAR_EL3 // 通常而言,就是0地址,而其他寄存器值不变 // 应该有其他方法进行reset,但绝不是简单的跳转。 // __asm__ ("mrs x0,RVBAR_EL3 \n\t":::"x0");// 0 auto func=static_cast<SmcFunc>(lowerMaskBits(16) & esr.ISS); if(func==SmcFunc::warmReset) ASM_WARM_RESET(3); break; } case ExceptionClass::HVC_AA64: { auto func=static_cast<HvcFunc>(lowerMaskBits(16) & esr.ISS); if(func==HvcFunc::warmReset) ASM_WARM_RESET(2); break; } default: unhandledException(); break; } break; } case ExceptionType::DEBUG: { break; } } exitCurrent(); }