Sunday, 25 April 2021

The Kilobyte's Gambit


Recently, I've been playing The Kilobyte's Gambit. This is an older Javascript 1K chess engine that has been put together with CGA/MS-DOS -style visuals, referring the series The Queen's Gambit. Here you play against a pixellated caricature of Beth Harmon the chess master from the show.

After winning the first game ever against it, I got emboldened and thought it was a simplistic opponent. However, later games revealed I can hardly guarantee a win and have probably lost more games against it than I've won. True, I try to play fast but even when I play slow and careful a victory is not always secured.

I was also curious if the small engine would reveal its limitations to me.

A low level player like me is pleased when the early game follows some known opening pattern, and then a comfortable middle game follows. Yet, when opening theory is discarded as anarchistically as Beth does here, then I'm faced with a difficulty: What to do when the opponent does a move I know to be "wrong" but I cannot exploit this knowledge in any way? 

Beth's approach resembles some of those dreaded "pawn-pushers" at Lichess.org. I try to play normally and soon my defences are overcome with pawns far too close to comfort. Yet I somehow know it's not a good approach.

As much as I hoped for a "silver bullet" solution that would break the computer and win every time, this doesn't seem to be the case.

At some point I tried copying the pawn movements. After creating an near-immobile wall between you and the opponent, the computer gets confused and simply moves pieces back and forth.


This gives time to pile up your rooks and material to the right edge and push through. After a couple of successes with this approach I begun to be confident that this is it. Yet as the wall is broken you are not always the one to benefit from it! Also, "Beth" may choose an opening where this isn't even really possible.

Still, are there any observations beside "play well and carefully?" One clue is in the instructions: Beth sees four moves ahead. This is enough so it won't fall for a simplest traps or discovered attacks, and unlike a beginner Beth won't be hanging pieces.

The 4-move horizon is broad enough to produce moves that can break a castling and lead to a mate. These can even appear diabolically clever.

Frustratingly, Beth never castles, which seems like another bad practice, but the engine does rather well despite of this. It knows the castling move, though, but as much as I've observed it's only done to avoid a mate.

The non-castling is one piece of knowledge that can be exploited. Checking occasionally provokes the king to move around, if there is no adequate or useful piece for blocking the attack. Furthermore the king may be lured to territory where it can be mated.

(The castling rules appear to be off: You can't castle if the involved rook is under threat, and this is wrong.) 

If Beth appears to sacrifice something, be very careful. Often this is just a prelude to a fairly useless exchange, but something worse might happen. One idea I've tried to observe is not to be too responsive to the black moves. If Beth seems eager to exchange a piece, try to come up with something else.

The engine is quite bad with end games with pawn promotions, as it might simply not see that five-six moves ahead the player can have a new queen. This suggests a strategy where the black pawns are reduced and all pieces exchanged. To make this happen is of course easier said than done.

Early in the game, Beth might even "give away" pawns as part of piece exchanges. These are not usually too dangerous to take, but the moves afterward have to be done carefully.


In the above position, the center pawn can be taken with the knight. Black takes knight and then White takes knight with the queen (check). Some threatening situations arise but as the computer doesn't see that far here these could be resolved without loss.

In the same vein there can be opportunities for forks. I cannot really fathom how the engine allows these. I suspect it might see a mate or worse in the near future, something that's not easy for the player to see, and the only way to avoid it is to permit the fork.

Clearing the field of black pawns entirely should not done lightly, as the engine seems to thrive on an open field. I guess as the decision tree becomes broader, a human can't handle it easily but it's no problem for the computer. Semi-closed positions are better as there a person can more easily see ideas beyond the 4-move limit.


Lessons learned?

I'm wondering whether it's a good idea to concentrate on this game. Does it improve chess skills at all?

I compared the engine to Stockfish levels 1-4 in Lichess, and Level 4 ("1700") was able to win it with certainty, whereas I suspect Level 3 might win it with luck. At this time, it didn't. The thing is the Lichess Stockfish easy levels sometimes do random mistakes and blunders that the 1K engine doesn't do at all.

It's a good idea to have some exercise against "bad" openings too, as there are those non-castling pawn-pushers in online play too. (I've started to suspect some of them are testing their own engines) Many chess engines don't really offer this option.

The graphics are quite unclear, which makes the game somewhat more difficult. Many times I've blundered because I couldn't tell the king and queen apart, especially when the king may move about more than the queen.

When going back to Lichess after an 1K gambit session, the graphics there look blissfully clear and spacious. Possibly, just possibly, this CGA-ordeal helps in reading the board.

Sunday, 18 April 2021

Korg Volca SysEx


The Korg Volca FM (2016) is a neat tiny remake of a Yamaha DX7 (1983) digital FM synthesizer.

The cheapness does come with some strange omissions, and the most astounding is that it doesn't understand the MIDI Program Change signal.

Also, editing the sounds fully on the Volca is not really possible. The knobs cleverly access multiple operators and the overall algorithm through a few knobs. (Edit: Actually there is a way to edit the parameters and it's not too bad.)

One selling point of the Volca is that as it's a DX7 clone, it can load DX7 patches, i.e. programs/voice data. As the Volca can receive a single-voice patch, I thought I could simply send the whole instrument data at once, and ignore the lack of Program Change messages.

This model also means the Volca can interpret all the original DX7 parameters, such as the meticulous editing of 6 separate operators, Feedback, Pitch envelope and LFO. It's just all under the hood, as the parameters are not accessible with MIDI Control Change messages.

There's an unofficial firmware update that expands the Volca capabilities in these respects, but I wanted to avoid that route as yet.

Sending SysEx

I used to think System Exclusive packets were something arcane, possibly because I didn't get them to work reliably with an Atari ST and a BASIC in the 1990s, having very little information at hand.

With better hardware and better drivers, plus all manuals and information on the Internet you could hope for, it's much more easy to experiment. It's simplicity in itself, a number of 7-bit bytes are bookended by $F0 and $F7 bytes.

Volca doesn't have MIDI out so I couldn't just sniff the contents of the SysEx package.

The device can send/receive patches using audio(!) and for a moment I thought I'd examine that format as I did with the Panasonic JR-200 tape format those long short years ago.

However, the Volca SysEx patch is well documented and there's a lot of DX7 material around on the web so I felt I should be able to pull this off without going to extremes.

Almost 100% of all Yamaha DX7 patches on the internet are in the 32-patch (4kbytes) format, whereas I'm far more interested in the single-voice (156-byte) format.

Even MIDI can easily send that much data in an eyeblink, so although I don't expect real-time editing of the voice it should be comfortable. 4Kbytes wouldn't be that slow either, as MIDI moves stuff by 31250 bits per second, with a stop bit that becomes 3125 bytes per second I'm told.

Just to give a further idea of the speed, MIDI moves roughly 52 bytes during a frame if I'm working on 60fps screen (Not that MIDI has anything to with the screen sync)  It's not a huge amount when presented like this!

Well, after a few misunderstandings and typos I could send the SysEx dump to Volca, proven by having the text display show my instrument name. By the way Volca only shows 8 characters of the 10, which can be annoying when making a disction between Trombone1 and Trombone2 and so on...

It says MyPatch1, not NyPatchI!

SysEx is initiated by sending $F0 over MIDI, and terminated with $F7. As MIDI data contents tend to be 7-bit, the data within a SysEx dump is within 0-127 range ($00-$7F).

$F0 - Exclusive status
$43 - YAMAHA ID
$00 - Global MIDI Channel (Device)
$00 - Format Number (1-voice as opposed to 32-voice)
$01 - Byte Count MSB (1=128)
$18 - Byte Count LSB (128+

... data

$F7 - End of Exclusive

I was worried that the Global MIDI channel (Device) does not really do anything. If I had two Korg Volcas in my MIDI chain, there's no software way to differentiate between the two and they would both receive the same data. So a setup with multiple Volcas wouldn't work with this approach, unless I had separate MIDI buses for different MIDI interfaces.

Well, as I don't have multiple Volcas, the remaining real problem was to decipher the patch data as something meaningful, and I didn't undertand the DX7 patch structure that well. (I used to have a Yamaha DX11 instead.)

Using Processing/Java and midibus library, the below sends a bare Sysex, excluding all the program setup and given that "output" is an already set midi bus.

I've colorized the areas to correspond with the comment lines.


Some notes on the patch structure

A human-readable DX7 patch sheet might say Frequency Coarse 1.0 and Frequency Fine 0.0, but would not tell which bytes would represent such information. In turn the Volca MIDI specification says that Coarse frequency can be described with 0-31 and Frequency Fine is 0-99, but not tell the relation to the frequency. 

I couldn't find a full "Rosetta Stone" that would solve all this, but at least the source to this DX editor  served as a starting point. Here I could already see that Keyboard Level Scale values 0-3 correspond to -LIN, -EXP, +EXP and +LIN and the oscillator mode is R/F, pointing to Frequency Ratio and Fixed Frequency.

It's worth to note that the six Operators are "upside down" in the SysEx bank, starting from 6 and ending with 1.

So, I'm onto something here, and the original DX7 manual was also helpful here.

The FM synthesis is based on the idea that an oscillator frequency is modulated with another oscillator. If you first modulate the frequency of one oscillator and then in turn use this to modulate the frequency of another, you can get quite complex sounds. There's a feedback in the algorithm too, which often provides that 'raspy' or 'crunchy' digital DX sound.

The way the 6 operators interact with each other depends on the overall Algorithm (0-31). The algorithm decides which of the operators are carriers and which are modulators. If you can't see how the algorithm is built, then editing the sound is quite pointless. The Volca of course bypasses this rather neatly.

For example, algorithm 4(of 31) means that Operators 1,3,5 are carrier (sound-generator) operators, and 2,4,6 act as modulators for the respective carriers. (6 also feeds back into itself).

Algorithms 1 and 21 (#0 and #20)

You have to refer to the algorithm chart of the DX7 or Volca, bearing in mind these are often numbered 1-32 whereas the MIDI data is 0-31. Roughly, these start from algorithms where all operators feed into each other in turn, ending with algorithms where all or most operators are parallel carriers.

Operators can feed a proportional frequency or a fixed tone. It's usually better to start from having all operators in Proportional Ratio mode.

Frequency Coarse in Ratio mode:

0 = 0.50 Hz
1 = 1.0
2 = 2.0
3 = 3.0

...

29 = 29.0 Hz
30 = 30.0 Hz
31 = 31.0 Hz

Frequency Fine (0-99) complements Coarse so that Frequency Ratio is

Coarse Freq * (1+F*0.1)

where F is the 0-99 setting.

... meaning that 0 = 0.50 * 1.99 would be 0.995hz and 1 = 1.00 * 1.99 would be 1.99Hz obviously.

It's a good idea to start with carriers that have 1 = 1.0 and modulators not too far off either. Voices easily become weird if you mess with disproportionate carriers.

Detune can add some "life" to a sound, ranging from -7 to 7 and the effect depends also on whether the operator is a carrier or a modulator.

0  = -7
1  = -6
2  = -5
3  = -4
4  = -3
5  = -2
6  = -1
7  = 0
8  = +1
9  = +2
10 = +3 
11 = +4
12 = +5
13 = +6
14 = +7

Each operator has an amplitude (volume) envelope, and these have 4 rate values and 4 level values. Instead of single decay there are two. This makes for 6*8 parameters for envelopes alone!

The scaling of these envelopes is too much to go into here now, just remember rate values are "inverse", smaller the rate longer it will take.

Rate Scaling means the envelope will be played faster in proportion to the note pitch, so if this is 7 the envelope part of the operator will be fast in any case.

When forming a sound it might be useful to first set all the attacks to 99 and all levels to 99 for all carriers, and then start reducing the effect of the different operators.

The velocity sensitivity (0-7) part is important, as this adds expressivity to a sound. Setting them all to 0 means all operators are indifferent to velocity change, which might be a good starting point. (Keeping in mind that operators also have levels).

Subtly adding values to some of the operators means that the proportion of the action taken by the operators on the sound will depend on the velocity. Bear in mind the "velocity" slider on Volca is not an overall "amplitude" but rather feeds in this 0-127 velocity value. 

The velocity data that comes in with notes does not affect this velocity. After the voice has been triggered, changing the velocity CC (41 decimal) does not change the sound already being played.

It's all so much clearer now! (sarcasm)

The verdict

As only 156 bytes are sent, I could send this individual voice data about 15 times a second without observing any kind of buffer build-up.

There's a chance the SysEx dump could even be used as a kind of in-song sound manipulator depending on where and how often notes are played. But it's probably much better idea to create sounds that are responsive to velocity and only send the SysEx patch at the beginning of a song.

Both in theory and in practice, the lack of Program Change interpretation can be bypassed using the SysEx. The practical issue remains of building a library of sensible patches to send... So I need to decipher the 32-voice banks after all.

Saturday, 3 April 2021

Sinclair QL and 1084S Monitor

For some weird reason I had believed the 1084S display cannot be connected to my computers, because it does not have the 21-pin RGB fitted in. Then one day I realised I have an cable that does the job for Amiga.

Then I thought perhaps QL should work with it too, if not with the analog RGB then with the digital. This is something I tried a few years ago, mostly by guessing, but then abandoned it as I couldn't get it to sync.

I then found these instructions which made me rethink the cable.

http://sinclairql.speccy.org/articulos/bricolaje/RGB_1084S.html

https://foro.speccy.org/viewtopic.php?f=15&t=643

I'll replicate the info here.

It turned out I needed to disconnect one wire and reconnect another in my already existing cable.

These are the female connectors at the backside of the 1084S and QL respectively:

So, to connect the QL to the 1084S a cable with 8-pin DINs is needed. 5 connections are made.

RED=Red signal
GREEN=Green signal
BLUE=Blue signal
WHITE=SYNC
YELLOW=GROUND

The picture I got was really good.

Some additional QL notes

I admit I was somewhat flummoxed when turning my QL on after this long while. But one reason for making this blog is that I can go back to old material and again remember how things work. I don't really understand my boot arrangement anymore, but at least it works.


Using a QL keyboard after a long while was not an entirely positive experience. It is far more clunky and noisy than I remembered. The basic sensitivity of the keys is ok. 

CTRL+left or right is Backspace and Delete, and this is rather intuitive as the CTRL is really close. Not having to reach further is a benefit after getting used to it. For some kind of tasks this layout can be good, but for games it's not too great.

WTV can be a helpful command if the image does not fit the screen. With the 1084S the screen can be adjusted to fit, though. MODE 0 and MODE 8 switch between the lower and higher resolutions.

Depending on the setup the drive might respond to following commands (mdv is microdrive, flp for floppy drive and so on)

dir mdv1_
dir flp1_
dir win1_
dir sdc1_
dir ram1_ for the RAM drive

load sdc1_filename loads BASIC programs, whereas exec sdc1_qed launches programs such as Qed. Using Qed, F3 enters command mode, where Q exits and X exits and saves.

Sunday, 21 March 2021

Leilei Relay


Baby UFOs in UFOland have to train arduously before they can leave their homeworld.

Historically, the most infamous of these training grounds was the Lei Lei Relay.

Collect the lanterns to advance to the next field, up until you reach the UFO cathedral. There you will attain the title of UFO master.

Guide the UFO using Joystick in Joystick port 2. Left and Right turns the ship, while Up is thrust and so is Fire. Pressing key '1' on the keyboard switches the musics on/off.

*

On and off I have been working on new Commodore 64 games. Most of them do not get much farther than early stages, so I really, really wanted to do something so small that it would get finished.

Even then, this game turned out to be somewhat bloated compared to my original intentions. So it's a small game that became slightly bigger than I wanted to.

It does have multicolor character graphics, sprites, smooth vertical scrolling, inertia, polar coordinate tables, multiple SID tunes and sound effects.


My original intention was to make a one-screen lander-type game. Some inspiration has been drawn from lander games and Space Taxi.

But it's closer to a gravity game like Thrust in the sense there's no actual landing involved. Instead, you need to collect all the lanterns from each room to proceed to the next one. The quicker this can be done, the more bonus points can be achieved. And that's it.

The weird shape of the player ship gives the game some additional spice. It needs to be turned into a vertical position to help squeeze it through some of the more narrow passages.

Unlike with Fort Django and Digiloi, there's no shooting, which makes this game simpler. But it's likely to be infernally difficult.

There's no PETSCII character graphics this time, I have some ideas about their use for other games, which may or may not be completed!


Tile map layout

As usual, I have come to trust Tiled for map-making. Instead of PETSCII I now used multicolor character graphics. The pipeline from character editing to "meta-tiles" would deserve a closer look but I'll leave that for another time. Let's just say experience in PETSCII gives a good start for optimizing character graphics, but it does have its own peculiarities.

First level in Tiled

At the center of the game routine is the scrolling screen buffer. Instead of updating screen portions piece by piece, the whole screen is redrawn for each frame.

I abandoned the character-specific colors to make this easier. This gives the game a somewhat monochromatic appearance. I tried to alleviate it by using different colors for different levels, and mixing the colors when possible.

There's a huge ordered buffer in the memory, but at least it made some other things easier for me. The level data in itself takes very little space, but testing levels and putting them in a reasonable order is time consuming so I went for a fairly low number.


More technical parts

The brutal redraw routine means I can scroll the area in whatever speed I like, and also if I draw changes into the buffer, these are rather simple to calculate, as all Y-coordinates work as increments to the HI-portion of the address. So if the first line of the buffer is at $8000, the next line is at $8100 and so on.

A new thing to me was polar coordinates and inertia, although I already made the routines for a more complex game idea, they found a better home in Leilei Relay.

The UFO coordinates are 24-bit values, but this is less complex than might sound.

24-bit coordinate:

[Low byte][High byte ][Highest byte  ]

Sprite coordinate:

          [Sprite LSB][Sprite MSB bit]
  

The high and highest byte are transferred directly to sprite screen coordinates, so that "highest" gives the MSB bit for the sprite coordinates higher than 255.

Low byte is kind of sub-pixel coordinates, as they increment over 255, the sprite has moved visibly one pixel.

For thrusting the craft forward, a polar coordinate table is needed. These are 8-bit values centred around the point 128,128 ($80,$80). They have been generated using Processing.

move_anglex:
.byte $80,$80,$81,$81,$82,$82,$82,$83,$83,$84,$84, [...]
move_angley:
.byte $70,$70,$70,$70,$70,$70,$70,$70,$70,$70,$70, [...]

So the first values are $80 and $70, which indicates 128 and 112, denoting a 0,-16 vector.

I have 256 values in the table although the UFO has only 32 visible angles. I won't go into the confusion this might create, I initially thought it would be nice to have more functional angles than visible angles, but after experimenting with it, this is really a no-no. 

It's really annoying to see the ship move to a direction it is not pointing at even if the difference is very subtle.

So, it may be just as well assumed the table is 32 values long.

Oh, and the above table is not directly applied to the sprite coordinates, but to the ship inertia X/Y values, which are in turn 16-bit values.

These again have the initial value of $8000, $8000. (low byte=$0, high byte=$80)

If the ship angle is stored in register Y, then the above table can be used to alter inertia.

sec
lda inertia_x_lo
sbc #$80
sta inertia_x_lo

lda inertia_x_hi
sbc #$0
sta inertia_x_hi

sec
lda inertia_y_lo
sbc #$80
sta inertia_y_lo

lda inertia_y_hi
sbc #$0
sta inertia_y_hi

clc
lda inertia_x_lo
adc move_anglex,y
sta inertia_x_lo

lda inertia_x_hi
adc #$0
sta inertia_x_hi

clc
lda inertia_y_lo
adc move_angley,y
sta inertia_y_lo

lda inertia_y_hi
adc #$0
sta inertia_y_hi

Sooo the $80 is first subtracted from the inertia values, and then the motion (with the $80 baked in the values) is added. There must be a more clever way but this is the one I used.

The inertia values are then used similarly to affect the ship (24-bit) coordinates.

This is for the x coordinate, for the y coordinate it's the same. It may be useful to keep them separate.

sec
lda ship_x_lo
sbc #$0
sta ship_x_lo

lda ship_x_hi
sbc #$80
sta ship_x_hi

lda ship_x_highest
sbc #$0
sta ship_x_highest

clc
lda ship_x_lo
adc inertia_x_lo
sta ship_x_lo

lda ship_x_hi
adc inertia_x_hi
sta ship_x_hi

lda ship_x_highest
adc #$0
sta ship_x_highest

The main game sprite is simple, but it does have a layer of anti-aliasing, using another sprite. These were generated using Processing. There are 32 frames, making a total of 64 sprite frames for the UFO.

For each frame, the sprite graphics data are copied from outside the video bank area to the visible sprite frame, as 64 sprites would take a huge chunk of the video bank and this didn't fit into my plan. I'm now getting to understand how important it is in C64 programming to have a good plan for locating the graphic data and the video bank.

With this technique I could have had 64 frames (128 with anti-aliasing), but I felt it easier for the player if the vertical and horizontal positions can be more clearly discerned.

The thruster flame has only one frame but it is positioned differently in alternating frames to give it more direction and a tiny bit of transparency, again using a polar coordinate shift.

Four sprites are used on the lanterns, although with multiplexing they could have used up less. But I didn't want to practice multiplexing this time, it was enough to handle the above issues and sprite-to-sprite and sprite-to-background collisions.


Wrap up

Looking at my notes, I worked on the game intensively for a few weeks in May 2020. Some of the routines had been done previously so I didn't have to "invent" the inertia, polar coordinate and precision coordinate routines then. So adding that I might have worked on this for about month.


It appears I considered a release at end of June 2020, but decided to wait. Then I picked it up on this weekend, with the idea I might be heading towards a phase of adding things to the game. 

But instead I simply cut off loose ends, adjusted some of the musics, added some graphic variety, removed anything that still looked like a bug, tested it on a real C64 and released it.

Design-wise, there was a lot that could have been added, but at this point every addition would have needed another round of testing and assessing the whole game, so it was better to keep the original promise of a "small game".

Leilei Relay at csdb


Monday, 15 March 2021

GeForce NOW

I tried this tip to run GeForce NOW on my Linux and run streamed games through the browser. To be honest I'm not sure if this is needed anymore, but I followed the instructions to the letter and now have the service.

The nice thing is that I can "buy" a game in Epic or Steam but don't have to download them.

I was motivated to get games like Fortnite and Apex Legends running, so I could have some first-hand experience about these games. Also, I could try less interesting free-to-play games like War Thunder without having to waste hard drive space.

But as I could also "sync" my existing Steam library, it might be revealing to check games I already know well.

What I noticed is apparently no Steam savegames are translated to the service, so I had to begin the games from the beginning. The other immediate thing is that the streaming computer has a different keymap, affecting some games in (very minor) ways. E.g. Fortnite has auto-run key on = but it's not at shift+0. The key is not really needed, though.

The stream picture quality with 1920x1200 is high. On occasions I tried a lower display resolution to see if it would affect the evenness of the stream, but to be honest it might not have done much.

My observations are not serious comparisons, just how I felt about it.

Rise of the Tomb Raider cutscene

Above: Streaming from Geforce NOW

Below: Running on my Linux and GTX 1060 3GB with my preferred settings.

Rise of the Tomb Raider cutscene

In this scene there is a minor difference in how the pendant casts a shadow, more hair detail, and the leather jacket is rendered perhaps slightly differently. There may be different reasons for this than just the game settings, though, e.g. driver versions and such. Oh and the local screen might be scaled up from a 1440x900 or something,  and this might already explain the difference in materials.

The basic gameplay of Rise of the Tomb Raider is so loaded with "inertia" so a slight lag didn't matter. I felt more doubtful about the quicktime events, though.

Just Cause 3 is a game which I played with some intensity last year. Arguably it has more crisp controls than Tomb Raider, but still I didn't notice any great disadvantage here. The game itself eventually crashed, I think, which is what happened few times when running on my Linux too.

Just Cause 3

I didn't do a comparison of graphic settings, but it may be that using best quality shadows helps give the above effect. I don't know, but blurred and less definite shadows may actually look better in games.

I went through a few battles in Everspace. This was quite playable through the stream. Granted, here the cursor might not exactly follow the mouse, but the ship control itself is sufficiently sluggish so it translates well to the streaming environment. 

The loading hiccups I've sometimes experienced when playing this on my computer, were completely absent on the streamed version. Full graphic detail is active with no slowdown.

Everspace

Elite: Dangerous is a good match for the service, as the game has a slower pace to begin with, and my experiences with Steam/Proton had been a mixed bag. Yes, I initially got it working via Proton, but after later updates I had difficulties in getting the game to run and have no incentive to troubleshoot it. 

After various arrangements and recovering lost passwords and verifying the account, and waiting for shaders to load and planets to generate, I got to my Elite: Dangerous via GeForce NOW. The nice thing here is that the game account is at Frontier, so I guess I am continuing my saved game. 

Oh, and the Geforce NOW tends to have DLC and additional content of the games pre-loaded so the Horizons addition was already plugged in. This is interesting, as I don't really own the Horizons content at Steam. (Horizons is what makes it possible to visit the surfaces of the planets.)

Elite: Dangerous

But so, Fortnite. Twice I tried the free variant of the Geforce service and had 200+ players in line before I could have a go. This meant about an hour of waiting. Then I paid the 6 month fee to get rid of the queue and the one-hour playing limit. The stream itself doesn't seem better, but I guess they wouldn't want to demonstrate a crappy stream in the free service.

I have now played a few hours of Fortnite. One time, the game got stuck in mid-stream, and I used the terminal to shut the browser window.

There was some choppiness as the session starts which I believe is due to the MMO logistics and not the stream really.

Apex Legends was offline and in "maintenance" which I'll have to see as a minus as it shows the service content might change on a whim.

Fortnite

This kind of streamed gaming requires some tolerance towards minor screen effects, as I wouldn't say the stream is perfectly smooth. But it's not always clear what is caused by the stream and what comes from the game itself, or even my setup.

Generally I don't see much point in running a game like Tomb Raider over the stream, if I already have it downloaded on Linux. If the streamed games are from a high-end "rig", then compared to that the 1060 already renders games rather well.

Using full graphics settings is fun to try but reveals that the additions are mostly in details I wouldn't care so much about. Granted, these are already slightly old games.

All in all there's a strange feeling this is not quite "real", but at least for trying out new games and especially games otherwise unavailable for Linux, it is a nice addition. The selection of games is not super-huge, especially at the indie end. But it's just a fact now that many major games are exclusive to different platforms and services.

Saturday, 6 March 2021

Sharp MZ800 Unicard installation


I got this Unicard for Sharp MZ-800 from Marq. The first idea was just to borrow this to see if my Sharp showed similar problems, but I became an owner in the process.

For him, the 4-colour screen appeared 2-color, plus there were other hiccups. This might have been a faulty VRAM problem, but who knows. But at least 4-color screens are possibly only for 32K VRAM. Some kind of palette glitch could not be absolutely ruled out, either.

Well, after fitting the unit this much was immediately clear: my Sharp works and the Unicard manager screen has the proper 4 colors.


But in the process I found out a thing about my Sharp I did not remember: There were already two cards inside instead of one. Though my blog post about Sharp mentions the ports I'm not sure if I understood there were two separate cards.

In addition to the disc drive controller there is indeed a serial port card that gives 2 RS-232 connectors. This is the Sharp MZ-1E24, which is probably so LOOK!RARE!!11! I couldn't even see one on eBay.


But I got more curious about why the Unicard couldn't fit correctly to the backside, and it looked like the custom fitting of the two-card system was to blame.

First I thought it was just the RS232 on-board plug getting caught in the roof of the metal sled holding the top card. Then it appeared the sled itself had become mangled, which made me really worry.


To cut a short story even shorter, turns out the RS-232 connector (Edit: not the VGA as I hastily wrote previously) at the panel doesn't fit well with the roof of the metal sled that houses the card. So the screws are impossible to fit and trying to force the card too much might break it.

After some consideration I pulled the Sharp top cover out. After all the case is modular and can be removed very easily.


Looking at the metal sled it wasn't really that damaged. Still, as the material was soft enough I beat it into slightly better shape.

As a remedy for the VGA connector conflict I cut away a portion of the sled. Computer museum enthusiasts may facepalm now.


Digging out the correct sawblades sounded like inviting a wasted afternoon so I drilled holes using a metal drill and yanked the piece out with pliers.

More advanced pliers could have cut into that material without doing the drilling. It's ugly but won't show out.


Now that I'd pulled the cover out I could conveniently pre-fit the card.


Finally, the screws fit. 

The end result is not very pretty because the already previously modded new opening below the proper expansion slot hole. 

What does it do, then? That's a story for some other time and another blog post. It does have VGA, SD-Card, probably RS232, PS/2, Internet thingy and a micro-USB.

But at least as Marq had already put the software and firmware into shape, I can play Flappy. The composite image is very solid but very "patterned". That VGA connector might be put into good use later.



Sunday, 21 February 2021

AMOS Basic on Amiga


In early 1990s, I used STOS Basic on Atari ST for a while, then went over to AMOS on Amiga.

STOS still had line numbers(!) and as far as I remember it did not have procedures. AMOS crammed together a lot of features and resembles Sinclair QL SuperBASIC in some respects.

I was spoiled with AMOS and Basic generally in that just with hitting F1/RUN my program would run in a second. So I've never been especially tolerant towards slow build times.

Blitz Basic helped make more system-friendly code but it was also somewhat trickier to get into.

Installing AMOS Pro with the compiler takes some effort but without the compiler and a hard disk I wouldn't really bother with it. 

Acceleration is very useful too, especially in a setup where a modern PC is used for writing the source and compiling it on a super-fast Amiga emulator. I'll discuss this further below.

Find all the AMOS Pro disks, then the Compiler disk images which has the necessary update/patch. Perform the update on the disk images (couldn't get it to work on an already existing HD install) and then install them to HD.


AMOS basics

The following program opens a 16-colour low resolution screen, removes the cursor and flashing. Then it sets up a few colours and clears the screen.

   Screen Open 0,320,240,16,Lowres
   Curs Off 
   Flash Off 
   Paper 0
   Ink 1
   Pen 1
   Cls 
   Palette 0,$FFF,$F00,$F
   Hide 
   Sprite Update Off 
   Sprite Off
   Print "Hello World!"
   Amos To Front
   Do
      I$=Inkey$
      If i$="q" Then End
   Loop

Using Hide, Sprite Update Off and Sprite Off together is redundant, but goes to show all this variety exists. (Hide simply hides mouse cursor)

Amos to Front brings everything on top. The Compiler can be set to have Amos in the back by default. This way I can be absolutely sure the first thing the executable displays is something I choose, and not the orange AMOS screen with flashing cursor.


Editor and procedures

Compared to modern coding environments, the editor shows relatively few lines. Another problem is that you cannot really split the source into multiple files, so it will easily grow in size. This is something I didn't care about back in the day but I finally have come to see this as valuable.

But at least it's possible to close and open procedures. This means that for a longer program, having most of the code as closed procedures helps keep the source length manageable.

_CIRCLE here is poor use of Procedure

There is some overhead for calling procedures and the compiler can't see a difference between a simple call and a structurally complex one. So, I'd avoid intensively calling procedures. A long FOR loop that calls a procedure to do a minor task can be 2-3 times as slow than one that doesn't.

For example there could be a procedure that draws a screen using 200 tiles, but these individual tiles should not be drawn using procedure calls.

Another editor quirk is the procedure and variable names are always automatically upper-cased. So it's less easy to make any useful distinction between procedures, variables, global variables and "constants" (not that AMOS really has constants).

I've done this: All procedures are preceded with _ and all global variables are indicated with G_ so that for example G_MAXITEMS is a global variable and _CLEARITEMS is a procedure.

Parameters can be passed to procedures between brackets:

    Procedure _ADDITEM[TYPE,X,Y]
        print "I'm adding type ";TYPE
        print "at coordinates ";X;" ";Y
    End Proc

These parameter names are local to the procedure, so I could call another procedure from within this procedure that also uses TYPE, X and Y (integer) variables as parameters. So far so good.

Although there's a neat way to return values from procedures, these parameters are clumsily referred by using PARAM after the procedure call. 

    _ADDITION[2+2]
    print PARAM

    Procedure _ADDITION[X1,Y1]
        RESULT=X1+Y1
    End Proc [RESULT]

So, X=_ADDITION[2+2] is not possible.

POP PROC exits the procedure. POP PROC [RESULT] puts the value in PARAM/PARAM$, otherwise the procedure returns nothing.

In 1990s I mostly used a single global variable like RES to store procedure outputs, and simply avoided using procedure results.

So, although the AMOS procedures elevate the language far above BASICs that don't have them, they are not exactly as powerful as c or Java functions.


Causes and effects

AMOS has quite rigorous syntax for if-endif structures.

I can have the normal BASIC style...

    if FLAG=0 then print "You're Dead!": print "More"

...and...

    if FLAG=0
        print "You're Dead!"
        print "More"
    end if

...is fine too, but...

    if FLAG=0
        if 
FLAG2=0 then print "You're Dead!": print "More"
        if 
FLAG2=1 then print "You're Alive!"
    end if

is illegal, as it combines if-endif and then.

So I have to do:

    if FLAG=0
        if 
FLAG2=0
            print "You're Dead!"
            print "More"
        end if
        if FLAG2=1
            print "You're Alive!"
        end if
    end if

...which is kind of clean but it can be annoying. AND, OR, ELSE and ELSE IF are helpful here, though.


"Cross-developing" AMOS

Above, I complained about the AMOS editor. But there's another way. 

After the AMOS Compiler has been properly installed, it also works from the Amiga CLI. APCmp is the command:

    APCmp source.AMOS INCLIB

INCLIB ensures the outcome is really stand alone and doesn't need an additional library file with the distribution.

Interestingly enough, APCmp can also compile ASCII formatted files, which means I'm not limited to using the AMOS editor. (Sources in AMOS editor are always tokenized).

I'll skip over the idea of using some other Amiga editor for typing the source, and instead use a modern code editor in Linux.

The downside is that the APCmp first tokenizes the ASCII source and then "compiles" the file, and the tokenization phase is what takes a lot of time.

But this is not the end. Although my Raspberry Amibian setup can be too slow for this, it doesn't mean everything is. So, using UAE in my main Linux box, setting the processor/accelerator to Blizzard 1230 IV, the resulting "Amiga" is considerably faster than the Amibian. Then the AMOS tokenization/compilation time will be shrunk to almost nothing for any reasonably sized source.


This means I can edit my AMOS source in sublime-text in Linux and compile with CTRL+B build command. This needs a Makefile/script that combines different separate files in the project folder and dumps the thing over to the UAE Amiga disk image folder. 

Something like this:

    cat *.txt > source.asc
    cp source.asc /path/to/amiga/harddisk/folder/amosstuff/source.asc

So I can have any txt file as part of my source without explicitly defining them in the Makefile or the script. 

Yes, CAT combines the original sources in alphabetic order, so if this approach is taken some care should be taken when naming files. As all the source snippets are filled with procedures, the order doesn't matter that much. 

I can still name a file aaa_main.txt if I want a particular block to come up first. One thing to remember is that global variables need to be declared before they are used.

On Amiga side I still need to run the compiler from CLI, perhaps using a short script that both compiles and runs the program:

    APCmp source.asc INCLIB WB NODEF
    source

I saved this as "build" on the Amiga project folder and then used "execute build" to run it from the CLI.

(INCLIB means the library is integrated with the outcome file. WB puts workbench back on start and NODEF means no default screen will be opened.)

Of course, it is only at this stage it will be revealed if there are errors in the source, and not inside the Linux editor. Also, the line numbers in the errors refer to the source.asc file, but it's not too hard to check that in the Linux side.

AMOS editor is still friendlier about errors and easier for debugging. But the fast speed of the UAE environment means that if the code crashes totally, it doesn't take too long to get back to the command line.

By the way, writing Processing/Java and AMOS in same session can be confusing! AMOS procedure inputs are marked with [ and ], dimensioned arrays with ( ), with Processing it's the exact opposite. With AMOS, you can't have ; at the end of the line, equality checks are = and not == and so on and on.

The subtly dangerous thing with AMOS is that as variables don't have to be declared, I can mistype a variable name and it does not register as an error.

A syntax pre-checker at the Linux end might be helpful. Also, while at it, allow more modern syntax and have procedures converted to GOSUBs or even unrolled, when possible.

So, perhaps goodbye to fiddling with Amiga keymaps and AMOS editor idiosyncrasies? Of course I'll lose that AMOS feeling. Snif.


Hiding AMOS

I already talked some about Amos to Front and hiding the "orange screen" effect that plagued many AMOS programs.

I used to be careful with hiding if a program was written with AMOS. This made sense as often the first impressions are important. If anyone saw it was a BASIC program to begin with, it might get a negative response. Although some might go "wow, was this really made in AMOS?!"


Apart from the orange screen, AMOS programs can be revealed by using Amiga-A to flip back and forth between the program and workbench, or using Control-C to break out. These might not be uncommon key combinations, but together they can be revealing. 

BREAK OFF instruction can remove the latter, and AMOS LOCK disables the Front/Back switching. The counterpart is AMOS UNLOCK. Locking might unfriendly though, considering the programs seem to multitask nicely enough. Yet if I intend to spread the software on a floppy disk image, it might not matter.

Function AMOS HERE results in true or false. With this it's possible to see if AMOS is currently in the back or not. This way the program might be stopped from using CPU too much, but I've not really tried it.

What remains is that if there is file access, and the device is not present, AMOS compiled programs will open an ugly AMOS-like dialogue box.


System friendly?

Although AMOS programs are self-contained, it's still possible to write shell-runnable programs that don't open a screen at all. The string variable COMMAND LINE$ holds the arguments. AMOS executables tend to be huge, so it's not an ideal way to write tiny commands.

Graphics is a thornier issue. In later days, more modern graphics modes came available and software that didn't play nice with these new modes were frowned on.

But Amiga 500 itself is now increasingly recognized as a retro platform on its own right, despite any newer developments. So it is possible to target the Amiga 500 and care less if the program works on some new-fangled Amiga or not.

Looking at the AMOSPro manual, there are ways to access the Amiga libraries so who knows, it might be able to set a system screen/window.

Some speed considerations

To get some more speed out of AMOS, there are some simple guidelines that can be followed.

I already mentioned the dangers of using procedures in a wrong way. I also noticed GOSUB/RETURN is faster than calling a procedure without parameters. This puts to question whether PROCEDURE should be used so much after all.

Integer variables (1,2,3,4...) should be favored. Floating/real number variables (1.0, 2.0, 3.0, 4.0 ...) are indicated with # so that variables like X# and Y# are floats and X and Y are integers.

If no complex evaluation is needed then Add X,1 should be faster than X=X+1 and Inc X should be faster still.

Adding a variable to another such as Add X,Y does not seem to slow down the computation much.

However, after compilation the difference of these tends to shrink to almost nothing. AmosPro allows compilation+running within the editor environment so comparing non-compiled with compiled sources is quite easy. Set TIMER=0 before your test loop and then print TIMER afterwards.

It can be useful to reduce multiplications and complex equations in a loop.

For example:

    For I=0 to 99
        LOC=BASE+I*32
        if peek(LOC)=1 then Inc A
    Next I

Consider this instead:

    LOC=BASE
    For I=0 to 99
        if peek(LOC)=1 then Inc A
        Add LOC,32
    Next I

Again:

    For Y=0 to 10
        For X=0 to 10
            LOC=BASE+X+Y*40
        Next X
    Next Y

This is faster:

    LOC=BASE
    For Y=0 to 10

        For X=0 to 10
            Inc LOC
        Next X
        Add LOC,29
    Next Y

Also, if a calculation like AD=AD+Y*WID needs to be done a lot, first Z=Y*WID and use Add AD,Z instead. (If Z doesn't need to change)

Unrolling is helpful in BASIC too, and the compiled code also benefits from it. So, instead of the first example:

    LOC=BASE
    For I=0 to 49
        if peek(LOC)=1 then Inc A
        Add LOC,32
        if peek(LOC)=1 then Inc A
        Add LOC,32
    Next I


AMOS and machine code

Writing 68000 machine code is not super-hard in itself, but on a complex 16-bit computer and OS like the Amiga it's a chore to get something visibly up and running.

I've used AMOS as a frame to which I can add some machine code at speed-critical points.

Just to prove it works:

   Reserve As Data 10,65536
   Doke start(10),$4E75
   Call start(10)

The machine code routine is called from the start of the bank 10, where I have reserved 64K of memory.

Then I have used doke (16-bit length POKE) to write the value that corresponds with RTS so the only thing the routine does is come back to AMOS.

This is a very silly way to insert 16-bit code. It is better to load in an output binary from an assembler:

   Reserve As Data 10,65536
   Bload "output.bin",start(10)
   Call start(10)

Assuming the binary is less than 64K long.

As the start address can be where ever AMOS has reserved the memory area, the code has to be relocatable. This isn't a huge deal, for example vasmm68k knows to produce relocatable and optimized code just as long the ORG/* statement is left out. 

Vasmm does a lot to ensure the code is relocatable, but the programmer still has to know some things. For example, it's not possible to move a label address directly to a d-register. It's possible to do move.l #label,a0 because the compiler changes it to lea, but this has no counterpart for d-registers. (Edit: This explanation is not correct, the move instructions have (pc) relative addressing modes just as lea does, it's just that in practice the compiler allowed the use of LEA without explicitly stating a relative addressing mode.)

Register values can be set from AMOS using areg(n) and dreg(n), corresponding with the a and d registers of the 68000. The dreg 0-7 can be freely used whereas a-register 0-3 are available. 

This isn't a limitation, for example areg(0) could point to a start address of a memory area that contains a large table of values you want to use from machine code. Or, the table is inside the assembly binary in a reliable location you can access from AMOS.

To give some clarity the start addresses can be variables. E.g. G_REFRESH=Start(10): Call G_REFRESH

I stored a calculation result in a variable that was apparently in 0-255 byte boundary but became something else in the assembler. I learned AMOS had signed the variable during the process. This helped get rid of the sign:

    If A<128 then A=A AND $7F: Areg(0)=A

This means that even if AMOS prints the variable content as 84, it might be internally something else. It's possible the transition using areg(0) is buggy, or I have not understood something. If the variable is directly set as A=84: Areg(0)=A there's no trouble.

Phybase(bitmap#) and Logbase(bitmap#) are AMOS variables that have the addresses of the current screen bitmaps. If double buffer is on, then Phybase will point to the start of the visible screen and logbase to the "logical", the hidden screen. Screen Swap will change these addresses.

So if you use areg(0)=phybase(0) you can transmit the start of the screen bitmap 0 over to the machine code, for custom graphics routines.

There are a few ways an Amiga can set up a bitmap screen. AMOS reserves screens bitmaps with one bitmap after another and apparently you can't tell AMOS to do otherwise. (The other way would be to store bitmaps so that all bitmap lines follow each other.)

In my experience, drawing grids of 16x16 (or 8x8) tiles with 68K is considerably faster than AMOS at the same job with a PASTE BLOCK loop. This helps a lot in purely tile-based screens.

Something I've been working on...

Other than that I don't know if it's wise to try write faster graphics routines than AMOS already does.

As an aside, I found out the hard way there is a difference between how 68000 and 68020 can handle some operations. It's better to have any word-size (move.w) operations on word-aligned addresses, (e.g 0xdf000 is okay, but not 0xdf001). 

Even if 68020 doesn't crash on these, the plain 68000 does. I spent some time wondering why particular code did work on the WB3.1 68020 emulator environment but not on the real A500, thinking I'd run out of memory or did some other cock-up.

Obviously byte-size operations (move.b) are fine on any kind of addresses. It's just that when possible, moving words and long words is much more effective.