See that the:
while(jiffies < FRAMERATE) { halt(); }
Includes the JIFFY wait. Even with other devices generating interrupts or with line interrupt is the same. If you remove the halt() inside is virtually the same, changing NOPs by more read-RAM ops.
Let the CPU does NOPs. This is an inherited habit I got from PC and other multi-threaded devices. I does not harm. Think on a possible new multi-task OS. Probably never, but as I said is an habit. Good practices like using BIOS instead I/O ports even when we know there will not be a new MSX model. i.e. can someone be sure if Turbo-R R800 has the same behavior than the Z80?, or even the one-chip models can implement some, reducing energy and temperature.
The optional halt() changes in the case of another interrupts. In this case can change the jiffy usage instead using the optional halt(). But, putting jiffy = 0 just before the wait has a problem, it only sync for 60 fps. It is better to use 2 jiffy references:
loop { jiffy = 0; ... // game logic jiffyref = jiffy; while(jiffy < FRAMERATE || jiffyref == jiffy) { halt(); } ... // fast drawing }
This waits and sync for any framerate pace.
Also, if you have other devices generating interrupts, what happens with line interrupts?, the code will be broken.
It’s best to just forget about the halts, in fact stay away from them because it can make things look like they are working when they are really not, and stick to checking JIFFY only.
I disagree. Yes, working with JIFFY is fine in most cases, but there are cases when you want to use HALTs. As you said, line interrupts is one of these cases.
Anything that works in overscan mode will depend on a couple of line interrupts, so these programs will need a nice HALT loop.
Nope, in that case you still don’t want to use HALT, but another counter or flag set on the line interrupt, or (more commonly) just do it in the interrupt handling routine.
loop: halt jr loop
So, what purpose does the halt
serve here? None...
See that the:
while(jiffies < FRAMERATE) { halt(); }
Includes the JIFFY wait.
Except that you had a halt()
before the while loop to which you attributed behaviour which it only has if vblank is your only interrupt. This is what I referred to as being confusing , and this is why I recommend to stay away from
halt
. It does more harm than good.
Let the CPU does NOPs. This is an inherited habit I got from PC and other multi-threaded devices. I does not harm.
It does do harm because it allows people to write code which relies on halt to do synchronisation, which works at first sight but is not a proper way to do it. It can easily result in a lot of time wasted with long debugging sessions, or breaking certain user configurations, and for this reason I can not recommend it.
The easiest way to know for sure you do not accidentally rely on such incorrectly synchronised behaviour is to not use halt
at all. And fortunately we can, because halt
is from a program logic point of view one of the most useless instructions that exist.
Think on a possible new multi-task OS.
Halt has no function in a multi-task OS, rather in the contrary it will waste precious CPU cycles doing absolutely nothing while it could be running some other task.
can someone be sure if Turbo-R R800 has the same behavior than the Z80?, or even the one-chip models can implement some, reducing energy and temperature.
So, in this thread about teaching people (often newbies) about C, you think it is a good idea to recommend a potentially harmful practice that can lead to broken code and lots of debugging pain for this hypothetical (I say nonexistent) benefit?
I present one simple rule: loop with a JIFFY check. This always works, no ifs, no buts, simple. By introducing halt
to the picture you create no practical added value, just complexity and risk of broken code. It’s not worth it.
Also, if you have other devices generating interrupts, what happens with line interrupts?, the code will be broken.
It all depends. Nevertheless, you must not assume that the VBLANK interrupt is the only interrupt present, it’s just bad design and a bad assumption.
It’s best to just forget about the halts, in fact stay away from them because it can make things look like they are working when they are really not, and stick to checking JIFFY only.
I disagree. Yes, working with JIFFY is fine in most cases, but there are cases when you want to use HALTs. As you said, line interrupts is one of these cases.
Anything that works in overscan mode will depend on a couple of line interrupts, so these programs will need a nice HALT loop.
Nope, in that case you still don’t want to use HALT, but another counter or flag set on the line interrupt, or (more commonly) just do it in the interrupt handling routine.
I don't see any reason why. Why should I set up a counter, when the HALT already does everything I want it to do?
loop: halt jr loop
So, what purpose does the halt
serve here? None...
From Zilog's Z80 manual:
HALT Exit
When a software HALT instruction is executed, the CPU executes NOPs until an interrupt is received[...]
So, the loop: halt; jr loop
has a clear purpose: wait until an interrupt arrives (and process it when it does), and then wait again until one of the interrupts decides to end the program. At this point the program is running completely from interrupts. Granted, not all programs will use this scenario, but it's a perfectly valid way to do things.
I don't see any reason why. Why should I set up a counter, when the HALT already does everything I want it to do?
Because halt
does not wait for A interrupt, it waits for ANY interrupt. Except in the simplest of cases and while making big assumptions on the user’s configuration, you have no way of knowing which interrupt will come next, or even which interrupts are currently active and handled by the system. Multiple interrupts can even be active at the same time and handled without you ever knowing. Halt
is inherently not a reliable synchronisation mechanism.
So, the loop: halt; jr loop
has a clear purpose: wait until an interrupt arrives (and process it when it does), and then wait again until one of the interrupts decides to end the program. At this point the program is running completely from interrupts. Granted, not all programs will use this scenario, but it's a perfectly valid way to do things.
So, how is this better than:
loop: jr loop
?
It’s not, this is why I say it serves no purpose.
Because halt
does not wait for A interrupt, it waits for ANY interrupt. You have no way of knowing which interrupt will come next, or even which interrupts are currently active and handled by the system.
Oh, but I do know exactly what interrupt will come next and which ones are active, because I've set them up during the init routine myself:
; initalize interrupts ; init: vblankoff lineinton setintline 0 set212lines spriteson sprsize16 fillvram 0,65535,0 r2v8 patterns,00000h,8*16 ; pattern data r2v8 patt.colors,02000h,8*16 ; pattern color data r2v16 map,02800h,1024 ; screen data ret
(Yes, I do use lots of macros when coding in asm)
My point is: when you know exactly what interrupts you're going to receive and when, constanly polling a memory address is unnecessary.
So, how exactly is this better than:
loop: jr loop
?
True, you have a point here. The way this loop works is different from a HALT, but in our case the result is the same.
My point is: when you know exactly what interrupts you're going to receive and when, constanly polling a memory address is unnecessary.
And my point is, in general this is a bad assumption and it is not a good idea to teach this to new programmers.
Putting aside whether it is proper to make any assumptions on which interrupts are active. In your approach, what if a single time you don't reach the halt fast enough before the interrupt occurs (maybe the music replayer gets a lot of notes at once) and all your halt syncs are now off-by-one? Or, what if you start using line ints, but had forgotten that you (or someone else who maybe wrote that library you use) was using halt to synchronize timing, leading to who knows what kinds of mysterious bugs that even an experienced programmer can easily spend an afternoon on debugging? (I've seen it happen...)
The only proper way to respond to specific interrupts is to identify it after it occurs. In an ISR you do this by checking the device flags before handling and resetting it. To be able to know this outside the ISR you need to propagate this information from the ISR into a flag or a counter. Such as by using JIFFY (for vblank).
The way this loop works is different from a HALT, but in our case the result is the same.
It's not fundamentally different, from the point of view of the program running on the main loop, halt is little more than a nop with arbitrary length.
I suposse that finally everyone uses the solution were he feels more confortable. I'm newbie as MSX programmer (started with Basic this year, jump to MSX-C a few weeks ago) but not as programmer (I'm programmer in my professional life) and I think that simplest solutions, where possible, are allways good solutions.
This is, as MSX newbie I think that JIFFY is a good solution. No need of extra complexity since your only purpose is synchronize the game. Whenever your game could do, you MUST wait VDP for next frame, so JIFFY is fine. The only scenario I can suposse where JIFFY could not be a solution is if you want skip frame in your game and the game logic continues even with frame not finished. Personally, I hate skip frame in MSX
I suposse that finally everyone uses the solution were he feels more confortable. I'm newbie as MSX programmer (started with Basic this year, jump to MSX-C a few weeks ago) but not as programmer (I'm programmer in my professional life) and I think that simplest solutions, where possible, are allways good solutions.
I’m all for simplicity and comfort, but my objection against using halt for synchronisation is that it’s not the right tool for the job, which is what I want to convey.
If you want to synchronise with VBLANK then do just that, check something that actually directly relates to VBLANK. Hook H.TIMI, poll JIFFY or poll the VDP status register, there are several options and the code is super simple.
Just don’t use halt, because its only relation to VBLANK is that in the common configuration, the VBLANK interrupt happens to be the only interrupt that’s active, but that’s where the relation ends. As soon as a second interrupt comes into play it breaks and becomes unreliable, and this may happen outside of your programmer’s control because a user could be running a TSR or some extension.
The only scenario I can suposse where JIFFY could not be a solution is if you want skip frame in your game and the game logic continues even with frame not finished.
Isn’t that just this?
while (!gameOver) { gameUpdate(); int lastJIFFY = JIFFY; while (JIFFY == lastJIFFY) { // busy wait } }
By the way, IMO it’s better to compare JIFFY against a previously read value, than to reset JIFFY to 0. JIFFY’s a nice and useful counter, resetting it to 0 all the time just breaks its core function unnecessarily I think. E.g. for 30 fps:
while (!gameOver) { int lastJIFFY = JIFFY; gameUpdate(); while (JIFFY - lastJIFFY < 2) { // busy wait } }
By the way, IMO it’s better to compare JIFFY against a previously read value, than to reset JIFFY to 0. JIFFY’s a nice and useful counter, resetting it to 0 all the time just breaks its core function unnecessarily I think. E.g. for 30 fps:
while (!gameOver) { int lastJIFFY = JIFFY; gameUpdate(); while (JIFFY - lastJIFFY < 2) { // busy wait } }
A question about JIFFY value. I suposse that it's a 16 bit value, so it's higher value is 65,535, maybe? If we don't reset JIFFY it's increased 60 times per second, 3600 times per minute. In about 18 minutes the counter will loop to 0, so the check while(JIFFY - lastJIFFY < 2) will never occur. Is that so?