You can probably guess what the game is :)
So I thought I'd use this built in compiler and see what it could do. I'd do two things with it, move the objects on the screen, and collision detection. Steering code and so on can be BASIC.
This is doing about 30fps with 25 asteroids, and it's 9 lines of actual code. A fair amount of that time is the actual drawing bit. Code could definitely be clearer, but still getting a feel for it. It's pretty ineffecient code ; (it assembles to about 300 words), because the CPU isn't really designed for Threaded Languages ; it can do it fine ; it doesn't have a push/pop or a load/save with auto increment. At the moment it's using one register for TOS and keeping the rest on the stack ; that was probably a mistake. I'll probably rewrite the library at some time (there's not very much of it).
These are bitmaps. The blitter isn't great at drawing lines ; it's not terrible by any means (especially when parallel to an axis), but it's not really its strong point.
Saturday, 18 April 2020
Friday, 17 April 2020
Insert
Something I should have done ages ago, to allow the screen editor to insert spaces, at present it operates in overwrite mode.
This doesn't matter too much because spaces are not tokenised, except in string constants, so if you put 100 X = 42 into it it will list it as 100 x=42
But it was a pain when editing programs.
This doesn't matter too much because spaces are not tokenised, except in string constants, so if you put 100 X = 42 into it it will list it as 100 x=42
But it was a pain when editing programs.
READ/DATA/RESTORE belong with GOSUB and GOTO
....are not implemented. Deliberately. I did implement GOSUB GOTO and RETURN but under protest :) and it's bad, the RENUM command ignores them so if you renumber a program with GOTO and GOSUB in it won't work. There is actually an ON token but it doesn't do anything other than generate a syntax error.
But still one needs data. So I borrowed a concept from AppGameKit BASIC, which has substring functions, so that you can pass in CSV data (or seperated with any single character) e.g.
12/23/42/61
and there are two functions to split it up. One returns the number of substrings, sub.count(a$,"/") (obviously 4) and one extracts a substring sub.get$(a$,"/",2) so you can store your data in strings (or just in the program code) and access it this way.
But still one needs data. So I borrowed a concept from AppGameKit BASIC, which has substring functions, so that you can pass in CSV data (or seperated with any single character) e.g.
12/23/42/61
and there are two functions to split it up. One returns the number of substrings, sub.count(a$,"/") (obviously 4) and one extracts a substring sub.get$(a$,"/",2) so you can store your data in strings (or just in the program code) and access it this way.
Thursday, 16 April 2020
RPL , working towards 0.80
So, this morning I've written all the words except the variable access words and the structure words. Onwards and upwards.
After that I will write another game from my list and try using it in the wild. Something that moves quite a lot of individual things very quickly. None of the games have done yet - Frogger / Invaders probably look as if they do but it's a cheat :) Anything with regimented groups uses tilemaps.
L8R: Added the variable access, still no structures. You can see what it looks like in this picture, this code is equivalent to the following FORTH.
: star 42 emit ;
: demo star star cr test @ . 23456 test ! cr ;
The variables are different. The ^test and &test refer to specific operators.
SYS has been amended so that as well as assinging to CPU register R0 the address of the A-Z variable block, it also initialises R8 to the to of the small stack allocated for RPL.
Which reminds me. A big missing thing that I keep meaning to fix is the insert space on the screen , at present you can only overtype. This won't be full insertion, probably, but it will allow the use of a key (probably Ctrl+E) to make space in the current line.
L88R: Changed & for technical reasons. It's a special token that is connected to the constant, confusing the lister/tokeniser So it is now '^' ... except that didn't work either. Discovered a bug in the tokeniser where it was thinking ASCII codes higher than 'Z' were numbers.
L888R: RPL is done, probably add a few words here and there, but as originally designed , it works.
After that I will write another game from my list and try using it in the wild. Something that moves quite a lot of individual things very quickly. None of the games have done yet - Frogger / Invaders probably look as if they do but it's a cheat :) Anything with regimented groups uses tilemaps.
L8R: Added the variable access, still no structures. You can see what it looks like in this picture, this code is equivalent to the following FORTH.
: star 42 emit ;
: demo star star cr test @ . 23456 test ! cr ;
The variables are different. The ^test and &test refer to specific operators.
SYS has been amended so that as well as assinging to CPU register R0 the address of the A-Z variable block, it also initialises R8 to the to of the small stack allocated for RPL.
Which reminds me. A big missing thing that I keep meaning to fix is the insert space on the screen , at present you can only overtype. This won't be full insertion, probably, but it will allow the use of a key (probably Ctrl+E) to make space in the current line.
L88R: Changed & for technical reasons. It's a special token that is connected to the constant, confusing the lister/tokeniser So it is now '^' ... except that didn't work either. Discovered a bug in the tokeniser where it was thinking ASCII codes higher than 'Z' were numbers.
L888R: RPL is done, probably add a few words here and there, but as originally designed , it works.
Wednesday, 15 April 2020
FORTH yet not FORTH
Have a working RPL compiler as a unary function, but not a dictionary yet. Just a few test words. It's very , but not exactly like FORTH. The main obvious difference is in the variables - these are prefixed with ^ and & ; so &count is the same as count @ and ^count is the same as count ! in FORTH. This is because it has to work with the BASIC variable system.
You also have a static read-only dictionary - word addresses are stored in BASIC variables (hence the ^ and & operators !), and can't (at the moment anyway) define immediate words. The structures will be simplified. But it is recognisably the same.
One thing which is actually a plus point is it can't split a definition over two lines of code (you can have a very long screen line). This has the benefit that it almost forces you to break up definitions into smaller ones rather than writing whole page definitions, which I kinda think you should be doing anyway.
There is a comma, because the syntax parser and lister is designed to deal with BASIC and the comma compiles nothing but can be used to clarify the syntax e.g. do you want +! or + ! ; by writing +,! it becomes simpler. It is an issue because the spacing is done entirely by the lister, no spaces are stored in the tokenised code. It's not like Microsoft BASIC, if you look at it as characters it's largely unreadable.
You also have a static read-only dictionary - word addresses are stored in BASIC variables (hence the ^ and & operators !), and can't (at the moment anyway) define immediate words. The structures will be simplified. But it is recognisably the same.
One thing which is actually a plus point is it can't split a definition over two lines of code (you can have a very long screen line). This has the benefit that it almost forces you to break up definitions into smaller ones rather than writing whole page definitions, which I kinda think you should be doing anyway.
There is a comma, because the syntax parser and lister is designed to deal with BASIC and the comma compiles nothing but can be used to clarify the syntax e.g. do you want +! or + ! ; by writing +,! it becomes simpler. It is an issue because the spacing is done entirely by the lister, no spaces are stored in the tokenised code. It's not like Microsoft BASIC, if you look at it as characters it's largely unreadable.
Doing things as and when.
Well, every, after and cancel work as the gif on the right shows. They seem to be fairly reliable, though I haven't bullied it too much yet.
It's deliberately made not reentrant, so if you call a routine it can't call another one until you return from that. If you delay it while it does a lot of work it just queues the events up for later. I thought that was safer rather than just letting it do what it wants, which the Amstrad version does, as far as I can see. The underlying code allows any number of repeats, so I may extend it so you can say "after 150,2,5 call code.routine()" which will call it 5 times at 1.5 second intervals.
I also added @ as an operator returning a variable data address, because I'd added it as a token for RPL which I want to keep as FORTHesque as possible.
It's deliberately made not reentrant, so if you call a routine it can't call another one until you return from that. If you delay it while it does a lot of work it just queues the events up for later. I thought that was safer rather than just letting it do what it wants, which the Amstrad version does, as far as I can see. The underlying code allows any number of repeats, so I may extend it so you can say "after 150,2,5 call code.routine()" which will call it 5 times at 1.5 second intervals.
I also added @ as an operator returning a variable data address, because I'd added it as a token for RPL which I want to keep as FORTHesque as possible.
Tuesday, 14 April 2020
New commands
I want to add 3 commands, which I think are quite neat, and I've only ever seen on this machine (the British machine, the CPC464).
They are EVERY AFTER and a means for resetting them. They are timers, that call a procedure either on a regular basis, or after a one off event. So on a CPC you'd write something like
EVERY 40,0 GOSUB 1000
Which would GOSUB 1000 every 40 ticks, which on the CPC is 50th of a second using timer 0. AFTER does the same thing, except it just fires once. I'll probably use CLEAR <n> to reset/clear the timer. You do have to be quite careful because of problems with code not being predictable. But it'll be quite useful.
So adding this will push it from version 0.69b to 0.70 and then I'll add the RPL() compiler which will push it to 0.80
They are EVERY AFTER and a means for resetting them. They are timers, that call a procedure either on a regular basis, or after a one off event. So on a CPC you'd write something like
EVERY 40,0 GOSUB 1000
Which would GOSUB 1000 every 40 ticks, which on the CPC is 50th of a second using timer 0. AFTER does the same thing, except it just fires once. I'll probably use CLEAR <n> to reset/clear the timer. You do have to be quite careful because of problems with code not being predictable. But it'll be quite useful.
So adding this will push it from version 0.69b to 0.70 and then I'll add the RPL() compiler which will push it to 0.80
Game 8 : Space Invaders
So, Space Invaders is now working. Not authentic (it's not colour in real life, it has cellophane on the screen). The obvious other differences are the shields, which are reduced to an 8x8 block array - because the one thing that you can't do on the computer is read the screen. So it disintegrates in small blocks.
Monday, 13 April 2020
Game #7 : Frogger II
So, done a bit of work on Frogger, and it now mostly works. You can play the game, there are just no sound effects and the turtles don't dive.
So I'll finish that this morning, and will do Space Invaders (aahhh the classics :) ).
Then I will spec this quasi FORTH that's been bouncing round my head for a few days, and write that.
Hopefully makes the assembler almost null and void. Though it's only a small amount of code, having an orthogonal instruction set gives you a simple assembler.
Most of the work is actually macros like push and pop (you can write push r0,r1,r2,r3,link and it generates 6 instructions in the right order) which are very useful, and some clarifying ones. For example carry testing is done using the "skip if carry matches operand bit 0" instruction (this sounds completely insane but it actually works very well) but you almost never use that because macros make the far clear skc sknc sklt skge, for carry testing and subtraction comparisons. There's a similar thing for register sign/value test skz sknz skp skm. Jumps branches and Returns are all done with the same instruction (branch-link which does Rx = PC, PC = operand) but it's completely unreadable in source, so there are jmp, jsr, ret instructions which compile to the relevant opcodes.
So I'll finish that this morning, and will do Space Invaders (aahhh the classics :) ).
Then I will spec this quasi FORTH that's been bouncing round my head for a few days, and write that.
Hopefully makes the assembler almost null and void. Though it's only a small amount of code, having an orthogonal instruction set gives you a simple assembler.
Most of the work is actually macros like push and pop (you can write push r0,r1,r2,r3,link and it generates 6 instructions in the right order) which are very useful, and some clarifying ones. For example carry testing is done using the "skip if carry matches operand bit 0" instruction (this sounds completely insane but it actually works very well) but you almost never use that because macros make the far clear skc sknc sklt skge, for carry testing and subtraction comparisons. There's a similar thing for register sign/value test skz sknz skp skm. Jumps branches and Returns are all done with the same instruction (branch-link which does Rx = PC, PC = operand) but it's completely unreadable in source, so there are jmp, jsr, ret instructions which compile to the relevant opcodes.
Subscribe to:
Posts (Atom)
Breaking change
We all make mistakes. One early mistake I made was copying (partly) the old thing in Microsoft BASIC where you didn't have to declare ...
-
I'm quite surprised. I actually am finding doing this rather, well, boring. It's a bit like manually translating a foreign langu...
-
This game is not slightly based on "Sabre Wulf" So this is coming along okay though I haven't done a huge amount on it yet...
-
So the completely original arcade adventure is now working except for sound effects. I think the next thing I'll do is this sort of ...