172 karma · joined September 6, 2013
I think I can do a little bit better; maybe 10% faster.
https://www.theexplodingwhale.com/evidence/resources/dave-ba...
Though, to be sure, most often TRS-80 is used to refer to the Model 1, 3, 4 line. But plenty of people will think of the Color Computer when they hear TRS-80. Or, possibly, their line of rebranded pocket and laptop computers like the TRS-80 Model 100 or TRS-80 Pocket Computer 1.
The Model 1 version does require a hardware mod to get access to vsync.
I wrote those programs and couldn't have done it without something of a virtuous circle of trying stuff on the real hardware and then improving the emulator to more accurately model the hardware and so on.
To cite a reference, the first issue of "80 Microcomputing", a magazine dedicated to the TRS-80 line of computers. See the second paragraph of Wayne Green's editorial "80 Remarks" on page 8:
https://archive.org/details/80-microcomputing-magazine-1980-...
You can see this with today's word by guessing MOTTO. The leftmost T will be yellow and the rightmost will be black.
Similarly, if you guess TENET the initial T is black but the final T is green.
Today's word is ZBVFG (rot13).
PMESS LD A,(HL) ; get character from message
INC HL ; move message pointer to next character
CP 10 ; newline?
RET Z ; return if so
OR A ; zero?
RET Z ; done if so
CALL PCHAR ; print character
JP PMESS ; keep looping
If instead of "RET Z" we had to do a conditional jump to a return it would be 10 cycles for each test instead of 5. CP 10
JP Z,DONE ; 10 cycles, jump taken or not
...
JP PMESS
DONE: RET ; 10 cycles, BTW
The conditional return just happens to be cheaper if not taken because it skips the work of popping the return address off the stack. Though purely an outcome of the implementation you can treat it as sort of a branch prediction.Incidentally, the Z-80 also has relative branches (JR) that differ in execution time whether they are taken or not. The branch offset is a single byte so JR is only 2 bytes compared to JP's 3. A JR is 7 cycles if not taken, 12 otherwise. Again, we can treat it as a hard-coded branch prediction that predicts the branch is not taken. If space or distance to target is not a problem, a JR is faster if taken less than 60% of the time.
The 6502 has only an 8 bit stack pointer which points into page 1 only (addresses 0x100 .. 0x1FF).
Usually out of pride or the sunk cost fallacy (or something like it) I'll convince myself there was no other way the problem was going to be solved. Either way the next time around I spend just a little bit longer trying to think of an easy way out.
It did serve as a back-end search for Yahoo! though it never could keep up with the full query volume. It was lambasted by the creators of Google for experimenting with putting clearly-marked ads in search results. That transgression looks hilariously quaint to modern eyes.
https://www.referenceforbusiness.com/history2/72/Open-Text-C...
Just don't bother with these as they've already been done: https://www.dropbox.com/sh/yqpc3bglatpmpvl/AACA2CXFv1XeA1ZmL...
The TMS34010 was something like a CPU and 2D graphics processor combined. It has the ability to change its word size from anywhere between 1 and 32 bits with data addressable on bit boundaries. It's wild.
Like many games at the time the code in the ROMs had tamper protection. The basic idea is to detect if the ROM has been altered and if so assume it is a pirated copy and do something to cause the game to fail. Pirates could still make exact copies but they'd have to retain copyright notices and trademarks which makes them more vulnerable to detection and prosecution.
At the last minute Dave changed the position of some logos on screen but forgot to update the copy protection. When an illegal copy was detected the game would use some of the digits of the score obtained at the end of the game to zap bytes in memory if the overall score itself was within some range. This kind of random scrambling that only happens semi-randomly is better than copy protection that triggers deterministically. Imagine if the game did a checksum of its ROM and immediately said "Illegal Copy Detected - Program Halted". It would make it easier to find the copy protection routines and disable them.
So the first version that shipped thought it was an illegal copy and triggered the random scrambling. It so happens that sometimes the random scrambling would result in giving the player 40 credits.
There were other effects which players discovered and are listed here: https://forums.arcade-museum.com/archive/index.php/t-79058.h...
Mostly it was applying higher-resolution tiles and sprites but some had particle effects added. For instance, some exhaust smoke on the cars in Frogger.
My favorite was Gyruss which is somewhat like Space Invaders of Galaxian but with a 3rd person view from behind the player's ship so you see enemies come toward you in pseudo-3D. We found that the game internally used polar coordinates and had a fair bit of code that converted those 2D and chose the right sized sprites. Replaced all that with 3D models rendered at the same spot in space.
I view these techniques along a continuum where you're essentially changing the porting layer. An emulation puts that layer at the hardware. A hybrid approach pushes it some ways into the game. A source code port is above the binary but some ways into the code. A remake is at the "game design" level.
Edit: I think I've found it: http://www.gweep.net/~shifty/portfolio/oddsvf/index.html
I guess "ugly" is a fair assessment of the 8086 at the time. Certainly the 68000 was a much cleaner and orthogonal architecture. On the other hand, I'd rate the 8086 at least as good as if not better than other contemporary microprocessors such as the Z-80 and 6502. The 6809 was sweet but a 16 bit address space rooted it in the previous direction and the 68000 make it clear that the 6809 wasn't in Motorola's future plans. Sure, had IBM chosen the 6809 there surely would have been a compatible follow-on but I can't imagine even the stanchest IBMer to have that kind of hubris.
But calling MS-DOS ugly at the time would have been unfair. It was as capable as any other microcomputer OS at the time in the home computer space. It was widely proclaimed to be a rip-off of CP/M so we might take that as a compliment and if you look at TRS-DOS, Apple DOS or whatever PET's used it was just fine. It'd be unrealistic to suggest and mini or mainframe OS was an option and Unix just wasn't there yet. If IBM had given Microsoft more lead time they might have went with Xenix which they did have out in 1981 for the Z-8001. I'm not so sure IBM would have been interested, though, as they wouldn't have an exclusive license to the OS.
Not to mention that the overhead of the operating system was an important consideration. The machines didn't have much capacity to waste and whatever you picked it still had to perform well on a system with only floppy drives. Maybe that in itself doesn't rule out Unix but it sure cramps the design space.
In both cases, CPU and OS, the ugliness really took off with backward compatibility to maintain. The 80286 was already being designed so it drove that deeper into the weeds and there was no way of bypassing MS-DOS compatibility once it anchored the marketplace. The only way forward was to improve the OS while keeping MS-DOS programs running and the whole OS/2 debacle only helped to delay that upward path.
I mean, fair enough to say "ugly won" but some consideration should be given to the lay of the land when these long-term trends were set in motion.
The auto-increment/decrement addressing modes were aware of the bit size. Thus you could have polymorphic subroutines where the same code could be used to sum an array of bytes, words, 13 bit quantities or whatever size word you wanted (up to 32 bits).
Since the processor had built-in circuitry to help drive graphics displays it took a high input frequency of something like 40 or 50 MHz and actually divided that down to run the processor at around 10 MHz or so. The opposite of what we're used to now and made the darn things look scary (well, scarier) from an emulation programmer's perspective.
The memory interface was designed to work with shift-register VRAM. The idea there being that the VRAM chips had a built-in shift register 512 pixels wide. The display circuitry would make use of it by having each line from the frame buffer dumped into the shift register as the raster was moving down the screen. And then the shift register would clock out the pixels as the raster moved across each line.
During VBLANK (when the video circuitry is waiting for the raster to return to the top of the screen) you could use special instructions to load and store the shift register which would be as fast as any load/store operation. The entire frame buffer could be filled very quickly with any repetitive pattern or erased entirely by copying some fixed line to all the others.
Lotta cool stuff in that beast.
The point being that there is a bit of a tradition of using "." for "the current thing" so there's a sort of sense in using it for the most recent command.
If you're really curious get in touch with Bill Joy.
Mirrored memory is a side effect of unconnected lines on the address bus thus making the content of those bits irrelevant. Code can take advantage of this to run faster or put tag values into addresses.
On a GBA, VRAM is faster than ordinary RAM. Programs can do well to use it for tight inner loops.
Using STM (store multiple) to DMA registers? Again, go faster.
Save type masquerading might be code that helps when running on a development kit, but I admit that I can't think of what use it might have.
Self-modifying code that depends on the pre-fetch queue might be the best place to look for intent. Might be easy to tell if the program is doing it for some larger purpose or simply to fail subtly or overtly if it sees unrealistic processor behavior.
Any why would a program do extra work writing to an audio FIFO than need be?
http://donhodges.com/how_high_can_you_get2.htm
I made a much less serious patch -- a backwards-facing Pac-Man that places dots instead of eating them:
frac = 0;
while (len > 0) {
for (frac += scale; frac >= 1; frac--)
*dst++ = *src;
src++;
}
Instead of repeating the work of the inner loop every time, generate the code that has the scale baked in. For instance, doubling the image would output this code fragment repeated for the width of the source image: *dst++ = *src; *dst++ = *src; src++;
And scaling down by half would be: *dst++ = *src; src++; src++;
Though whether this is faster or the best technique really depends on the processor. It might be just as easy to pre-compute a lookup table pointing to the offset of the source pixel for each destination one. That's something an 8086 could do fairly easily and maybe a 6502, but not so much for a Z-80.