-
Content Count
13,060 -
Joined
-
Last visited
-
Days Won
21
Content Type
Profiles
Member Map
Forums
Blogs
Gallery
Calendar
Store
Everything posted by DZ-Jay
-
OMG! I came up with the cheesiest, yet most effective hack to patch this bug: right before calling TRKINIT when the song end is detected, I change the return address of the current procedure to point to the end of the tracker processing, skipping completely the PSG updates. From this: ;; ======================================================================== ;; ;; TRKPATINIT Pattern initialization ;; ;; ======================================================================== ;; TRKPATINIT PROC MVI _w_var(SONG), R4 INCR R4 ; skip speed [email protected] R4, R2 ; R2 = address of 1st pattern ADDI #2, R4 ; skip instruments and drums ptr MVI _b_var(PAT), R0 ; R0 = position in patterns order table ADDR R0, R4 [email protected] R4, R1 ; R1 = pattern number TSTR R1 BPL @@pat_ok ; end of patterns ? ... ; ------------------------------------------ ; Check if the song has ended ; ------------------------------------------ CMPI #TRK_END_SONG, R1 ; ... yes : stop replay ? BEQ TRKINIT @@rewind ADDR R1, R0 ; ... no : jump to loop position ... ADDR R1, R4 DECR R4 [email protected] R4, R1 ; ... and read again ; ... We get this: ;; ======================================================================== ;; ;; TRKPATINIT Pattern initialization ;; ;; ======================================================================== ;; TRKPATINIT PROC MVI _w_var(SONG), R4 INCR R4 ; skip speed [email protected] R4, R2 ; R2 = address of 1st pattern ADDI #2, R4 ; skip instruments and drums ptr MVI _b_var(PAT), R0 ; R0 = position in patterns order table ADDR R0, R4 [email protected] R4, R1 ; R1 = pattern number TSTR R1 BPL @@pat_ok ; end of patterns ? ... ; ------------------------------------------ ; Check if the song has ended ; ------------------------------------------ CMPI #TRK_END_SONG, R1 ; ... yes : stop replay ? BNEQ @@rewind MVII #TRKPLAY.done, R5 ; NEAT HACK: Change the return address to bypass B TRKINIT ; PSG updates in TRKPLAY(). @@rewind ADDR R1, R0 ; ... no : jump to loop position ... ADDR R1, R4 DECR R4 [email protected] R4, R1 ; ... and read again The biggest cost is the switch from BEQ to BNEQ, which means that, for most times when the end of song is not found, the branch will be taken, which costs an additional two CPU cycles than just falling through as before. This seems like a reasonable trade-off for what could have been a more expensive solution. It's simple, it's elegant, it's cheap, and it's a hacky as hell! What's not to like? -dZ.
-
There is another irregularity that I found in the tracker, that is bothering me. This one manifests mostly as a side-effect of auto-conversion: if the very last pattern ends with a non-zero volume note, and the sequence ends with the "stop song" terminator (TRK_END_SONG = $F000), then the last note is sustained forever after the tracker stops playing. I'm cautious not to call these annoyances "bugs," because I think they are actual optimizations that depend on some reasonable assumptions. In this case, it seems reasonable to expect that the last note to play on the last pattern would decay completely to silence before ending the song. Under normal circumstances this is true; but when doing an auto-conversion from IBN, for instance, the IntyBASIC music player does silence the song at the end of a MUSIC STOP command, even if the last note was still playing; so there is no such assumption. The test for the song terminator is done during the processing of patterns, which happens before updating the PSG. At that point, if the end of the song is detected, the song pointer is cleared, but the rest of the data structure is not. So any active notes will still be sent when updating the PSG right afterwards. On the next tracker cycle, the cleared song pointer will cause the tracker to skip processing completely (as expected), leaving the PSG playing whatever was already in its registers. To be sure, normally, this would work because the tracker initialization routine (TRKINIT) is called when the song terminator is detected, and if the current notes have been silenced, then the PSG is updated with silence. Then, on the next cycle, the tracker will just not update because the song pointer had been cleared. Ideally, the PSG should not be updated after resetting by TRKINIT, but that requires additional code (which is why I assume this was an optimization by Arnauld). Obviously, I do not want to make the song terminator test more expensive, since it runs on every cycle. The most obvious solution seems to be to have the subroutine that initializes the tracker and PSG (TRKINIT) also initialize the song data structure. However, that responsibility is left to the subroutine that loads new songs (TRKSNGINIT). This complicates matters a bit because we do not really want to load a new song. It means a bit of refactoring of the initialization routines is in order. Anybody else has any other ideas? -dZ.
-
Entry 2020: The Pandora Incident
DZ-Jay replied to cmadruga's topic in IntyBASIC Programming Contest 2020
That title sequence in the video looks very cool! 👍 -
I think adding the jump offset in the MSB of the first word would work. I do not think it would break anything because existing songs would have an MSB of zero (since the speed is constrained to 0..4), which would default to "no jump." Any non-zero value in the MSB would be interpreted as a jump offset. Alternatively, the "no jump" value could be treated as a flag to recycle the envelope, making it repeat itself over time; or silence the channel completely until the next note event. What would happen is that newer songs wouldn't work with the old tracker. But they do not work already, since this tracker changed the data format a bit to fix some drums limitations. I'll have to see how costly this will be, since the position of the envelope is executed once per tick, per channel. Eek! 😁 -dZ.
-
Hmmm ... That is an interesting idea. The concern I would have, apart from breaking existing songs, is reducing the length of the envelopes themselves. It is now a 64-point matrix describing the contour of the channel amplitude. Removing the last decle -- which actually means removing the last four points of the matrix. That may not matter too much at the highest speed (which is 1 tick per point), but the shorter speeds are intended for very long notes, and each point is repeated over a number of ticks. Let me think about this one. It may be possible to enhance the data format in some way to support jump indices without changing the existing behaviour too much. The tracker treats drums and instruments as separate things, and each is synthesized by its own process. That limits the way that both can interact, but that goes back to how Arnauld wrote it. One of the enhancements I added was the ability to support drums and instruments in the same channel, but this feature has very strict limitations due to the way that drums and instruments work: A row can contain either a drum event or a note (instrument) event. This means that drums and notes can alternate in a channel. Only drums use the noise generator, so you cannot layer noise on a note unless you "simulate" it as a drum with tone at the right frequency. A drum sound is not "tuned." That is, it only plays as defined and has no note associated with it. This means that if you want to play a drum sound with tone at different pitches, you need a drum definition for each pitch. Drums are only supported in channels A and D -- that is, the channel "A" of each PSG. The implication of the above is that noise can only be used in channels A and D, and only as part of drum sounds. This is the standing limitation of the tracker. Part of what I wanted to do as part of the "Tracker 2.0" was to blur the line between drums and instruments and treat them just like a synthesizer would. However, I can see how this could make the sound synthesis more costly. In my opinion, although not perfect, Arnauld's approach is simple, elegant, and practical: by limiting the facilities of sound synthesis and dividing them explicitly into drums and instruments, it allows a more targeted, special-purpose implementation for each. As an example of this, consider that the instrument synthesizer doesn't have to deal with changes to the noise and tone enable flags or alterations in pitch other than the deterministic, table-driven features it supports; and likewise, the drum synthesizer doesn't have to deal with procedural alterations to arbitrary pitches, etc. Each is very flexible within the confines of its specific process of sound generation. There are two alternatives we already have, of course: Streaming PSG channel changes from data, as games like Deep Zone, etc. do; which trades processing speed for storage, and externalizes the complexity of music composition; Forgoing user-customization of instruments sounds complete, like IntyBASIC does; which offers the opposite trade off, at the expense of variety and expression. I think this tracker offers a bit of both, which is -- in my personal opinion -- a good (and perhaps superior) compromise between them. That said, since I use this tracker myself, I intend to continue enhancing it with further capabilities. So keep the ideas coming! -dZ.
-
I found another issue ... well, more of a potential side-effect. The tracker does not have any bounds-checking for envelopes, so when it reaches the end of the array, it just continues reading whatever follows (which could be another envelope, or anything else in the data stream). At a glance, it appears that the tracker expects all envelopes to end at zero volume; because when the volume of a channel goes to zero, envelope processing automatically stops. However, if you forget to end an envelope in zero (say, you were trying to make a long, sustaining envelope), then if the note lasts longer than the envelope, it will not work as intended. Note that this only manifests in special circumstances, mostly when the note is exceedingly long and the envelope is on its highest speed. I am not sure if I should change this behaviour. I could easily mask the upper bits of the counter to force the envelope to cycle, but I do not know if this is desirable at all. It could be used for repeating envelopes, though, which are not supported right now and sounds like a useful feature to have. What do you think? -dZ.
-
Agreed. A "rare" hamburger.
-
Ah, so Peter is just a victim of the Evil Lettuce, who is using him for its foul deeds. Gotcha!
-
Is that evil Peter's twin brother?
-
There may be an even simpler way to do this, if people find it useful: You could write it as a MACRO for the assembler pre-processor, and include it as a library. The only thing is that, because the IntyBASIC user functions (DEF FN) do not support assembly, there wouldn't be an IntyBASIC interface for it. Therefore, you would have to put all your graphic macros in a file by itself and include them as an assembly source: ASM INCLUDE "mygraphics.asm" As for the custom macro, I believe @intvnut created one already that takes bitmap card definitions of arbitrary width and split it into individual character cards. Take a look at the macro library called "gfx_wide.mac" that comes with the SDK-1600 distribution (the distro that includes jzIntv and the as1600 assembler), in the "macro" folder. The description of its functionality seems to be exactly what you are looking for: ;; ======================================================================== ;; ;; WIDE_GFX.MAC ;; ;; ;; ;; MACROS DEFINED IN THIS FILE: ;; ;; wgfx_start n Start a packed graphic of width 'n' pixels ;; ;; wgfx s Add an 8-pixel wide row to a packed graphic ;; ;; wgfx_flush End a packed graphic ;; ;; ;; ;; These macros allow defining graphics wider than a single tile. The ;; ;; wide graphics are divided up into 8x8 tiles left to right. Graphics ;; ;; may be as wide as 256 pixels, which is certainly overkill. :-) ;; ;; ;; ;; Graphics taller than 8px are supported. wgfx will always force the ;; ;; height of the graphic to be a multiple of 8, with the pixels aligned ;; ;; to the top of the tiles. The generated width will also be a multiple ;; ;; of 8, aligned to the left, although only pixels within the user- ;; ;; specified width will get set. ;; ;; ;; ;; EXAMPLE USAGE: ;; ;; ;; ;; wgfx_start 60 ;; ;; wgfx ".###........................................................" ;; ;; wgfx "#...#.....................................#...#............." ;; ;; wgfx "#...#.....................................#................." ;; ;; wgfx "#####.#...#..##..#.##..###..###...##.....###..#.###.##...##." ;; ;; wgfx "#...#.#...#.#.##.##...#..#.#...#.#.##.....#...#.#..#..#.#.##" ;; ;; wgfx "#...#..#.#..##...#....#..#..####.##.......#...#.#..#..#.##.." ;; ;; wgfx "#...#...#....###.#.....###.....#..###......##.#.#..#..#..###" ;; ;; wgfx "............................###............................." ;; ;; wgfx_flush ;; ;; ;; ;; ======================================================================== ;; You could either prepend all those "wgfx" calls with "ASM" in order to use them in your IntyBASIC source, or you could just put them in a separate file as I mentioned above, and include them as an assembly source. This could be included in anybody's programs, and does not require modification to the IntyBASIC source or distro. If it is useful to others, I may include it in a future release of the IntyBASIC SDK, along with the new tracker. -dZ.
-
And just to clarify, the "K" for "Kilo Words" is used in the Intellivision because the ROM is 16-bits wide, as opposed to most other consoles and video game platforms out there, where 8-bit Bytes are the counting measure. -dZ.
-
The computer is correct. It's just that 5 KB is equal to 2.5 "K" in Intellivision parlance. I was just pointing this out in case you were concerned that 5KB is too much for an Intellivision ROM, it is not. What I am trying to say is, when you hear that so-and-so game is "4K" or "8K" or "42K" in the Intellivision world, that "K" is not "Kilo Bytes," but "Kilo Words." There are two bytes per word. Thus, an 8K Intellivision game (like those from the Mattel) will report about 16KB file size. Does that make sense? -dZ.
-
Think of the Intellivision controller as two controllers in one: a data entry key pad(for menus and object commands, etc.), and a disc and action buttons (for running and shooting). Those are the use cases for which the controller was design. As others mentioned, the signals for the keypad overlap somewhat with those of the disc and action buttons, making them ambiguous to discern which one was pressed if used simultaneously.
-
Remember that when people talk about "K"s in the Intellivision world, they mean"Kilo Words" or "Kilo Decles," which is the typical 16-bit word on the platform; and not "Kilo Bytes." So, a ROM file of 5KB is still about 2.5 "Kilo Decles," which is considerably less than the common cap on the ROM map of 42K (again, Kilo Decles). dZ.
-
Entry 2020: The Pandora Incident
DZ-Jay replied to cmadruga's topic in IntyBASIC Programming Contest 2020
Wow! It looks and sounds great! Here are some comments on what you show on the video: I really like the shooting animation/sound effect combo -- they go together very well now. The "swoosh!" sound effect of the opening and closing doors is very good too. The "crunching" sound (which I suspect is to alert that a monster is nearby) is good, but perhaps a little too loud. Maybe it should be slightly fainter to fit more as a background element, especially if the creature is outside the room. This could add to the atmosphere. The laser death visual and sound effects are good. I would suggest two things to make it more interesting: 1) flicker the beams a little to make them look more like light-beams, and less like straws; and 2) add an initial "power up" sound effect, like a tone ramp to indicate that the lasers are about to fire. The monsters seem to go through the doors, because there is no opening and closing animation accompanying their traversal. If this was done on purpose, I suggest adding some sort of "matter-transference" visual effect during the transition. Otherwise, it just looks like an oversight. The "Game Over" scene is great, as well as its music. However, the normal incidental music continues to play while the player avatar is clearly collapsing to its death. This makes the transition a bit awkward. I recommend that you either change or stop the incidental music at the moment when the player starts to die, just to make the transition to the "Game Over" screen more impactful. That's it! It's looking and sounding great so far. I can't wait to play this new version. -dZ. -
Darn! Found a bug in the NPK.Note() macro that breaks a pattern when the first row is a NUL event. As a consequence, all NUL events at the beginning of the pattern are skipped. This explains some anomalies I found while tracking the drums demo, but I thought they were just quirks of my patterns. Oh well. I'll fix it today and provide an update distro. -dZ.
-
Hello everyone, I've updated the distro files on the first post to "revision #1." This revision includes a bug fix and an enhancement: FIXED: 3-channel support (i.e., disabling "TRK_ENABLED_6_CHN" for non-ECS play) was broken. NEW: Envelopes now support five speed levels, from 0 to 4. Level #4 is now the fastest, advancing the envelope on every tick. TYPO: The table describing the envelope intervals had the levels reversed, showing #0 as fastest and #3 as lowest. The documentation has been updated with the new envelope information. Thanks! -dZ.
-
Hi, @First Spear, I just created a Perl script that translates a song in "IBN" (IntyBASIC Notation) into "IMT" (Intellivision Music Tracker notation). It tries to discern patterns in the song, and deduplicate and split it accordingly; and, unless given a specific pattern length, will attempt to find the most optimal length (i.e., the one that would result in the smallest ROM data size). It is not perfect, but it is rather functional and practical as a first step. It's also a work in progress, so it doesn't yet support "MUSIC GOSUB" and "MUSIC JUMP"; but I intend to address that as it continues evolving. If you or anybody is interested in trying it out, let me know. I will publish it publicly a bit later, once I polish it a little bit more. :) -dZ.
-
Entry 2020: The Pandora Incident
DZ-Jay replied to cmadruga's topic in IntyBASIC Programming Contest 2020
Great! I'll check it out today. Thanks! -dZ. -
Blanking a sprite in INTYBasic.
DZ-Jay replied to atari2600land's topic in Intellivision Programming
Are you trying to just hide the sprite? The solution that @artrag offers is the easiest way, but perhaps a bit of context may help explain why: A sprite is only visible on the screen when all the following conditions are met: The "visible" flag of the sprite in the "X" register of the MOB is set to "true" (1). The position of the sprite is within the visible area of the screen. The card that the "A" register of the MOB points to has any pixels set. The foreground color of the sprite is different from the color of the background cards under it. By way of example, consider that the way you hid the sprite in your own example falls under the third option from the list above: the card that the MOB points to is set to a block of all blank pixels. It works, but as you noticed, it's a bit involved and expensive, and overkill. If all you want to do is "hide" the sprite, but not alter any of its existing attributes (say, keep its original position, card, color, etc. intact), then you are better off just clearing the "visible" flag in the "X" register of the MOB. However, most of the time, removing a sprite from the screen comes as a result of "disabling" or "killing" the sprite, or otherwise taking it out of commission entirely; in which case resetting the state of the MOB completely to zero will do that. It also has the benefit of being super easy to do. And as to why it works: "resetting" the sprite clears the values of all MOB registers back to zero, which in turn means the following: The "visible" flag in the MOB's "X" register is now "false" (0). The X and Y position values of the MOB are now set to zero, which falls outside the visible screen area. The card in the MOB's "A" register is set to zero, which points to the first card in GROM, which happens to be a block of all blank pixels. Notice that the above violates most of the conditions needed to make the sprite visible. As @artrag also pointed out, the "RESETSPRITE(n)" macro does this in one go by clearing all MOB registers. -dZ. -
Odd Commodore peripherals you had bitd
DZ-Jay replied to bluejay's topic in Commodore 8-bit Computers
Oh man, memories. I had Keymaster ... although all my software back then came from some European BBSs of ill repute. -dZ. -
Odd Commodore peripherals you had bitd
DZ-Jay replied to bluejay's topic in Commodore 8-bit Computers
Oh, wow, someone remade ISEPIC back in 2017! 😎 https://www.c64copyprotection.com/isepic-2017/ I may get one of those and fulfill a boyhood dream. LOL! -
Odd Commodore peripherals you had bitd
DZ-Jay replied to bluejay's topic in Commodore 8-bit Computers
Wow! I remember those. I recall, back when I was 11, dreaming about having one and all the fantastic things that it would allow me to do, and trying to figure out in my head how it could even work, like magic. Then a friend of mine got a hold of Fast Hack'Em and other cracking software from some German BBS and we cracked all the games from the neighborhood and schoolground kids in a couple of days. 😆 Awwww, those were the days. ISEPIC was quite awesome, though. :) -dZ. -
Conversion of pictures to Intellivision background
DZ-Jay replied to Vincehood's topic in Intellivision Programming
So ... are you making a game? Have you anything to show (gamewise) so far? :) -
Conversion of pictures to Intellivision background
DZ-Jay replied to Vincehood's topic in Intellivision Programming
That's very good. It reminds me of nanochess' Sydney Hunter. -dZ.
