But.. Will there be chickens?
@ren Hmmm, I guess they are a staple...
* Coordinate at the command end (SY*, DY*)
I don't know if that means the same line it ends, or the next one, so direct copies could be done without updating DY.
By the way, please also consider the original Yamaha application manual scan, where the phrasing is different from Eugeny’s edited version.
I think it means the line after it ends. As I understand it N is the number of lines it will draw, which is NY unless you try to copy across the maximum Y coordinate and it clips. So the end state is:
N = min( NY, MAX_Y - SY, MAX_Y - DY ) = typically NY
SY* = SY + N = typically SY + NY
DY* = DY + N = typically DY + NY
NY* = NY - N = typically NY - NY = 0
What is DYH?
With DYH I mean the high byte of the DY value. When I copy a (16, 4) tile to e.g. (0, 240), DY can end up as 100H, so I need to make sure it is 0.
I made two small errors, there should be no need to set DY for the column copies, only DYH, since the DY increment matches the one I want. For the row copies I also do need to set DYH, for the above mentioned reason.
OK DYH could be set to the fixed value depending on the page you are using for scrolling. I think it not adds much delay.
exx out (c),l ;port 99 out (c),h add hl,de ;next line in vram exx outi ;another pixel add hl,de ;next line in RAM jp nz,loop -- 91 cycles
a port 99 setup for every pixel (!!!), THAT is what takes 30% cpu!
Where is that code from?
wait, the 30% cpu was about scrolling 4 pixel per frame?
that is a whole different thing
the code was borderfill on 9938 where the blitter is lost to scrollcopy.
Ah, yeah scrolling diagonally 2 pixels / frame on V9958, 32 4x16 HMMM copies every two frames.
But my real grand plan to free up CPU time is to make the game run at 30 fps, while updating all the motion at 60 fps (scroll + sprite positions). This means the input handling, game logic, collision detection, tile animations, sprite animations and z-sorting, all this expensive stuff gets spread out over two display frames. This will give a lot more breathing room, and since the motion updates at a higher frequency, the game will still appear smooth.
To achieve the 60 fps motion, the main loop will always write two values for scroll and sprite positions, one for the current and one for the next frame. The ISR will pick the appropriate scroll position of the two, and update the sprite locations from one of two 128-byte RAM buffers. I think this is a fairly modest cost for smooth 60 fps motion.
Slight change of plans; instead of writing sprite attributes from a 128-byte RAM buffer on the ISR, I’m going to double buffer the sprite attribute table, write the attribute data each frame, the colour data every other frame, and use an YMMM command to copy the colour data from the first buffer to the second.
This way I can avoid updating VRAM from the interrupt handler, which is troublesome for two reasons; one, it adds a CPU cost of about 0.7 ms (4% frame time) each frame, but more importantly, accessing VRAM on the ISR either via port 98H or through VDP HMMC commands is troublesome because it conflicts with the main loop. If I use 98H I can’t use that on the main loop, and ditto for HMMC, plus it requires big di blocks around non-HMMC commands. The latter also messes with the command registers so I can’t do optimisations like we discussed earlier.
The 512-byte copy will take approximately 2.7 ms to complete (16% frame time, 8% on average), but since the VDP command engine is idle at that time anyway, it will increase the VDP parallelisation, incur virtually no CPU cost, and the ISR can return quickly. Additionally, all sprite routines can write to VRAM rather than some to RAM and some to VRAM. I think this will be better :).
Also, after the idea a few posts back, I’m definitely going to use OUTI to set VDP registers on the ISR:
; ; VDP registers to set on VBlank ; DisplaySettings: MACRO horizontalOffsetCharacters: db 0 db 26 | VDP_REGISTER horizontalOffsetDots: db 0 db 27 | VDP_REGISTER verticalOffset: db 0 db 23 | VDP_REGISTER patternName: db 0 db 2 | VDP_REGISTER spriteAttributeLSB: db 0 db 5 | VDP_REGISTER spriteAttributeMSB: db 0 db 11 | VDP_REGISTER spritePattern: db 0 db 6 | VDP_REGISTER ENDM ; hl = this DisplaySettings_ApplyHL: ld c,VDP_PORT_1 REPT 14 outi ENDM ret
Nice and quick, and easy to configure. Also there seems to be a nice path to extend this to an easy flexible screensplits system: have the last register set be the next screensplit line, and return a pointer to the next object.
It is probably the way intended to be used considering the OUTI Z80 instruction. Direct access for non-consecutive registers, and indirect for consecutive ones as OUTI count can be almost halved.
Also, using C instead hardcoding the numbre it complies with the MSX regulation. Instead VDP_PORT_1, shadow copy the ports listed on BIOS to somewhere in RAM and set C from there.
ld a, (vdp_port_1) ; now it as a RAM address ld c, a ...
But remember also to update the register values in system variables, or you (or any other software in the case of DOS software with EXIT to prompt option, this is, a non-reset-to-quit software) will not be able to read their values. You could delay this and do it i.e. when any copy command to use that VDP working time.