I did it for a game, but it was a mess.
It was a port of a 2-way fine scroller... by making it a 1-way scroller, we trashed the game pretty badly... it was definitely not faithful to the original. This alone was a disappointment to me and to the client.
It was not achievable. We could horizontal scroll on byte borders, but only by excessive blitting (block transfer). There was too much RAM to move and not enough time. Remember, the IBM PC's RAM throughput was only 1.19 MHz - the same as the Atari 2600 VCS - but with a lot more resolution and thus RAM to move.
I even wrote code that wrote code of a very long sequence of "Load exact address/Store exact address" operations, so we could avoid the CPU overhead of looping and indexing. It just wasn't gonna happen.
The problem is, you weren't fine scrolling so much as saying which scan line to start rendering the display.
We couldn't very well have the top line jumping up and down, so we told the CGA card to add extra lines, so the rendered display ran past the physical top of the monitor bezel and on past the bottom. It was very hokey, and it didn't overlap fully on every monitor bezel. And of course it was completely unsupported.
Use documented interfaces, or else
But the thing that probably killed the method dead was what has always been drummed into your head by IBM: "don't bit-bang the hardware (much). Only use supported and documented methods of interacting with the display, because we're going to change the undocumented stuff". And of course, that's exactly what they did.
So even if we had made it to market, we would have had trouble with third party CGA cards, EGA and everything that came after. We would've had to buy back a lot of games.