When linking to these pages, please use the URL:
www.iki.fi/sol/ - it's permanent.
It's Ludum Dare time again, and I figured I might write a game for the zx spectrum (again) for the heck of it.
After waking up, I checked the theme which was "Shapeshift". I thought of several ideas - Ikaruga clone, a take on the lost vikings or fury of the furries where you'd just swap between characters in place.. like in trine, actually. Or how about a take on "hole in the wall" TV show. All possible, but would either take way too much time to make for the speccy. So I took a simpler idea, a twist on the ancient "robots" game.
In "robots" you have a bunch of robots that always move one step towards you on every turn - yes, it's turn based - and if two robots collide, they die. If they collide with you, you die. In some versions you can also teleport to a random location, possibly limited times, or with cooldown.
In my twist, the robots come in three colors, and you can change your color. Each color can 'eat' one color and be eaten by one other color. I didn't know whether it'd be fun or not, but it's a simple enough mechanic to write quickly.
Then I went out for a nature walk with my boy, went out to eat, came back home and (after talking it over with my wife) started working on the game.
Time between my "Trying to get a speccy game together" tweet and my "Here's my ludum dare entry" tweet is 9 hours. I also helped with dinner, ate dinner, took dogs for a walk, and performed the usual fatherly responsibilities like reading a bedtime story.
My original idea was that every color change you do is also a move, but this quickly turned out to make the game extremely difficult. Being able to choose your color freely made the game easier.. possibly too easy, but that's a better option, especially in Ludum Dare games.
For art, I used my image spectrumizer. The fact that stb_image loads photoshop's .psd files makes the art workflow really powerful. I'd draw in photoshop and have the same file open in spectrumizer on another monitor, and every time I save the file I'd see the spectrumized result instantly.
Being "robots", I only needed two sprites - a robot and the player. I tried to make the robots really angular and boxy and the player more round and smaller, but when we're talking 1-bit 16x16 sprites (and my skill level) the results may not be quite as good as I'd hoped. But I did think about it before doing the art, which is something I guess =).
Being turn based, I could have some longer sound effects. I used BeepFX player by Shiru again, this time being able to use it more properly.
Working off my speccy toolchain and various bits of routines and tools I had written for it. As an example, the sprite routines I used are total overkill for this game, as the sprites only move in 16x16 aligned positions. That also means there's 7 unused copies of the sprites (and masks) in the game. Yes, it's a waste.
Since I had all those routines to use and the turn baseness of the game meant I didn't need to care about hitting 50Hz, I didn't need to write a single line of z80 assembly to build the game.
After getting the game logic going and having the game over done and even the level progression written, I still needed to write the main menu. I started coding a window drawing routine to draw the menu on top of the game's graphics.. and then figured, this will take ages, and I have memory to spare. So I fired up photoshop again and just drew the main menu as a static image instead.
For packaging I used my Mackarel again. The game uses two full-screen images (the game background and main menu) which I told mackarel to load as a compressed low-memory block. This was much simpler than to turn the images into include files. This did lead to a slight complication, because the my default code offset is low enough that two uncompressed images didn't fit under it, so I needed to tweak it. Fortinately that didn't blow up anything, so I've apparently didn't have any (critical) hard coded addresses anywhere.
If I was really tight on memory space I could have stored the main menu in compressed form while running and decompress when it's actually needed, but at the moment the memory map shows 16k unused space.. (well, not totally unused: the non-const globals are allocated from there).
Mackarel 2.0 by Jari Komppa, http://iki.fi/sol Progname set to "RGBobots " "crt0.ihx": Exec address : 40960 (0xa000) Image size : 7550 bytes Compressed to: 4123 bytes (54.609%) by ZX7 "logo.scr" Image size : 6912 bytes Compressed to: 1956 bytes (28.299%) by RCS "lowblock.dat": LowBlock addr: 23296 (0x5b00) LowBlock size: 13824 bytes Compressed to: 4438 bytes (32.104%) by ZX7 Boot exec address: 56684 (0xdd6c) BASIC part : 51 bytes Screen unpacker : 122 bytes App bootstrap : 123 bytes (69 codec, 54 rest) "rgbobots.tap" written: 10867 bytes Estimated load time: 82 seconds (19 secs to loading screen). Memory : 0 2 4 6 8 10 12 14 16 |-------|-------|-------|-------|-------|-------|-------|-------| On load: rrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr sssssssssssssssssssssssssss..b.................................. ................................................................ ..............................LLLLLLLLLLLLLLLLLLCCCCCCCCCCCCCCCC On boot: rrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr sssssssssssssssssssssssssssLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL LLLLLLLLLLLLLLLLL...............CCCCCCCCCCCCCCCCCCCCCCCCCCCCCC.. ................................................................ Key : r)om s)creen b)asic L)ow block C)ode block H)igh block .)unused -)reserved
One of the last things I did was the different input schemes. Keyboard routines were relatively simple (and the interface-2 joystick is also a "keyboard" device), but for some reason I had a lot of problems getting kempston support going. After struggling with that for an hour or so I figured it doesn't absolutely need to be there, so I edited the option out from the main menu picture and removed the option. The problem with the kempston (in this case) is that it's a separate port to read from, and the turn-baseness of this game was achieved through an ANYKEY() check.. which, for some reason, I couldn't get working with kempston. So I cut it out.
Based on the early comments on the Ludum Dare entry page people seem to be liking it. I don't think the game has any chance of getting to the top-10, though, and I think there'll be games that are way more deserving to be there.
Someone asked for a windows port of the game. I considered this for a while, but that would probably take more effort than writing the game did, due to the way the control flow goes. It's better to just download an emulator and run the .tap through it.
Anyway, I hope you'll find it as fun as it was to write!
I finished my Steam backlog some time ago. Like many people, I had managed to generate quite large backlog of games I didn't play when I acquired them, thanks to various bundles out there. Most of these I tried for a few minutes before uninstalling, or simply archived based on the game's genre. Point is, I ran out of stuff to churn through.
Then I looked at my GoG backlog. Yeah, I have a bunch of games there, too, that I had ended up with and hadn't tried. I browsed the games and picked Advent Rising, because it looked interesting and had some positive reviews.
The game boasts having Orson Scott Card of Ender's Game fame as a screenwriter, so I had rather high hopes on the story.
As a warning, this blog post includes blatant spoilers for the game. Not that I recommend you play it, though.
The game is rather old, and definitely hasn't aged well. There are a lot of older games that look much better. The controls are flaky, the world doesn't feel "solid". The level design shows that they had issues with the controls, because apart from a couple instances of "platforming" (and even those consist of two jumps a piece), the geometry is fairly easy to navigate. There are some places where you must jump to get to a ledge, and for some reason the character's basic jump is just tiny bit too powerless for these and instead you need to use a charged super jump. The basic jump seems to be there primarily so that you have an easy way to get out from the frequent small geometry glitches.
The game is very unstable. I would chalk this up to trying to play a 2005 game in 2016, except that based on the game's FAQ the same crashes on scene transitions were in the game at release. There's other, more funny bugs too - early on in the game I was supposed to ride on a car driven by the game's AI. The AI decided to run to a wall and couldn't get out of an infinite loop of reversing a bit and then running to the wall again. So I had to exit the car and traverse the landscape on foot instead, dodging the enemies designed to attack the vehicle.
Talking of enemies, the game pretty much consists of running from point A to point B, fighting or ignoring the same bunch of enemies over and over again. There are some boss fights, couple of which can only be won by repeating a specific trick which may not be obvious (so a walkthrough is handy).
Sometimes it's also unclear what you're supposed to be doing. As an example, there's a certain type of enemy ship that you fight and/or pass by in the world, and then at some point you're all of a sudden supposed to ride one.
Towards the end of the game there seems to be some story discontinuities, with some things happening without warning or explanation. I presume they ran out of money or time and just assumed nobody would notice. According to wikipedia, the publisher had a million dollar competition for players after the release - spending that money on further development might have made more sense.
At one point when running after a bad guy a random bounty hunter appears and you end up in a boss fight. This is not foreshadowed by any way nor it is explained, or referred to, later on in the game.
One cutscene shows your character barely on his feet stumbling across a tunnel and saying he has a headache. This, too, is completely detached of the whole.
An alien brain control robot of some kind appears on one cutscene without warning and controls a big alient you end up fighting. That's the only instance of such a thing, and you have no idea what it was when you saw it the first time. Why you didn't just fight the big alien without such a brain control robot, no idea. The game throws aliens at you to kick butt without any explanation anyway, so you didn't have any expectation for an alien to be friendly.
You're captured in one cutscene and then just run around without your captors when the game returns control to you with no explanation on what happened.
And so on.
So what about the story? The story starts out strong, and then pretty much flops. Basically there's a first contact situation with an alien race, and when humans go to talk to them, they just say that they're sorry, but all humans are about to die because another alien race is coming to wipe them out.
After that it's just repetitive running around kicking alien butt. The story runs you through a bunch of samey environments interspersed with cutscenes where more or less everybody you've ever met dies. The game includes one choise near the beginning which basically says which order people will die, and the most major change in story is at the end battle, which happens after the credits have rolled. So yes, to see the effect of your choise, you need to play the whole game from beginning to end again.
Oh, and humans are pretty much demigods, their potential just needs to be unlocked somehow. After you learn a couple of psychic thingies you can pretty much just ignore the weapons dropped by all the aliens, and keep spamming force waves or whatever. The various alien species have a religion that orbits around humans, which most of them don't believe exists. There's strong hints that there's more to this in the story, to be revealed in sequels.
Yes, sequels. The game was supposed to be the first part of an ambitious trilogy, with companion novels written by Orson Scott Card. Neither of these things materialized as the game flopped. I suppose the follow up games (and books) would have revealed more about the universe and about how humans got those powers (and how the heck nobody in said universe hadn't stumbled upon said powers by accident).
Now, during my steam backlog rummaging I've gone through tons of really crappy games, but for some reason they didn't cause me to write a 800 word negative review. The reason why this game in particular irks me is the huge waste of potential. The game is about 90% there - it feels like the the second 90% is missing.
So the problem appears to be the USB ground, causing the Xenyx to boost the noise in the ground signal into the output audio signal. I figured that a reasonable workaround for the time being is to stop using the Xenyx as a USB audio device, and power the Xenyx off a separate USB power plug, instead of using the PC, and to use a separate (sub-10$) USB audio device as the second device.
This is by no means an optimal solution, but it does come with the additional bonus that now I can wire the PC's audio output to line input instead of 2-track, and thus use the low/high pass filters and panning control on PC audio, meaning that if I'm watching a particularly noisy lecture on youtube, I can actually do something about it.
Based on the research I did while hunting for a fix, a "proper" fix would be a USB separator. Or buying a more expensive mixer, maybe. Or a more expensive sound card, perhaps.
I'm titling this post as "fix v1" in hopes that there's a more proper fix downstream, but I'm not holding my breath. At least the noise is gone!
Some time ago on this blog I posted about a cheap hardware hack I used to enable several audio inputs. That was, in the long run, a rather bad solution, so I went and bought a cheap hardware mixer with USB input, namely a Behringer Xenyx 302usb. I like it, it does everything I need (more or less), but it picks up noise from my PC, and that's rather irritating.
I need two audio devices so that I can dedicate one to Windows system sounds (and other audio applications) and have one dedicated for synth output for lower latency.
I've managed with the noise by setting the PC to output audio at maximum volume and having the mixer's volume low, but there's limits to how much this helps, and I'd like to have some breathing room with the volume adjustments. Also, some games output audio at relatively low volume, so I have to increase the volume, and thus, noise, in those cases. Anyway, it gets irritating after a while.
The noise source is clearly the PC, since if I move windows around, the noise changes. However, if I plug headphones (or speakers) directly to the PC's audio output, the noise is not noticeable (i.e, the audio signal is WAY higher than the noise floor is - I'm pretty sure the noise is there if I'd up the volume enough).
There is no noise if I only play sound through the Xenyx' built in usb audio interface.
The noise is there if the input is plugged to the Xenyx' line input or 2-track input. The noise is there if I use the PC front or back audio plug. The noise is there if I plug a separate USB audio device to the PC and use that as the input, even if that device is plugged to a separate, powered USB hub.
Since the noise is also there when the audio plug is not connected, but touches the computer chassis, I suspect that the noise is coming from the ground of the PC (and that carries over through the USB hub), and the Xenyx somehow manages to amplify it. The fact that the noise is NOT there in the Xenyx USB input is a clear plus for the Xenyx.
The "obvious" solution would be to ditch the Behringer device, but I'd love it if there was some easier (and cheaper ^_^) solution.
As a voodoo thing I also tried some ferrite rings, but those didn't have any effect.
UPDATE 1: Tried taking power to the xenyx from a usb charger plug instead of the PC's usb, and noise goes away. But that way I can't use the USB device in the xenyx, so that's not a solution.
UPDATE 2: Yes, I could use another external USB audio device as audio input, and only use the USB on the Xenyx as power source. That did remove the noise and let me use a separate audio device for synths. Not ideal - ideally I'd just use the xenyx as the audio device - but works. I just need to buy a usb charger thingy to power the Xenyx, I guess..
If you've been reading these blog posts you may remember that I ordered a 128k speccy (a grey +2 to be precise) off eBay long ago. It arrived, was very well packaged and was in precisely the shape it was advertised to be in, i.e, no idea whether it works or not, but has worked sometime in the past.
Luckily, unlike some later models, it uses the same power as the 48k I have - no way I was going to use the original monster of a power brick. I powered it up.
And I didn't get a picture.
I figured that I've got this, popped the thing open, and did the same video out modification as I had with the 48k... and got no picture.
In addition to the old style composite TV plug the 128k also has something called "RGB" output, which I believe is a rather custom plug. I found the wiring for said port and a couple pins in it also has the same composite TV out signal, so I tried plugging some wires into those holes and held them to the video cable, and got an image.. completely out of sync, black and white, and what appeared to be corrupted characters in random places.
Disheartened, at this point I was ready to just sign the thing as DOA, put it on the shelf, but did a post on the world of spectrum (WOS) forums. And got a bunch of replies, explaining what might be wrong... and there sure are a lot of things that are wrong with the specific make and model of speccy that I had bought.
Among these things is the fact that the motherboard has transistors that are installed the wrong way around. At the factory. Yes. I have no idea how these things have ever worked.
Anyway, I had done enough soldering for some time at that point and concentrated on other things for a few months.
Fast forward to today. This weekend I was ill with some kind of flu, my wife was on a trip so I had to look after our son, and I had a small soldering project of fixing my wife's broken headphones, so, since I already had the tools out and was already feeling miserable, I thought what the heck, let's give it a go.
In the meantime someone on WOS had posted photos of a simple mod they had done to get the video signal working, so I figured I might as well try it. What I got as a result was the out of sync image - without corruption this time - directly out of the composite TV connector. So, progress.
Posting about my progress on WOS again I was pointed at some resources which told me that I'd only need to fix one of the transistors. I spent an hour or so trying to desolder one before giving up. I'll be the first to admit that I'm not good at desoldering. The transistor is a tiny little thing in a crowded place on the board so I couldn't just grab it with my fingers, and pulling with pliers wasn't working and I have no idea where my desoldering pump has got to, probably lent it to someone who never returned it.
Eventually I just cut off the transistor's legs, soldered short wires to the board and the remaining stubs of wires on the transistor. The result is REALLY ugly, but it did the job, and I got stable image out of the machine.
The next problem (which I had already posted about on WOS earlier and got discussion going) was getting data onto the machine. The thing is, the original speccy depended on external tape player, but the 128k one has an internal tape deck ("datacorder"). With the integrated tape thingy, they didn't think anyone would want to use an external audio source, so there's no plug for it.
I believe some later model may have that by default, but this one didn't. There's a relatively simple modification that can be done though, making the device think that the audio is coming from the "datacorder"; I did this and (after a couple of false starts) got it working. When loading data to the 48k I have to boost volume rather high for it to work; the 128k loads at reasonable volumes. I believe this is because the audio goes through the "datacorder's" amplifier.
So now I have a (relatively) working 128k +2. The audio output is crazy noisy, there's still a couple of transistors that are the wrong way around (but don't affect my use apparently). I'm pretty sure there are other issues too, but it's in much better shape than it was yesterday. Now, if I'd just get over this flu..
We've used Kahtoola Microspikes for some years now, and even bought some for some of our relatives. They are simply easiest way to survive in the slippery seasons in Finland, although you have to take them off whenever you go indoors (for example shopping). I've mentioned these on this blog before.
Now then, another relative of ours got interested in said spikes, so we promised to send a link to a local webstore that would sell them.. and couldn't find any. We did, however, find something oddly similar - "Snowline Chainsen Pro". I googled a bit and judged that these are probably some cheap knock-offs, but decided to dig a bit deeper.
The Kahtoola site lists patents. One of the patents describes the microspikes pretty clearly. The inventor is one "Jin-Hong Choi".
Snowline's CEO is one "MR. JIN-HONG CHOI". Interesting.
Googling even further I could find at least three separate websites for Snowline, with this one listing company history mentioning that in 2007 they "achieved export to American brand 'Kahtoola'." Other brands include "Mont-Bell", "Zamberlan", "Snake", "Koch Alpin", "Mt. DAX" and "Crazy Idea". Why their own brand is only now making headway internationally I have no idea.
So apparently Snowline is not a "cheap knockoff" like I first thought.
As I mentioned in the previous blog post, I made a simple shmup for the zx spectrum. You can play it here on an online emulator (link depending on the online emulator and dropbox still existing, so if you're reading this on 2025, good luck on that). The .tap file that you can use with an emulator or actual hardware can be found here.
After I had decided to start working on a simple shmup, I went to IRC and said that I need name for a cheesy shmup, stat. Pekuja said SolarGun, which sounded good enough, so I ran with it.
To make the game I used pretty much all of the stuff I've built for the speccy so far - sprite routines, Image Spectrumizer for the logo, Mackarel to package the result. Code was compiled using SDCC (small device c compiler), along with z88dk, although I think the only part of z88dk I actually used is the .tap to .wav conversion to test with actual hardware.
For sound effects, I found a neat sfx editor called BeepFX by Shiru. Unfortunately even the shortest sound effects (by modern standards) were so long that they take more than a frame of rendering time. I reduced the couple of effects I used to a bare minimum, which is kinda sad, because the BeepFX is able to generate pretty nice sounds. Maybe for a turn based game? Hm.
Here's a video of some gameplay. I didn't try for a highscore =)
The game itself is pretty simple. Asteroids fly at you at increasing pace, shooting an asteroid gives you 10 points, dodging an asteroid gives you one point. You can sustain three asteroid hits with no problem, fourth one is game over time.
Back when I was doing first tests, I found that I could, basically, memcpy 64 scanlines worth of stuff to the screen in a frame (without doing anything else). That's 2kB of data. I've learned more efficient ways of moving stuff around since, but it's still a good yardstick (especially since the emulator I was using runs z80 code a bit too fast compared to actual speccy).
So since we can only copy 1/3 of the screen in a frame, double buffering is pretty much out. To make it look like we're scrolling a big image and have lots of sprites on the screen at the same time, I cheated a bit.
The game's logo (which took all of five minutes on Photoshop and Image Spectrumizer) takes 1/3 of the screen. The way I cheated with rendering, I could have used the whole screen, but this way the play area is 128 pixels high, which helped with the player's ship math (I could do 7.1 fixed point, basically).
The scrolling background is copied to the screen interlaced, 13 scanlines in a frame (so we draw the whole background every 10 frames or so). The enemy sprites are drawn once every three frames - or in other words, on each frame, we update 1/3 of the enemy sprites. The sprites are never cleared, instead the background draw takes care of that. This also creates a kind of motion blur effect on the sprites.
This also meant that I didn't need to worry about display beam racing. The sprites still blink just a little bit when they are written by CPU and read by ULA at the same time, but there's so much noise going on that you barely notice it.
Not only are the enemies drawn every third frame, their physics is also evaluated only on every third frame. This has the additional benefit of giving more accuracy to the movement - the enemies can move 1/3 of a pixel per frame without having to use fixed point math.
The player's ship - the focus point of player's attention - is drawn on every frame.
So in practise you're playing three separate games running at third of a full framerate at the same time, which is pretty convincingly 50Hz.
I timed things so that when you have the full set of sprites on screen we're still running at 50Hz, but whenever sound effect plays, it pushes the timing over the frame. The sound effects are pretty rare, though.
I use color attributes for a slight "vignette" effect, with darker colors on the edges. This is partially because I didn't want to handle sprite clipping with edges, and partially because otherwise things would have been (even more) monochromatic.
The attributes are also used for "explosions" (drawing a red blob around where an asteroid is cleared) and the player's shots. The player's gun is a slowly recharging mega-beam which slices through everything. There's probably a bug with the hit detection because the beam doesn't always wipe out asteroids it should.
Whenever either of those attribute effects are used, the next 16 frames are used to clear the attributes back to the original state, 1/16 at a time. This also creates the nice "blider" effect on the beam.
Gun charge meter and user's lives are also done with simple attribute changes.
The score.. Oh right. Printing out the score is a surprising problem - you see, there's no division (or modulo) opcode on the z80, so if we wanted to simply sprintf the score, that would probably eat significant amount of our frame time.
Enter binary coded decimal, a numeric encoding format so common that there's often special opcodes in various CPUs for it. Simply put, for each hex nibble we store a value from 0 to 9, and the rest (a-f) are illegal. If a nibble's value is over 9, we add 6 to the value, to roll it back to the legal range. In case of overflow, the next digit is incremented by one, and then we have to check if that is in the legal range, etc.
While relatively complicated (depending on how many digits you want to handle), it's still way cheaper than dividing by 10 a few times. The z80 also has an opcode for doing all this - DAA (decimal adjust after addition). Instead of using that, I wrote the same kind of logic in C. Probably a huge waste, but fast enough for this project.
Looking at the Mackarel memory maps, the game takes quite a lot of the memory, but there would still be plenty of room for more stuff if wanted:
Memory : 0 2 4 6 8 10 12 14 16 |-------|-------|-------|-------|-------|-------|-------|-------| On load: rrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr sssssssssssssssssssssssssss..b.................................. ................................................................ .....................LLLLLLCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC On boot: rrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr sssssssssssssssssssssssssssLLLLLLLLL............................ CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC.................. Key : r)om s)creen b)asic L)ow block C)ode block H)igh block .)unused -)reserved
The low block (L) contains the game logo. It is simply copied to video memory when the game starts. If there were more than one type of enemy at once, I could store the sprite data for various levels there. The memory below the 32k limit is considered "slow", because it is contended, and thus it's preferable to keep all code and data we want to play with above that limit. As a recap from earlier blog posts, accessing contended memory while the screen is drawing incurs at an additional speed penalty.
There's also some room above the code block (C), meaning that there's still room to add code before I'd have to start worrying about size optimization.
As before, all of the sources are available on github.
Over the new year's I decided that I wouldn't try to make "the best thing ever" for speccy and instead would make and release smaller things. To get things rolling, I decided to create a simple shoot-em-up, and while doing that I covered a couple of little details my earlier experiments had not, so let's look at these.
First bit is interrupts. The z80 has three interrupt modes; IM 1, which is on by default, hops into fixed address in ROM, IM 0 executes opcode from data bus (which may be whatever) rendering it completely useless, and IM 2 hops to an address in a lookup table indexed by a byte from the data bus (which may be whatever), which is awkward but not completely useless.
We could leave the interrupt to point at ROM, but since it does things like scanning the keyboard which takes variable amounts of time depending on what keys are pressed, and since we'd rather not have the ROM poking at RAM addresses, it's better to turn it off.
We could just disable interrupts and leave them disabled, but then we'd lose frame sync which isn't so nice. Okay, we could count instruction clock-cycles for the whole frame, but that would be insane.
So we're down to using the third interrupt mode.
The way the third mode works is that you create a table of addresses where the interrupt should jump, set one register to say where the table is, and then change the interrupt mode. Whenever the interrupt happens (near beginning of vertical retrace, or 50Hz), the z80 reads a byte from the bus, uses that as index to the table, and hops there. Another slight problem is that the addresses are 16 bit, but the index may be odd or even, meaning that in practise your interrupt address must be of form 0xXYXY - both bytes being the same.
The zx spectrum 48k ROM has a large chunk of 0xff:s in it. I presume they didn't need the whole 16k of ROM, and the unused bit was left to 0xff. Whatever the reason, there's a fun little trick we can do on the 48k speccy.
ld a, #0x39 ; 0x39xx is a rom address with lots of ff's in 48k speccy ld i, a ; set i ld hl, #0xffff ld (hl), #0xc9 ; 'ret'. Should be reti, but that's 2 bytes, and we aint got room for that. im 2 ; interrupt mode 2, which should now hop to 0xffff which has our ret instruction.
First we set the interrupt jump table address to point at the large chunk of 0xff:s, causing the interrupt to always jump to the address 0xffff. Then we poke the opcode for 'ret' to 0xffff, and finally we set the interrupt mode to 2. After this, whenever the interrupt happens, it just does 'ret'.
What we should be using is 'reti'. Unfortunately, opcode for 'reti' is 0xed4d, so it won't fit in our single byte. Another sneaky thing that could be done is relying on the fact that the memory loops, and put a partial opcode at the 0xffff address and use the first byte of ROM as the second part. If the resulting opcode is a relative jump backwards, we can then put whatever we'd want there.
The difference between 'ret' and 'reti' opcodes is that 'reti' is also decoded by a bunch of z80 peripheral chips, but that doesn't seem to matter on the zx spectrum, so we're fine.
Now that the interrupt handling is done, the next bit that was missing was user input.
If you look at how the speccy keyboard works, there's a 5-wire connector and a 8-wire connector to the keyboard membrane, and there are exactly 40 keys on the keyboard, split into eight 5-key groups.
These wires are connected to the port memory space on the z80, meaning we have eight i/o ports from which to read, and the bottom 5 bits of each of those ports represents a key on the keyboard. So, to read the whole keyboard state, we need to read from 8 ports.
If a key is down, its bit is zero. If a key is up, the bit is one.
Note that the bottom 8 bits of the port address is 0xFE, or 254 - yes, the same port where we write audio and border color.. everything is basically hooked to the same i/o port in the spectrum.
I wrote a simple inline assembly function that reads all those ports and puts the data into a byte array, and then through some C preprocessor trickery my keyboard use boils down to:
if (KEYDOWN(SPACE)) fire();
But wouldn't it be nice to be able to control a game with a joystick instead? Unfortunately, because spectrum didn't come with a built in joystick port, there are at least four joystick standards for the spectrum. Two of these are the most prominent: the "interface-2" joystick and the "Kempston" joystick.
The interface-2 joystick was Sinclair's definition of the joystick port. Its input maps to keyboard ports directly (more specifically the numeric keys), so it's relatively easy to support via the keyboard code. The joysticks themselves used the same connector as the very widely used "Atari" standard, but the pins are wired differently because Sinclair wanted some vendor lock-in. Sigh. Anyway, the later "big" model speccys also have these ports built in.
If I ever get to fixing the 128k speccy I bought off ebay, I'll have to make a atari-speccy joystick port converter.
The Kempston interface uses the standard Atari pinout, but doesn't map to the keyboard. Instead, the Kempston state is read from port 0x1f, again five bits for left/right/up/down/fire. To support Kempston, I simply added another byte to read in my keyboard update function, and reading the joystick is just a matter of, for example, checking if "KEMPL" key is "down".
Since reading from random unconnected ports may result in any kind of result, one can't simply just assume that there's a joystick there and read all of them (and some incompatible joystick standards actually overlap, if a completionist wants to support everything out there), so the game needs to prompt for input preference from the user. A slight bummer, but users are totally used to that.
A while ago someone mentioned that I haven't done a recent search terms post in a while. This is true. I figured I'd do one now, and looked back to see what dates I should include in the search, and was surprised to find that I didn't do a RST post last year at all!
Google has become depressingly good at filtering funny search results from the reports, however. Google analytics only gave about 600 keywords for the whole year(!), and from them, I only found the following mildly amusing ones.
You can find collection of older RSTs at this rather long page
avr circuit simulator for ubuntu atanua
Sorry, never got around to implementing it, even thought I got sources to an avr simulator and license to use it.
common stories of sol
I guess I do repeat myself sometimes.
concept of lookup table in graphics tutorial
Concept. Okay. First, calculate something. Then, realize you're calculating that exact same thing a lot. So how about storing the value and reusing the result. But you have a range of values. So instead of storing one value, you store a table of values. And that table is a lookup table. Not really limited to graphics, though. Common lookup tables in graphics (historically) are color lookup tables, screen offset tables, and trigonometric tables, such as sin and cos, for old enough machines where calculating those is really expensive.
death rally creators dos
That would be remedy entertainment.
how to make a audio engine in c++
Through a long, tedious, but also rather interesting learning process.
how to port dos program to windows
Learn DOS programming. Learn Windows programming. Not necessarily in that order. Now you know how.
how to use escapi
Look at the example source code, maybe?
want to make a mmo how to get started 2015
Same way as before: learn absolutely everything about everything first. And/or become rich.
webcam dll multi device support
Depends on the make and model of webcams. Some work, others don't. Most people don't use multiple webcams at once, and thus the developers don't always expect that.
white magic spells that work
why are busses so loud
They are relatively heavy vehicles, and thus require rather a lot of energy to move, and acoustics isn't the primary concern. While I do believe that it would be possible to design a bus that's fairly silent, I trust that people would prefer just getting whereever they're going at a cheaper rate than ride on a bus that doesn't make so much noise.
After playing around with compressed loading screens and compressed application images, I ended up with a monstrosity of a build scheme.
It's no wonder most people don't bother with the compression..
On the left side of the graph you can see an example of a relatively complex project with couple C and couple assembly files being first compiled with their appropriate compilers and then linked, resulting in an intel hex (ihx) file. This file is typically converted to binary and then run through some .tap making utility, and we're done. Well, done-ish. But if we want a loading screen, and want both the loading screen and the application image to be compressed, things get a bit hairier.
First, you'll notice that we're compiling the hypothetical "unpack.asm" twice. This is because they're not position independent, and will have to run from different offsets. The compiled decompressors and then binarified and copy /b:d to a combined binary file which can then be tapified. On the loading screen side things are slightly more complicated because we need a specific bootstrap written in spectrum BASIC and the compressed loading screen image itself, but eventually we get a tap file from that branch as well. These tap files can be joined with another copy /b command to get the final tap file.
What the graph doesn't include is that since you pretty much always end up with compressed files of different sizes, you also end up with different loading offsets, which may mean patching the compiled binaries, or recompiling the boot-up assembly after manual changes. And you also need to care about all the offsets you want to load things into, and have to update build scripts and whatnot whenever these change.
Or you can do something a little fishy.
Introducing Mackarel, the one-stop solution for neat zx spectrum releases. Mackarel converts ihx files, generates the needed basic, generates loading screens if need be, compresses, patches binaries, and generates the final tap file in one swell swoop.
There's a couple different compression algorithms to pick from, but the defaults are picked so that you don't really have to care about it. If your compile chain doesn't generate ihx files, you can also feed binaries to Mackarel, but then you have to give the start address of the binary too.
On a more technical side, the compressed loading screen and combined basic bootstrap works pretty much the same way as described in my earlier post about compressed loading images, except that everything is self-contained within Mackarel and you don't need to call any compressors, etc. yourself. If no loading screen is provided, Mackarel will generate a simple one for you which typically takes, along with its decompressor, about 250 bytes.
The application image itself is compressed and loaded to the end of the memory space. Upon launching, the bootloader first copies itself to video memory, and also sets stack to point there. Running from video memory, it decompresses the application image to the desired start address, restores stack and hops to the beginning of the image. Assuming the application doesn't do anything wonky, it's clean to exit back to basic if you really want to.
As before, all of the code is available on github.
And the Pouet thread, too.
Haven't written a breakdown as of yet. Any interest?