There are two issues to address here, each of which has a couple of solutions.
Size of vectors
Your first problem is how you do any processing at all without overflows. A vector (32767, 0) is clearly going to rotate nicely to anywhere. However a vector (32767, 32767) is going to become (49339, 0) when it's rotated 45 degrees, and that'll overflow. Even (-32768, 0) is going to overflow if you rotate it by 180 degrees.
Limit vector magnitude
You might solve this by having something limit the vectors entered so that their magnitude is small enough to fit. If data is being entered from a PC, you can use that to only give you valid data. Calculating a magnitude needs a square root though, which is going to be tricky on a constrained device.
Or only store 15-bit values ####8
If these checks are too much of a problem, you could just limit the range of values for vector entry to signed 15-bit. Processing can then take place in signed 16-bit arithmetic, knowing that the result cannot overflow. This would be my preferred solution, not just for simplicity, but because you have engineered out any possibility of failure.
Calculation of rotation
And then we come to how you do the rotation calculation without overflow. The sin/cos clearly is a fraction. I would strongly recommend having an extra bit in the sin/cos lookup value so that +1 can be exactly represented (e.g. +/-128 in a 9-bit value; or +/-64 if constrained to 8-bit). But from there, how to avoid overflow?
Use your processor's multiply instruction registers
On pretty much all processors with a multiply instruction, you have two registers in and two registers out. If you start with two 16-bit values, you get one 32-bit value held in two registers. If you're using +/-64 to represent +/-1, you'll need to divide the result by 64 - which conveniently just means discarding the lower 7 bits. With some simple bit-shifting and masking, it's easy to get a single word containing the result you want.
Break the multiply into lots of conditional adds
If you've got a sin/cos value of 64 (representing 1.0) then the input value passes through unchanged. Each lower bit after that represents a fraction of the input - 32 represents a half, 16 represents a quarter, and so on. You can add all these fractions together, knowing that the result can never overflow. Of course this takes a bunch of operations, but you're optimising for storage over speed. (You might also get some rounding errors in the lsbs, but again, this is prioritising storage over all else.)
But more generally...?
I'd have to wonder about where these limitations came from. If you're on an FPGA, it's unlikely you're so constrained by storage as to be worried by this. And if you're using a micro, it's rare that you'd be this limited these days.
x
wont affecty
calculation (typical novice mistake). Rewrite it in a way ofnewx = x * cos(theta) - y * sin(theta); newy = x * sin(theta) + y * cos(theta);
\$\endgroup\$pmulhw
(felixcloutier.com/x86/pmulhw) gives you the high half of a 16x16 signed multiply,pmullw
gives you the low half. \$\endgroup\$