CPCWiki forum

General Category => Programming => Topic started by: arnoldemu on 16:23, 22 May 09

Title: BASIC programming tips
Post by: arnoldemu on 16:23, 22 May 09
Here are some tips to help with BASIC coding.
If anyone has anymore, please add them and I'll compile a doc for the wiki to help those who want to program in BASIC.

------
A BASIC program to load a binary file

The simplest method to load a executable binary file using BASIC is:

10 memory <addr>-1
20 load"<filename>",<addr>
30 call <execution address>

where <addr> is the load address of the binary file, <filename> is the name of the binary file and <execution address> is the address to start the program from. e.g.

10 memory &1fff
20 load"demo.bin",&2000
30 call &3a56

Here, "demo.bin" is a binary file starting at &2000 with a execution address of &3a56.

If the code above is used, the minimum memory address that the file can be loaded to is &1173, without the "Memory full" error.

The above program can be improved to allow for a lower minimum memory address:

10 openout"d"
20 memory <addr>-1
20 closeout
30 load"<filename>",<addr>
40 call <execution address>

The minimum memory address now becomes &1a0.

NOTE:

    * The name of the file in line 10 can be anything. It is never written, and the above program will function on a write protected disc

The main advantage of a BASIC program to load a binary file is:

    * If a executable binary program is RUN directly: e.g.

      RUN"demo.bin"

      Then it will be started with the firmware function "MC BOOT PROGRAM". This will reset the state of the firmware and disable the disc operating system. Therefore the selected drive and other disc operating system variables are lost.

------
A HEX loader

This document describes various methods to poke a binary program into RAM using a BASIC program.

10 addr=&4000
20 read a$
30 if a$="*" then goto 70
40 poke addr,val("&"+a$)
50 addr=addr+1
60 goto 20
70 call &4000
80 data 3e,3a,cd,5a,bb,c9,*

Line 10 defines the start address (&4000) to poke the data to. Line 20 reads a string from the current data position. Line 30 tests to see if the string is the end of data marker Line 40 converts the string to a hexidecimal value which is poked into the memory address referenced by the "addr" variable. Line 50 increments the "addr" variable, so that the next byte is poked after the previous byte. Line 60 loops back to continue reading data. Line 70 executes the program Line 80 contains the data of the program

10 for addr=&4000 to &4006
20 read a%
30 poke addr,a%
40 next
50 call &4000
60 data &3e,&3a,&cd,&5a,&bb,&c9

Line 10 defines a loop for the entire range of the binary data. Line 20 reads a number from the current data position. Line 30 pokes the number to the destination RAM address. Line 50 executes the program Line 60 contains the data of the program.

------
Anthony Jordon writes:

Here's a little teaser for your CPC brain cells.

The short routine below (which could be part of a longer program) will display on the screen the highest line number of the program currently in memory.  Can you work out how it does it?

10 REM Last
30 last%=0
40 CALL &BFFE-35, &C970,&2371,&166,&DD00,&6EDD,&ED18,&4623,&4E23,&23EB,&19EB,&A28
,&B37A,&2B56,&235E,&EB00,&1,&170,&1100,@last%
50 PRINT last%

Executioner: Yes, it places the parameters on the stack which are opcodes for the routine below, then executes the code on the stack:

.at_last
dw #01ff
db 0

.start
ld de,#0170
ld bc,#0000

.loop1
ex de,hl
ld e,(hl)
inc hl
ld d,(hl)
dec hl
ld a,d
or e
jr z,exit
ex de,hl
add hl,de
ex de,hl
inc hl
inc hl
ld c,(hl)
inc hl
ld b,(hl)
jr loop

.exit
ld l,(ix+#00)
ld h,(ix+#01)
ld (hl),c
inc hl
ld (hl),b
ret

AJ: The hex values are basically just the machine code in reverse byte order.

For more details see my article in WACCI June 1994 Issue 79 page 17.

KT: Essentially it is a way to embed small binary programs inside basic without using a data statement to poke them. The values after the call are the 16-bit numbers to poke onto the stack and are poked from left to right, but because they are put on the stack the last number ends up being on the top of the stack. Note how the 16-bit values are two opcode bytes together and are the opcodes of the function. Then the CALL executes them on the stack. BFFE is the base of the stack in BASIC, and -35 is the length of the program.

-----------

Probably an old one:
POKE 370,0 hides first line number in BASIC
POKE the other line numbers also to 0 can hide a complete BASIC program.
(GOTO / GOSUB will not work then)

You can also reverse line numbers in listing like:
20 PRINT"HELLO"
10 GOTO 20

Play around with PEEKs and POKEs
-----------
 
- BASIC puts a limit on the number of parameters to a RSX (|) command to 32.

- RSX/CALL parameters are put onto the stack, this explains why you access them in reverse order compared to how they are written in the BASIC program.
-----------
Put setup code in a seperate loader program (e.g. font setup).
-----------
RAM available to BASIC.
RAM is available from the end of the BASIC program up to HIMEM-1.
You can find HIMEM with
PRINT HIMEM
HIMEM is also defined using MEMORY command.
The following effect the amount of RAM basic can use:
- expansion roms that are connected
- using MEMORY
- using SYMBOL AFTER to define a font
- variables defined in BASIC program
-----------
Finding the memory address of a BASIC variable.
PRINT HEX$(@<var name)
e.g.
PRINT HEX$(@a%)
For strings this actually points to a "string descriptor block" which is 3 bytes long. The first byte is the length of the string.
The 2nd and 3rd bytes are the memory address of the string itself.
-----------
MEMORY command
This sets the upper limit for BASIC. After this address you can store binary code and data safely. Basic will not overwrite it.
The upper limit is now dependant on the roms that have been installed. Around &a600 is normal.
You can find this upper limit by checking HIMEM before you modify it.
NOTE: If you define a font and then change MEMORY the font will still be in it's original position and could be overwritten.
Read how to relocate the font.
-----------
Moving the font in memory:
SYMBOL AFTER 256
MEMORY <address>
SYMBOL AFTER 32
If it tells you "Out of memory" then you need to increase HIMEM.
The font ends at HIMEM.
The size of the font can be worked out as follows:
If you entered SYMBOL AFTER n:
Size of font = (256-n)*8
So font will start at HIMEM-Size of font and end at HIMEM.
The first line "SYMBOL AFTER 256" will clear all defined chars. By default BASIC is initialised with "SYMBOL AFTER 240", and attempting to do this:
MEMORY <address>
SYMBOL AFTER 32
will give an "Improper argument" error.
Then you relocate HIMEM, then you tell it the chars you want to modify.s
Title: Re: basic programming tips
Post by: Gryzor on 12:48, 14 June 09
Hmmmm if this expands it would make a nice wiki article... Want me to make this a sticky topic?
Title: Re: basic programming tips
Post by: arnoldemu on 16:08, 24 June 09
Hmmmm if this expands it would make a nice wiki article... Want me to make this a sticky topic?
yes.

More:

- BASIC puts a limit on the number of parameters to a RSX (|) command to 32.

- RSX/CALL parameters are put onto the stack, this explains why you access them in reverse order compared to how they are written in the BASIC program.

Title: Re: basic programming tips
Post by: Gryzor on 18:05, 24 June 09
Made sticky, thanks for your input. But I think it'd be better if one post only (the first one) is updated with new info instead of adding new posts?
Title: Re: basic programming tips
Post by: Devilmarkus on 13:01, 19 August 09
Probably an old one:
POKE 370,0 hides first line number in BASIC
POKE the other line numbers also to 0 can hide a complete BASIC program.
(GOTO / GOSUB will not work then)

You can also reverse line numbers in listing like:
20 PRINT"HELLO"
10 GOTO 20

Play around with PEEKs and POKEs  ;)
Title: Re: basic programming tips
Post by: AMSDOS on 14:37, 27 July 10
I made up some speedy was of drawing stuff in BASIC (of course it was all based on Loops and drawing shapes like Circles), the idea was to vision the Memory as one large array just waiting to be filled. It would require two programs one which would draw the Image and Poke it to memory and the second to pull it back out of memory and draw it on screen really quickly. In the inital program to make it perform faster I wrote a simply POKE16 to POKE 16bit Numbers quickly into those memory allocations. Just making a Poke to Poke 16bit Numbers is handy in itself cause it just feels like your knocking two birds with one stone sort of thing!  ;D  Unfotunately I can't find my examples!  :(  Probably have to obtain it from my other dying computer before the HD dies!
Title: Re: basic programming tips
Post by: AMSDOS on 15:23, 27 August 10
#1 Tip for people using BASIC:
 
* Never ever use "GOTO" unless you fancy a bit of Spaghetti Programming or as a Looping effect!  ;D
* Responcible "GOTOs" are possible if you keep them Local to a routine which is being applied, Bad is when a program is all over the place - moving to other sections which aren't related, or is a subroutine in which case should of been GOSUB instead!
Title: Re: basic programming tips
Post by: Gryzor on 10:58, 06 September 10
Damn, 20 years too late...
Title: Re: basic programming tips
Post by: AMSDOS on 12:18, 06 September 10
Gryzor wrote:

Damn, 20 years too late...

Heh!  ;D  I think everyone has been guilty of using GOTOs at one stage or another.  8)  I simply reduce them to what I could and was surprised by the result in the readability of my programs. Of course Amstrad Magazines like AA would try to help people understand BASIC better with Tutorials and they might have suggested to restrain using GOTOs as well, not sure if this ever came though. I simply restrain from using it as much as possible in BASIC cause I keep it to "0" GOTOs in my Turbo Pascal programs - and yes Borland were very naughty to incorporate GOTO into Turbo Pascal and some very naughty Turbo Pascal 3 website recommends the use of it!!  >:(  It should have been excluded cause it's not of a True Pascal at all!  One BASIC program I was interpreting did present a problem in TP cause of a certain GOTO in it. In the end I managed to write it with without any GOTOs by making an Array which returned the values the original program had - it was looping in such a way it was like "320 DO this : Do that : Do this as well : GOTO 320".
 
Which brings me to my next point  ;D  Having stacks of Code separated by the Colon ":"  per line is another one of those it's getting hard to read code. I guess the main problem here is for a program to be published in a Magazine, it was always better to stack in the code per line. I guess it's one of those things where if you wanted to learn some BASIC programming, nobody was around to say - just separate the code per line - you simply typed in the program and do whatever the program did. Keeping a line to a specific command is breaking the code into something more readable cause you can begin looking at the process of the program. The other thing I like is separating a BASIC program into it's own Line Numbers to give it more identity. "Renum" is sort of a enermy in a way cause a standard RENUM turns your program into that usual format of "10,20,30,40,50,etc" - Yeah sure you might have "10,20,30,40,50, etc" as your Main Nerve center of your BASIC program - the bit which runs the rest of the program, check out my Collision Detection Game for example and see how it's structured! I've set it up in such a way that it's got a main core, and then the routines are separated with the Line Numbers. This helps to define what belongs to what and kind of defines what an easy to read BASIC program should look like!  ;D  Of course over the years Magazines presented this standard way of presented programs, Authors were subjected to presenting their programs in such a way they could only cram in 255 bytes of Text per line cause it had to be a 10 Liner or because it helped to reduce space a program would take up (as in AAs case they were always on about making a program consume less space by cramming in longer BASIC lines). Though as a suggestion it maybe possible to take some of those programs and make them more readable. RENUM can be used properly when it's understood. The proper way to use RENUM is to issue a new Line number first, followed by the Old Line number and a third parameter relates to the Increment of it, so it looks like this RENUM <new line number>,<old line number>,<increment>. Just avoid the programs which have GOTO everywhere!  ;)
Title: Re: basic programming tips
Post by: arnoldemu on 15:21, 21 September 10
Basic and using extra 128k ram:

Using OUT &7F00,&C4 or similar to change bank is not 100% safe with BASIC.
In fact, if HIMEM is between &4000 and &7fff, expect problems.

Normally you can "get away with it", if HIMEM is high (above &8000) and your basic program is small and doesn't use many variables.
You can also safely work with BASIC and banks if you set HIMEM < &3fff. But for large basic programs this is not useful.

But if your basic program is big (goes past &4000), or uses lots of variables (so fills up memory down from himem), or himem is low, you will find problems.

Basic doesn't know about the ram banking, and so if you change bank and then do something with BASIC it could read bad data and this will cause program to stop working, or work unpredictable.

So how can you solve this?

The best way is to use some assembler functions to help you to access the bank in a way that is safe for BASIC. e.g. You have control over banking, and only return back to basic when normal ram banking is set back (e.g. OUT &7F00,&C0).

Your asm code is then responsible for changing the bank to the one you want, doing some work, and then returning the banking back to normal.

Basic stores font/symbol data below himem. Then below this it stores strings and variable data.

Using these recommendations, HIMEM can be anywhere you want (sensible values), and you can still use the extra ram for data.

1. Use Bank manager. This is useful for reading/writing extra ram.

2. You need some asm/rsxs to handle your banking. You need to put this above HIMEM where BASIC can't touch it.
The asm/banking needs to be above &8000 so that it is safe when you bank memory into the range &4000-&7fff.

3. If you are loading data into the extra ram, then consider having an rsx, or asm function to do this safely:

Code: [Select]
setbank:
        ld (BuffBank),a
        and &3f
        or &c0
        ld b,&7f
        out (c),a
        ret

BuffBank: defb 0

;; CALL loadbank,<filename>,<address>,<bank>
;; e.g. a$="filename":call @a$, &4000,&c4

loadbank:   
        ;; load address
        ld l,(ix+2)
        ld h,(ix+3)
        push hl
       
        ;; string descriptor (note this is in main ram, so we can't change bank until
        ;; we have read the value. In addition cas in open needs to see it too. So either
;; copy filename to a buffer, or change bank when we read data.
;; Note, the buffer for AMSDOS or tape, needs to be above &8000. Normally for disc it is around &a700, and for tape a bit higher.

         ld l,(ix+4)
         ld h,(ix+5)
                           
         ;; length
         ld b,(hl)
         inc hl
;; address of chars in string
         ld a,(hl)
         inc hl
         ld h,(hl)
         ld l,a
         ;; address of string
        ld a,(ix+0)
        push af
;; this 2k buffer needs to be outside of &4000-&7fff. With binary it can be any value because the buffer is not used.
         ld de,&c000
         call cas_in_open
         pop af
         call setbank
         pop hl
         call cas_in_direct
         call cas_in_close

;; restore back for basic         
         ld a,&c0
         call setbank
;; return to basic
         ret

4. Any functions that access the extra ram, must set the ram bank, do some work and restore it. Remember if you are accessing any basic variables, then these are in main ram, so you need to read them and remember their data, now change banks, now work with them, and then restore bank back.

If you follow these tips your programs will work safely with basic, you can set himem as you want, and have less bugs.
 
Title: Re: basic programming tips
Post by: AMSDOS on 13:27, 07 January 11
A certain little BASIC program I'm working on (which is sadly bugged because I've tweaked it too much), had this problem where the Input from Keyboard was sluggish. I remember one of my assembly routines had a fairly fast way of testing for Keys with the use of KM TEST KEY which works for Keyboard or Joystick.
 
Code: [Select]
org &4000
 
ld a,8
call &bb1e
jr nz,left
ld a,1
call &bb1e
jr nz,right
ld a,0
call &bb1e
jr nz,jump
ld hl,value
ld a,255
ld (hl),a
ret
 
.left
ld hl,value
ld a,8
ld (hl),a
ret

.right
ld hl,value
ld a,1
ld (hl),a
ret

.jump
ld hl,value
ld a,0
ld (hl),a
ret

.value
defb 0

The result was a game running more efficiently because your character will easily move around the screen without pausing and dying in tight situations (sadly I'll have to go back to the original game so I can iron out the bugs which have got into my version). 
 
But how does this work:
 
For KM TEST KEY (&BB1E) which is rather fiddly to use in BASIC programs, you have to put the key number into the Accumulator ("A" Register), by calling KM TEST KEY if the key in the Accumulator is found a jump to a secondary routine is carried out. If that key wasn't found it will then look though the rest and if no key was pressed then in this situation it returns back to BASIC with a value of my own assigned to "value"." value" is there as a means to intergrate this routine in with the BASIC program. In this situation if a key is found it will go to that area and assign a value into "value" which can be found in BASIC by PEEKing into the memory address of "value".
 
Here's some examples - the first is some straight BASIC code which demonstrates how sluggish the INKEY$ is - perhaps there's a better way of doing it in BASIC?
 
Code: [Select]
10 MODE 1: BORDER 0:INK 1,26:x=20:y=13
11 WHILE 1
20 a$=INKEY$:oldx=x:oldy=y
30 IF a$=CHR$(242) AND x>1 THEN x=x-1
40 IF a$=CHR$(243) AND x<40 THEN x=x+1
41 IF a$=CHR$(240) AND y>1 THEN y=y-1
42 IF a$=CHR$(241) AND y<25 THEN y=y+1
50 LOCATE oldx,oldy:PRINT" ";:LOCATE x,y:PRINT CHR$(248);
60 WEND

When compared with the following program, it's quite slugglish.
 
Code: [Select]
1000 MODE 1:BORDER 0:INK 1,26:x=20:y=13
1010 WHILE 1:CALL &4000:oldx=x:oldy=y
1020 IF PEEK(&403F)=8 AND x>1 THEN x=x-1
1030 IF PEEK(&403F)=1 AND x<40 THEN x=x+1
1040 IF PEEK(&403F)=0 AND y>1 THEN y=y-1
1050 IF PEEK(&403F)=2 AND y<25 THEN y=y+1
1060 LOCATE oldx,oldy:PRINT" ";:LOCATE x,y:PRINT CHR$(248);
1070 WEND

This is the Assembly for that program above, again I've modified it to cater for the style this program is in, though the movement from it doesn't halt anywhere where compared to the first program, which makes it quite ideal for those programs which rely on quick responces.
 
Code: [Select]
org &4000
 ld a,8
 call &bb1e
 jr nz,left
 ld a,1
 call &bb1e
 jr nz,right
 ld a,0
 call &bb1e
 jr nz,up
 ld a,2
 call &bb1e
 jr nz,down
 ld hl,value
 ld a,255
 ld (hl),a
 ret
 
.left
 ld hl,value
 ld a,8
 ld (hl),a
 ret

.right
 ld hl,value
 ld a,1
 ld (hl),a
 ret

.up
 ld hl,value
 ld a,0
 ld (hl),a
 ret

.down
 ld hl,value
 ld a,2
 ld (hl),a
 ret

.value
 defb 0
Title: Re: basic programming tips
Post by: arnoldemu on 13:51, 07 January 11
Use "INKEY" and not "INKEY$".

I looked at the dissassembly of the BASIC ROM.

"INKEY" uses BB1E/KM READ KEY.
"INKEY$" uses BB09/KM READ CHAR.

I think with KM READ CHAR you need to set the key repeat speed to make it work better (it's something I've done in one my examples).

Of course with KM READ KEY you are now using key numbers and not the char representation ;)
Title: Re: basic programming tips
Post by: AMSDOS on 01:07, 08 January 11
arnoldemu wrote:

Use "INKEY" and not "INKEY$".

I looked at the dissassembly of the BASIC ROM.

"INKEY" uses BB1E/KM READ KEY.
"INKEY$" uses BB09/KM READ CHAR.

I think with KM READ CHAR you need to set the key repeat speed to make it work better (it's something I've done in one my examples).

Of course with KM READ KEY you are now using key numbers and not the char representation ;)


That's fine if you don't despise "INKEY" - unfortunately it's been spoilt for those who dislke "INKEY"!  ???
 
Incidently I have "KM READ KEY" at "&BB1B" and not "&BB1E" which the firmware guide has as "KM TEST KEY". "KM READ KEY" reads simular to "KM TEST KEY" though it returns a character code into the accumulator if one was found, "KM TEST KEY" does not.
Title: Re: basic programming tips
Post by: AMSDOS on 02:57, 03 July 11
Nich Campbell had me fascinated when I discovered the ACU 10-Liners he'd manage to convert to run on a 464 using BASIC 1.0. Back in the day I would have dug out the 6128 Emulator from AA68 and load that before loading the BASIC 1.1 program - which came in real handy particularly for the "FILL" command (wasn't as fast as the one in BASIC 1.1, though faster than whole Subroutine written in BASIC!  ;D ). Unfortunately it was to my disappointment when I first began typing in type-ins before I knew anything about BASIC 1.1.
 
I found this short program in AA21 which I thought is a good example of a BASIC 1.1 program which can easily be converted to work just as good in BASIC 1.0 (and I'd probably imagine AA might of had a few people sending in their solutions to it!):
 
Code: [Select]
1 ' Serpent
2 ' by T Magee
3 ' Amstrad Action   June 87
10 MODE 1
20 ORIGIN 320,200
30 GOSUB 60:a=a-2
40 IF a>-0.1 THEN GOSUB 60
50 a=a+2.1:GOTO 30
60 MOVE 220*SIN(a/2),98*COS(a),,1
70 DRAW 200*COS(a/2),198*SIN(a)
80 RETURN

The offender in this case is line 60 which appears to be a routine "MOVE", if you look carefully though, this "MOVE" statement is using the 4th parameter which, in BASIC 1.1 is allowed. In BASIC 1.0 "MOVE" is a two parameter command which allows you to set X & Y co-ordinates for the graphics pointer and that's it!  ;D  So addition to X & Y Coordinates in BASIC 1.1 the MOVE parameter allows you to set an INK colour which is the 3rd parameter (think of this as another way to set the Graphics PEN) & the 4th Parameter allows you to set the INK Mode. The INK Mode determine how that INK is to be written - valid parameters are 0,1,2,3 - 0 is Normal, 1 is XOR (Exclusive OR), 2 for AND & 3 for OR.
 
So in that sense if we go back to that original program all it's doing is drawing a few lines for a few steps and once a becomes greater than "-0.1" it goes to that drawing routine and does another draw over. Because that "MOVE" in line 60 is setting the INK Mode to XOR, it's writing over what's there thus removing that graphical line and carries to move on which makes it look like an image spiraling around the screen.
 
In them days AA seemed to be dazzled by some of these programs and would even break them down and explain how they worked!  :o  I guess they were looking for reader feedback so people could collect their £10!  ;D  On a 464 you could do a number of things, you could take line 40 and point it to another set of move/draw statements like this:
 
Code: [Select]
1 ' Serpent
2 ' by T Magee
3 ' Amstrad Action   June 87
10 MODE 1
20 ORIGIN 320,200
30 GOSUB 60:a=a-2
40 IF a>-0.1 THEN GOSUB 90
50 a=a+2.1:GOTO 30
60 MOVE 220*SIN(a/2),98*COS(a)
70 DRAW 200*COS(a/2),198*SIN(a),1
80 RETURN
90 MOVE 220*SIN(a/2),98*COS(a)
100 DRAW 200*COS(a/2),198*SIN(a),0
110 RETURN

which produces a similar effect in BASIC 1.0 (not the same though cause it's deleting over some of the places where the lines cross over) and is a bit slower too.
 
The better approach is to tell BASIC 1.0 to start writing in XOR mode how? Have a look at this program:
 
Code: [Select]
1 ' Serpent
2 ' by T Magee
3 ' Amstrad Action   June 87
10 MODE 1
20 ORIGIN 320,200:PRINT CHR$(23);CHR$(1);
30 GOSUB 60:a=a-2
40 IF a>-0.1 THEN GOSUB 60
50 a=a+2.1:GOTO 30
60 MOVE 220*SIN(a/2),98*COS(a)
70 DRAW 200*COS(a/2),198*SIN(a)
80 RETURN

In this example I've gone back to what appears to be the original program and have inserted a Control Code in Line 20 - it could indeed be in Line 60 though. In this case that Control Code (which works in BASIC 1.0 & BASIC 1.1), works on the Graphics Write Mode, the first character call (23) initiates the setting to call up the Graphics Write Mode, the second character call (1) is the parameter for which write mode to be used, which in this case is set to XOR. Because this program is only using one Graphics Write Mode, I've stuck this instruction in line 20 because having it being called up all the time in Line 60 will also slow the program slightly.
 
It's just amazing how a little understanding of a program like this can present a solution which would have work on all computers - in AAs case their solution was to remove the "1" from the MOVE parameter which does produce an interesting effect, though somewhat different result which had the pattern drawing over itself!  ;D
Title: Re: basic programming tips
Post by: AMSDOS on 06:59, 03 July 11
I always recall this example from AA66 which is somewhat trickier than my last example which throws more parameters into the equation:
 
Code: [Select]
1 ' Cursor
2 ' by David Ware
10 SYMBOL 255,128,192,224,240,224,32,16,16
20 tag:x=0:y=0
30 MOVE x,y,2,1:PRINT CHR$(255);
40 FRAME:MOVE x,y,2,1:PRINT CHR$(255);
50 a$=INKEY$
60 IF INKEY(0)=0 THEN y=y+6:GOTO 120
70 IF INKEY(2)=0 THEN y=y-6:GOTO 120
80 IF INKEY(8)=0 THEN x=x-6:GOTO 120
90 IF INKEY(1)=0 THEN x=x+6:GOTO 120
100 MOVE x,y,2,1:PRINT CHR$(255);
110 GOTO 40
120 FRAME:MOVE x,y,2,1:PRINT CHR$(255);
130 GOTO 40

To replicate this for BASIC 1.0 this is what I came up with:
 
Code: [Select]
1 ' Cursor
2 ' by David Ware
10 SYMBOL 255,128,192,224,240,224,32,16,16
20 x=322:y=232:PLOT -2,-2,2
30 MOVE x,y:PRINT CHR$(23);CHR$(1);:TAG:PRINT CHR$(255);:TAGOFF
40 CALL &BD19:MOVE x,y:PRINT CHR$(23);CHR$(1);:TAG:PRINT CHR$(255);:TAGOFF
50 a$=INKEY$
60 IF INKEY(0)=0 THEN y=y+6:GOTO 120
70 IF INKEY(2)=0 THEN y=y-6:GOTO 120
80 IF INKEY(8)=0 THEN x=x-6:GOTO 120
90 IF INKEY(1)=0 THEN x=x+6:GOTO 120
100 MOVE x,y:PRINT CHR$(23);CHR$(1);:TAG:PRINT CHR$(255);:TAGOFF
110 GOTO 40
120 CALL &BD19:MOVE x,y:PRINT CHR$(23);CHR$(1);:TAG:PRINT CHR$(255);:TAGOFF
130 GOTO 40

In this case the original BASIC 1.1 program is using:
 
Code: [Select]
MOVE x,y,2,1
for positioning the Cursor on screen x & y are used, but then a third parameter is used to change the graphics pen ink - in that case I've initally changed the graphics pen ink:
 
Code: [Select]
PLOT -2,-2,2
since this is the only colour used for the cursor. Like the last example this program is also using XOR mode to display the cursor in such a way that it's blinking away as it loops around. In this case I've place the XOR routine:
 
Code: [Select]
PRINT CHR$(23);CHR$(1);
after the move statement, however it should work in BASIC 1.0 after the PLOT routine in Line 20 since again it's writing in the same Graphics Write Mode.
 
The original program does have some differences from the original program, the main hurdle with it is the fact the original program is drawing a redefined text character as a graphic using TAG. In order to carry out those control codes, they must be carried out while the Graphical mode is switched off, hence the need to TAG..TAGOFF when displaying that Graphical character - otherwise it won't perform those control procedures and display funny control symbols instead!  ;D
 
To carry out a FRAME in BASIC 1.0 is simply a CALL &BD19 which waits for the frame to flyback!
Title: Re: basic programming tips
Post by: Devilmarkus on 12:04, 09 October 13
You should change line 100 to this:
Code: [Select]
100 MOVE x,y:LOCATE 1,1:PRINT CHR$(23);CHR$(1):TAG:PRINT CHR$(255);:TAGOFF
Otherwise you will see your screen move up after a (longer) while...
Title: Re: basic programming tips
Post by: AMSDOS on 12:41, 09 October 13
You should change line 100 to this:
Code: [Select]
100 MOVE x,y:LOCATE 1,1:PRINT CHR$(23);CHR$(1):TAG:PRINT CHR$(255);:TAGOFF
Otherwise you will see your screen move up after a (longer) while...

Isn't that what the Semi-Column does after executing the XOR Mode? I don't remember having the screen move up when I was testing it, though maybe it's a BASIC 1.1 issue?
Title: Re: basic programming tips
Post by: Devilmarkus on 12:52, 09 October 13
Sure you didn't see the screen wandering up, because the ; @ end of PRINT.

In MODE 1 you have 40x25 chars (=1000)  first before the screen moves up. (Long time for testing it, you need more than 999 keystrokes until it takes effect, but it will take effect)
Title: Re: basic programming tips
Post by: AMSDOS on 13:20, 09 October 13
Sure you didn't see the screen wandering up, because the ; @ end of PRINT.

In MODE 1 you have 40x25 chars (=1000)  first before the screen moves up. (Long time for testing it, you need more than 999 keystrokes until it takes effect, but it will take effect)

Oh ok, so if there was some text on the screen for example the control codes would interfere and force it to roll up.
Title: Re: basic programming tips
Post by: Devilmarkus on 14:22, 09 October 13
Try this and you will see, what happens:

Code: [Select]
100 MOVE x,y:PRINT CHR$(143);CHR$(23);CHR$(1);:TAG:PRINT CHR$(255);:TAGOFF
I added a CHR$(143) just to demonstrate it...

I did not test the code, but it should do as I expect it to do:
█  █  █  █  █... and so on....

Edit:

Well I will test it... Looks like you only need to do the PRINT CHR$(23);CHR$(1); only once... (Also the TAGON/TAGOFF)
Title: Re: basic programming tips
Post by: Devilmarkus on 14:27, 09 October 13
Tested and optimized:

Code: [Select]
1 ' Cursor
2 ' by David Ware
10 SYMBOL 255,128,192,224,240,224,32,16,16
20 x=322:y=232:PLOT -2,-2,2
30 MOVE x,y:PRINT CHR$(23);CHR$(1);:TAG:PRINT CHR$(255);
40 CALL &BD19:MOVE x,y:PRINT CHR$(255);
50 a$=INKEY$
60 IF INKEY(0)=0 THEN y=y+6:GOTO 120
70 IF INKEY(2)=0 THEN y=y-6:GOTO 120
80 IF INKEY(8)=0 THEN x=x-6:GOTO 120
90 IF INKEY(1)=0 THEN x=x+6:GOTO 120
100 MOVE x,y:PRINT CHR$(255);
110 GOTO 40
120 CALL &BD19:MOVE x,y:PRINT CHR$(255);
130 GOTO 40
Title: Re: basic programming tips
Post by: Urusergi on 17:01, 09 October 13
Code: [Select]
50 a$=INKEY$
I think this line is no longer needed, isn't it? ::)
Title: Re: basic programming tips
Post by: AMSDOS on 05:30, 10 October 13
Tested and optimized:

Code: [Select]
1 ' Cursor
2 ' by David Ware
10 SYMBOL 255,128,192,224,240,224,32,16,16
20 x=322:y=232:PLOT -2,-2,2
30 MOVE x,y:PRINT CHR$(23);CHR$(1);:TAG:PRINT CHR$(255);
40 CALL &BD19:MOVE x,y:PRINT CHR$(255);
50 a$=INKEY$
60 IF INKEY(0)=0 THEN y=y+6:GOTO 120
70 IF INKEY(2)=0 THEN y=y-6:GOTO 120
80 IF INKEY(8)=0 THEN x=x-6:GOTO 120
90 IF INKEY(1)=0 THEN x=x+6:GOTO 120
100 MOVE x,y:PRINT CHR$(255);
110 GOTO 40
120 CALL &BD19:MOVE x,y:PRINT CHR$(255);
130 GOTO 40

Hmmm okay, was that tested with BASIC 1.0? I'm only asking because TAG works a little bit differently in BASIC 1.0 and I had to switch it off, it looks okay because as the program is, you only need to switch on the XOR mode. Earlier I wrote WIN7.BAS which is simply this routine along with a little Icon & Message to Screen so in that case I had to switch off TAG to display those other Images.  :D
Title: Re: basic programming tips
Post by: Devilmarkus on 12:39, 10 October 13
It works fine with BASIC 1.0

To make sure, you can also move in diagonal ways:

Code: [Select]
1 ' Cursor
2 ' by David Ware
10 SYMBOL 255,128,192,224,240,224,32,16,16
20 x=322:y=232:PLOT -2,-2,2
30 MOVE x,y:PRINT CHR$(23);CHR$(1);:TAG:PRINT CHR$(255);
40 CALL &BD19:MOVE x,y:PRINT CHR$(255);
50 IF INKEY(0)=0 THEN y=y+6:p=1:IF y>398 THEN y=398
60 IF INKEY(2)=0 THEN y=y-6:p=1:IF y<16 THEN y=16
70 IF INKEY(8)=0 THEN x=x-6:p=1:IF x<0 THEN x=0
80 IF INKEY(1)=0 THEN x=x+6:p=1:IF x>630 THEN x=630
90 IF p=1 THEN p=0:GOTO 120
100 MOVE x,y:PRINT CHR$(255);
110 GOTO 40
120 CALL &BD19:MOVE x,y:PRINT CHR$(255);
130 GOTO 40

Edit: Now also the cursor stays in the screen bounds.
Title: Re: basic programming tips
Post by: Devilmarkus on 12:48, 10 October 13
For oldschool reasons:

Title: Re: basic programming tips
Post by: Bryce on 12:57, 10 October 13
Without a read error half way through, it's just not the same :D

Bryce.
Title: Re: basic programming tips
Post by: redbox on 14:56, 25 November 13
I've got one for all of you BASIC aficionados...

I have a 16-bit number in myvar (i.e. from range 0 - 65535).  I want to convert it to high byte & low byte.

If I do this:

Code: [Select]
myhighbyte = myvar / 256 : highbyte = FIX ( myhighbyte )
lowbyte = myvar MOD 256

...then I get overflow errors. 

Any ideas?

Title: Re: basic programming tips
Post by: arnoldemu on 15:00, 25 November 13

myhighbyte = myvar \ 256 : lowbyte = myvar AND 255

try that.


\ is integer division.

AND can be used as both for ANDing a value, and as a statement joiner (a=3 AND b=4).

MOD may use float division, same with FIX.
Title: Re: basic programming tips
Post by: redbox on 15:44, 25 November 13
myhighbyte = myvar \ 256 : lowbyte = myvar AND 255

try that.

Thanks.

Still getting overflow when myvar = 33924.

Does it only work within signed limited (i.e. -32768 to 32768)...?
Title: Re: basic programming tips
Post by: arnoldemu on 15:56, 25 November 13
Thanks.

Still getting overflow when myvar = 33924.

Does it only work within signed limited (i.e. -32768 to 32768)...?

33924 is probably being stored as a float value internally. yes, seems it is.
can you do it as a hex value? e.g. a=&c000


Title: Re: basic programming tips
Post by: arnoldemu on 15:57, 25 November 13
Thanks.

Still getting overflow when myvar = 33924.

Does it only work within signed limited (i.e. -32768 to 32768)...?
a% will be signed
a will end up being floating point
c=&aaaa actually ends up being unsigned 16-bit.
Title: Re: basic programming tips
Post by: redbox on 16:14, 25 November 13
a% will be signed
a will end up being floating point
c=&aaaa actually ends up being unsigned 16-bit.

So is there anyway to turn floating point variable into unsigned 16-bit?
Title: Re: basic programming tips
Post by: Urusergi on 18:19, 25 November 13
I've got one for all of you BASIC aficionados...

I have a 16-bit number in myvar (i.e. from range 0 - 65535).  I want to convert it to high byte & low byte.

If you have a number from range 0 - 65535 then it's necessary to convert to 16 bit number (-32768 - 32767)

Code: [Select]
alter=VAL("&"+HEX$(myvar))
Title: Re: basic programming tips
Post by: Grim on 20:44, 25 November 13
If you have a number from range 0 - 65535 then it's necessary to convert to 16 bit number (-32768 - 32767)

Code: [Select]
alter=VAL("&"+HEX$(myvar))
See UNT (http://www.cpcwiki.eu/index.php/Locomotive_BASIC#UNT_.28add.29), which uses the Firmware Math function INTEGER TO REAL (No need to reinvent the wheel :)
Code: [Select]
alter=UNT(myvar)
I'm not sure however that a BASIC keyword exists to do the other way around (eg. based on the Firmware Math function REAL TO INTEGER).


One way to avoid all that mess with variable type:
Code: [Select]
myvar = 33924
hi = int(myvar / 256)
lo = myvar - 256*hi
It should work whatever type myvar is.
Title: Re: basic programming tips
Post by: redbox on 21:11, 25 November 13
Thanks guys, much appreciated.

I went with Grim's last example (as I'd come close to this myself but didn't get the order/syntax right) and it's all working now.

Sometimes a bit of BASIC is a lot quicker than C or Python ;)
Title: Re: basic programming tips
Post by: Urusergi on 02:40, 26 November 13
See UNT (http://www.cpcwiki.eu/index.php/Locomotive_BASIC#UNT_.28add.29), which uses the Firmware Math function INTEGER TO REAL (No need to reinvent the wheel :)
Code: [Select]
alter=UNT(myvar)
Oh! well, I didn't know that command  :o   Thanks  :)

One way to avoid all that mess with variable type:
Code: [Select]
myvar = 33924
hi = int(myvar / 256)
lo = myvar - 256*hi
It should work whatever type myvar is.

Then, this code...

Code: [Select]
myvar = 33924
hi = int(myvar / 256)
lo = unt(myvar) and 255

is 2 bytes less, and it's a little faster, isn't it?
Title: Re: basic programming tips
Post by: Grim on 03:51, 26 November 13
Code: [Select]
myvar = 33924
hi = int(myvar / 256)
lo = unt(myvar) and 255
is 2 bytes less, and it's a little faster, isn't it?
Indeed! It's better that way! (smaller, faster and still works with any numeric type)
Title: Re: basic programming tips
Post by: Sykobee (Briggsy) on 10:51, 26 November 13
See UNT (http://www.cpcwiki.eu/index.php/Locomotive_BASIC#UNT_.28add.29), which uses the Firmware Math function INTEGER TO REAL (No need to reinvent the wheel :)
Code: [Select]
alter=UNT(myvar)
I'm not sure however that a BASIC keyword exists to do the other way around (eg. based on the Firmware Math function REAL TO INTEGER).


Wouldn't you just co-erce the result of FIX, INT or ROUND into an integer type? i.e., a%=ROUND(3.14,0) would give an signed integer with a value of 3.


I've just tested (painfully, annoying french keyboard mapping) in CPCBox and this works (just add 40000 and check for Overflow)


BASIC's PRINT keyword treats "unsigned" integers as signed integers, so the value=&FFFF method is only useful for assigning a 16-bit value directly.

Quote
Code: [Select]
myvar = 33924
hi = int(myvar / 256)
lo = unt(myvar) and 255

hi and lo here are still reals, using 5 bytes and being slightly slower. I'd definitely rather use hi% and lo%, or DEFINT h,l :-)

Sadly I can't confirm this because there is no \ key in CPCBox.
Title: Re: basic programming tips
Post by: Urusergi on 22:51, 26 November 13
hi and lo here are still reals, using 5 bytes and being slightly slower. I'd definitely rather use hi% and lo%, or DEFINT h,l :-)
Sadly I can't confirm this because there is no \ key in CPCBox.

Yes! It's a bit faster using %  ;)
Title: Re: basic programming tips
Post by: Grim on 10:07, 27 November 13
Wouldn't you just co-erce the result of FIX, INT or ROUND into an integer type? i.e., a%=ROUND(3.14,0) would give an signed integer with a value of 3.
Nay. ROUND, FIX, CINT, INT & co are real-to-real functions, they expect a real (ie. 40-bit float) as input (and will convert the input automagically if necessary) and output a real. So ROUND(33000) is exactly the same as writing 33000 directly, both result with the same real number. So when trying to fit that real number into a signed integer variable (a%), if the real is beyond the signed integer range (-32768 to 32767), the conversion will overflow (eg a%=33000).

The issue Redbox had was that he was trying to apply 16-bit signed integer operations (such as MOD or AND like in myvar MOD 256) to unsigned 16-bit values stored as real number (For real! Aye! Ye're still there floating with me? :)

So in that case, we can keep it real all along (myvar - 256*hi) or switch to 16-bit signed integer (unt(myvar) and 255). The later is more efficient, and even more so when the variables are also cast to signed integer, aye! :)

Quote from: Edsger W. Dijkstra
It is practically impossible to teach good programming to students that have had a prior exposure to BASIC: as potential programmers they are mentally mutilated beyond hope of regeneration.
Title: Re: basic programming tips
Post by: Sykobee (Briggsy) on 11:17, 27 November 13
Nay. ROUND, FIX, CINT, INT & co are real-to-real functions, they expect a real (ie. 40-bit float) as input (and will convert the input automagically if necessary) and output a real. So ROUND(33000) is exactly the same as writing 33000 directly, both result with the same real number. So when trying to fit that real number into a signed integer variable (a%), if the real is beyond the signed integer range (-32768 to 32767), the conversion will overflow (eg a%=33000).

The issue Redbox had was that he was trying to apply 16-bit signed integer operations (such as MOD or AND like in myvar MOD 256) to unsigned 16-bit values stored as real number (For real! Aye! Ye're still there floating with me? :)


Thanks - I was specifically responding to the question posed: "I'm not sure however that a BASIC keyword exists to do the other way around (eg. based on the Firmware Math function REAL TO INTEGER)." to which the answer is that you need to consider what you want to do with the decimal places first (using the real->real functions).  If you know there are no decimal places (or overflow risk), then I presume you can do integer%=real to cast?


What I meant by co-ercing was exactly that of specifying the target variable for the function as a signed integer, i.e., a cast.  Yes, there are opportunities for overflow, and you need to consider this in your design. :-)
Title: Re: basic programming tips
Post by: AMSDOS on 14:02, 15 June 14
I've been having a look at some early BASIC programs from 1984, this particular program from 60 Programs for the Amstrad (http://cpcwiki.eu/index.php/Sixty_Programs_for_the_Amstrad_CPC_464) CPC464 presents an interesting challenge because this game is harder on a 664/6128 while at the same time still being playable.


The challenge in this program is the M/C routine at the end of the program, which sets up TXT RD CHAR (&BB60). An interesting Firmware instruction because it returns different ASCII results and the program below is certainly in that category. For this program "Dambusters" on a 464 TXT RD CHAR will return a zero result if the area is something in it (including graphics). For 664/6128 machines using BASIC 1.1, it has the COPYCHR$(#0) which is used to return a Text Based character, ASCII values can be returned if ASC(COPYCHR$(#0)), however what happens (and this also occurs in the Firmware TXT RD CHAR), is different results occur even when graphical are present, so values don't change until TXT characters are found.



The easiest solution I could come up with in this program was to modify Line 490 from:


Code: [Select]
490 FOR A=1 TO 5:SOUND 1,220,4:SOUND 1,90,4:NEXT A:LOCATE X+1,20:CALL 360:K= PEEK(367):IF K=32 THEN 510

to:


Code: [Select]
490 FOR A=1 TO 5:SOUND 1,220,4:SOUND 1,90,4:NEXT A:LOCATE X+1,20:CALL 360:K= PEEK(367):IF (X>12 AND X<21) AND K=32 THEN 510

so now the program behaves like the 464 version, the program will also work on a 464, though it doesn't fix the difference between TXT RD CHAR on the different machines.


Code: [Select]
10 REM DAMBUSTERS
20 REM @ PAUL STANLEY.
30 MODE 1:BORDER 4:INK 0,16:INK 1,0:INK 2,20:INK 3,6:WINDOW #1,1,40,1,25:PAPER #1, 0:PEN #1,1:CLS
40 HS=0
50 GOSUB 960
60 GOSUB 770
70 GOSUB 620
80 BORDER 1:INK 0,0:INK 1,24:INK 2,20:INK 3,6:WINDOW #0,5,36,1,25:PAPER #0, 0:PEN #0,1:CLS #0
90 PEN 2:LOCATE 1,1:PRINT"SCORE:0":PEN 1
100 PEN 3:LOCATE 19,1:PRINT"HI-SCORE:";:PEN 2:PRINT HS:PEN 1
110 LOCATE 1,22:PRINT CHR$(24); STRING$(64,58);CHR$(24)
120 FOR F=1 TO 150 :PLOT 64+RND*506,RND*400+128,2:NEXT
130 G=0:FOR F=1 TO 5:PLOT G*2+64,15+F*2+48,2:DRAWR 508-4*G,0:G=G+2:NEXT
140 PLOT 100,72,2:DRAWR 216,40:PLOT 532,72:DRAWR -216,40
150 PLOT 64,64,3:DRAWR 0,32:DRAWR 8,0:DRAWR 16,16:DRAWR 4,- 4:DRAWR -16,-16:DRAWR 0,-20
160 PLOT 572,64,3:DRAWR 0,32:DRAWR -8,0:DRAWR -16,16:DRAWR -4,-4:DRAWR 16,-16:DRAWR 0,-20
170 D=1:S=0:A=1:P=17
180 Y=9
190 X=INT(RND*20)+6
200 PRINT CHR$(22);CHR$(1);:FOR F=1 TO 5 STEP 2:PEN 1:LOCATE X+1,6:PRINT MID$(B$,F,1);MID$(B$,F+1,1);:SOUND 1,120,4:PEN 0:LOCATE X+1,6:PRINT MID$(B$,F,1);MID$(B$,(F+1),1);:NEXT F
210  FOR F= 1 TO 4:PEN 1:LOCATE X+1,6-F:PRINT MID$(B$,5,1);MID$(B$,6,1):LOCATE X+1,6+F:PRINT CHR$(232);
220 SOUND 1,180,4:PEN 0:LOCATE X+1,6-F:PRINT MID$(B$,5,1);MID$(B$,6,1);: LOCATE X+1,6+F:PRINT CHR$(232);:NEXT F:PEN 1:PRINT CHR$(22);CHR$(0);
230 F=INT(RND*25)+3:G=5+INT(RND*11)
240 A$=CHR$(231)+CHR$(232)+CHR$(233)+CHR$(234)+CHR$(235)+CHR$(235)+CHR$(236):HT=10
250 PEN 1: LOCATE X+1,Y+1:PRINT  CHR$(22);CHR$(1);MID$(A$,A,1);CHR$(22);CHR$(0);
260 F=F-(INKEY(1)=0 AND F<29)+(INKEY(8)=0 AND F>2):G=G-(INKEY(2)=0 AND G<17)+(INKEY(0)=0 AND G>3)
270 PEN 2:LOCATE F+1,G+1:PRINT CHR$(22);CHR$(1);"+";CHR$(22);CHR$(0);:PEN 1
280 LOSS=LOSS+LEEK
290 IF LOSS>1000 THEN 550
300 IF INKEY (47)=0 THEN IF S<10 THEN GOSUB 430
310 SOUND 1,100,4
320 PEN 0: LOCATE X+1,Y+1:PRINT CHR$(22);CHR$(1);MID$(A$,A,1);CHR$(22);CHR$(0);
330 PEN 0: LOCATE F+1,G+1:PRINT CHR$(22);CHR$(1);"+";CHR$(22);CHR$(0);:PEN 1
340 PLOT 100,72,2:DRAWR 216,40:PLOT 532,72:DRAWR -216,40
350 Y=Y+D
360 IF RND>0.6 THEN F=F+INT(RND*1.5)-INT(RND*1.5)
370 IF F<0 THEN F=0
380 IF RND<0.4 THEN G=G+INT(RND*1.5 AND G<18)-INT(RND*1.5)
390 IF Y=HT THEN P=P+1:D=1:A=A+1
400 IF Y=P THEN SOUND 1,180,4:A=A+1:D=-1:HT=HT-3
410 IF A=7 THEN 490
420 GOTO 250
430  PLOT 90,112,2:DRAW (F+1)*16+54,(24-G)*16+8,2:PLOT 546,110,2:DRAW (F+1)*16+54,(24-G)*16+8,2
440 S=S+1
450 PLOT 90,112,0:DRAW (F+1)*16+54,(24-G)*16+8,0:PLOT 546,110,0:DRAW (F+1)*16+54,(24-G)*16+8,0
460 IF G=Y THEN IF X=F THEN 480
470 RETURN
480 SC=SC+10: LOCATE 7,1:PRINT SC;:LOCATE F+1,G+1:PRINT CHR$(238);:SOUND 1,180,4:SOUND 1,120,4:SOUND 1,90,4:SOUND 1,50,4:LOCATE F+1,G+1:PRINT" ";: GOTO 170
490 FOR A=1 TO 5:SOUND 1,220,4:SOUND 1,90,4:NEXT A:LOCATE X+1,20:CALL 360:K= PEEK(367):IF (X>12 AND X<21) AND K=32 THEN 510
500 LOCATE X+1,20:PRINT"  ";:GOTO 170
510 LOCATE X+1,21:CALL 360:K=PEEK(367):IF K<>58 THEN 540
520 LEEK=LEEK +1
530 LOCATE X+1,21:PRINT" ";:LOCATE X+1,22:PRINT CHR$(237);:GOTO 170
540 LET LEEK = LEEK +2:LOCATE X,20:PRINT"   ";:LOCATE X,21:PRINT"   ";:LOCATE X,22:PRINT CHR$(237);CHR$(237);CHR$(237);:GOTO 170
550 LOCATE 6,11:PRINT"- G A M E  O V E R -":LOCATE 3,14:PRINT"PRESS ANY KEY TO PLAY AGAIN": FOR F= 1 TO 20:SOUND 1,(F+100),4:NEXT
560 IF SC>HS THEN HS=SC
570 LOSS=0:SC=0:LEEK=0
580 IF INKEY$<>"" THEN 580
590 IF INKEY$ = "" THEN 590
600 CLS: RESTORE: GOTO 80
610 GOTO 610
620 SYMBOL AFTER 230
630 SYMBOL 231,0,0,0,16,0,0,0,0
640 SYMBOL 232,0,0,0,24,24,0,0,0
650 SYMBOL 233,0,0,0,56,56,0,0,0
660 SYMBOL 234,0,0,0,60,60,60,0,0
670 SYMBOL 235,0,0,126,126,126,126,0,0
680 SYMBOL 236,0,0,254,254,254,254,254,0
690 SYMBOL 237,73,145,73,37,74,145,74,73
700 SYMBOL 238,153,58,36,219,219,36,58,153
710 SYMBOL 239,0,0,0,8,62,0,0,0
720 SYMBOL 240,0,0,24,255,66,0,0,0
730 SYMBOL 241,0,0,1,255,37,0,0,0
740 SYMBOL 242,0,0,128,255,164,0,0,0
750 B$=CHR$(239)+" "+CHR$(240)+" "+CHR$(242)+CHR$(242)
760 RETURN
770 LOCATE 11,1:PRINT CHR$(24);"D A M B U S T E R S":LOCATE 11,2:PRINT"  @ PAUL  STANLEY  ";CHR$(24)
780 PRINT:PRINT"The year is 1943.You are in charge of a powerful Beam weapon with which you are to protect a Dam from the bouncing bombsof the British attackers."
790 PRINT
800 PRINT"They  have  several  Lancaster  bombers which fly towards you and release their dangerous bombs, which might miss their target."
810 PRINT
820 PRINT"However,your task is not all that simplefor your weapon is unable to reach the  Lancasters so you must just shoot at thebombs instead.                          You can move your cross-hair sights withthe cursor keys."
830 PRINT:PRINT:PRINT"        PRESS ANY KEY TO CONTINUE."
840 IF INKEY$="" THEN 840 ELSE CLS
850 LOCATE 11,1:PRINT CHR$(24);"D A M B U S T E R S":LOCATE 11,2:PRINT"  @ PAUL  STANLEY  ";CHR$(24)
860 PRINT:PRINT:PRINT
870 PRINT"Even adjusting the sights is difficult  because there is a strong Wind which    causes  your  sights  to move  about."
880 PRINT
890 PRINT"USE THE SPACE BAR TO FIRE."
900 PRINT:PRINT
910 PRINT"Because  of  the power of your weapon,  after 10 shots it is drained of power   and will only be ready by the next time an aeroplane attacks.Your battle is over";
920 PRINT"when too much water has poured through  the broken Dam."
930 PRINT:PRINT:PRINT:PRINT"         PRESS ANY KEY TO START."
940 IF INKEY$="" THEN 940 ELSE CLS
950 RETURN
960 RESTORE 970:FOR x=360 TO 366:READ a:POKE x,a:NEXT:RESTORE: RETURN
970 DATA 205,96,187,50,111,1,201
Title: Re: basic programming tips
Post by: Gryzor on 18:24, 15 June 14
Damn that was hard :D
Title: Re: basic programming tips
Post by: AMSDOS on 11:47, 16 June 14
Damn that was hard :D


Yesterday was a funny day gaming wise, I typed in this game and in it's unchanged condition the game is still playable  with BASIC 1.1 except if you don't shoot down one of the bombs, the bomb puts a hole in your Dam guaranteed which causes the Dam to Leak until no water is left and it's game over. It's funny though how you can keep on shooting Bombs and increasing your score even when the Dam is leaking.  :D  In BASIC 1.0 I cracked the game once until I got up to a score of 620 or so and got a Memory Full error. Not sure why that happened.


It's probably not the best example of TXT RD CHAR being used, other 464 games are unplayable with BASIC 1.1 because of TXT RD CHAR, the simple answer is BASIC 1.1 Firmware cannot perform what TXT RD CHAR does on a 464, which is simply detect if anything is within the cursors position, if it's text based an ASCII result it all well and good for all machines, but if it's something graphical, the 464 will return the result of 0, if it's only graphical the 1.1 Firmware will return 32.


So the way I see it, the error here is a fundamental firmware routine was changed, so it's no longer possible to return a 0 result if it's a 664/6128. Certainly hair pulling stuff given Dambusters was written in 1984 in possibly one of the first books to come out for the 464. I can only wonder if it was a deliberate ploy to modify the 1.1 firmware. :(


I can write another example of where this fault stands out and simulate the results on those machines if you like.
Title: Re: basic programming tips
Post by: AMSDOS on 15:10, 17 June 14
I made my own test program for TXT RD CHAR and found an interesting discovery. Initially the program was working on all machines, which means even graphics can return results of 0 if they are in that cursor position. This left only one test to do, the colour of the graphics. This test seems to confirmed the explanation of the different results on the different computers. On a 464, it doesn't seem to matter what colour of the graphics is, however this seems to be a big deal with BASIC  1.1s Firmware when the TXT RD CHAR check is carried out. A difference in PEN colour won't trigger the 0 result from TXT RD CHAR, unless it's in the same colour, where's a 464 will return 0 if any graphic in any colour is present in that position.
Test this program below by changing line 70 to include a PEN 2 at the start and the Ball will bounce in the inner square and watch how it escapes the square when the Ball is in a different colour.


On the 464 the Ball will continue to bounce in that square regardless of the colour of it.



Is that unusual?  :)


Code: [Select]
10 MODE 1:INK 1,26:BORDER 2
20 SYMBOL 255,60,126,126,126,126,126,126,60
30 BALL$=CHR$(255)
40 X=14:DIR=1
50 GOSUB 1000:GOSUB 2000:GOSUB 3000
60 WHILE INKEY(47)=-1
70 CALL &BD19:CALL &BD19
80 LOCATE X,11:PRINT CHR$(255)
90 LOCATE X+DIR,11:CALL 360:RESULT%=PEEK(367)
100 IF RESULT%<>32 THEN DIR=NOT DIR-1
110 OLDX=X:X=X+DIR
120 LOCATE OLDX,11:PRINT" "
130 WEND:MODE 2:CALL &BC02:END
1000 PLOT 200,150,2:DRAW 200,300:DRAW 350,300:DRAW 350,150:DRAW 200,150
1010 RETURN
2000 LOCATE 2,5:PRINT CHR$(150);STRING$(36,CHR$(154));CHR$(156)
2010 FOR Y=6 TO 20:LOCATE 2,Y:PRINT CHR$(149):LOCATE 39,Y:PRINT CHR$(149):NEXT Y
2020 LOCATE 2,21:PRINT CHR$(147);STRING$(36,CHR$(154));CHR$(153)
2030 RETURN
3000 FOR addr=&168 TO &16F
3010 READ a$
3020 POKE addr,VAL("&"+a$)
3030 NEXT addr:RETURN
3040 DATA cd,60,bb,32,6f,01,c9,00
Title: Re: basic programming tips
Post by: Singaja on 20:06, 16 February 15
Hi. I've been a ghost reader for a little and I guess it's high time to say hi everyone ;-) I stumbled upon this article about overscanning, and was astonished it could be done in pure Basic. Inspired I browsed through all this crtc stuff on the wiki and ended up with this ;-)
Code: [Select]
10 BORDER 0
20 OUT &BC00,2
30 OUT &BD00,46
40 OUT &BD00,45
45 OUT &BD00,45
50 OUT &BD00,44
60 OUT &BD00,45
65 OUT &BD00,45
70 OUT &BD00,46
80 OUT &BD00,47
85 OUT &BD00,47
90 OUT &BD00,48
100 OUT &BD00,47
105 OUT &BD00,47
106 OUT &BC00,2
110 GOTO 20
I'd love to add this blinking tape effects on the borders ,can it be done in basic?
Title: Re: basic programming tips
Post by: ZbyniuR on 23:02, 16 February 15
How to put machine code in Basic without FOR READ POKE NEXT DATA. Short and fast way, just in one PRINT. :D

Line 5 is need only once to generate 32 characters. Place them in quotes in PRINT in line 10. Now line 5 is no needed any more. Save and RUN and press any keys. ;)

This is LDIR demonstration. Working after reset or after SYMBOL AFTER 240. :)

Of course CAT is just tu show whatever on screen. Because CALL HIMEM+13 make copy from &c000 to &4000, and CALL HIMEM+1 opposite. :)

5 FOR a=1 TO 63 STEP 2:PRINT CHR$(1)CHR$(VAL("&"+MID$("19F02101401101C001D019F13FEDB0C92101C01119F2014001D03FEDB0C90432",a,2)));:NEXT:EDIT 10
10 PRINT"":CAT:CALL HIMEM+13:WHILE-1:CLS:CALL &BB18:CALL HIMEM+1:CALL &BB18:WEND

Title: Re: basic programming tips
Post by: AMSDOS on 02:54, 16 January 16
Following on from the last program, I was able to store a series of INK Palette Colours in a String, and get it's VALue when setting those INKs.
Because values range from 00 to 26, I need a second variable to increment by 2 & Single Values have to start with a '0'.


I've lengthened the program with a little Demo, the actual routine occupies Lines 100 to 150.


Code: [Select]
10 CALL &BC02
20 MODE 0
30 GOSUB 160
40 CALL &BB18
50 MODE 0:GOSUB 100
60 CALL &BB18
70 PEN 1
80 END
100 cols$="00261102010306162522151319181409"
110 i%=1
120 FOR p%=0 TO 15
130  INK p%,VAL(MID$(cols$,i%,2))
140  i%=i%+2
150 NEXT p%
160 FOR p%=0 TO 15
170  PEN p%
180  PRINT "Pen ";p%
190 NEXT p%
200 RETURN


I was also able to setup a Graphical Figure using a String Array, but thought this might be more useful.  :)
Title: Re: basic programming tips
Post by: ZbyniuR on 11:01, 16 January 16
Palette in string?

10 c$="\0@@\1zz\2kk\3bb\4aa\5cc\6ff\7pp\8yy\9vv\:oo\;mm\<ss\=rr\>nn\?ii":PRINT c$

Type it but instead "\" put ctrl+"\" and effect is the same, without FOR-NEXT. This is exactly your palette. :)
Title: Re: basic programming tips
Post by: AMSDOS on 11:43, 16 January 16
Palette in string?

10 c$="\0@@\1zz\2kk\3bb\4aa\5cc\6ff\7pp\8yy\9vv\:oo\;mm\<ss\=rr\>nn\?ii":PRINT c$

Type it but instead "\" put ctrl+"\" and effect is the same, without FOR-NEXT. This is exactly your palette. :)


What about Graphics?
Title: Re: basic programming tips
Post by: Morri on 12:38, 16 January 16
I'm only just getting my head around control codes after 25+ years and I LOVE THEM!
Only 1 year late but I will definitely use the control code/machine code trick in my next game.

I've attached my attempt to make multicolored sprites using control codes.

The sprite that prints out is my new character for a BASIC game I am working on.

In the one line there are commands for MODE, BORDER, INK, PEN, SYMBOL, transparent (chr$(22)) and the characters to print.
Title: Re: basic programming tips
Post by: Urusergi on 18:29, 16 January 16
Palette in string?

10 c$="\0@@\1zz\2kk\3bb\4aa\5cc\6ff\7pp\8yy\9vv\:oo\;mm\<ss\=rr\>nn\?ii":PRINT c$

Type it but instead "\" put ctrl+"\" and effect is the same, without FOR-NEXT. This is exactly your palette. :)

Incredible but true!! this is a valid basic program:

?"\0@@\1zz\2kk\3bb\4aa\5cc\6ff\7pp\8yy\9vv\:oo\;mm\<ss\=rr\>nn\?ii"

 ;D
Title: Re: basic programming tips
Post by: Morri on 20:50, 16 January 16
Here is a screenshot of my "BASIC" listing. Really quite amazing that this works.  :o

The dsk image is in my post above for anyone who would like to see the result.
 
(http://s13.postimg.org/4mu096aiv/control_code_sprite.jpg)
Title: Re: basic programming tips
Post by: ZbyniuR on 03:53, 17 January 16
Nice example of sprite in SYMBOL, but I prefer narrow text. So 4 colors in 1 character without CHR$(22), work little bit faster. Try GUI.BAS with sprite arrow pointer 100% Basic.
Or GUI2.BAS if you don't have joystick (move pointer by F1,F2,F3,F5).

And another example SYMBOL in string, to short program define national letters. All German signs in 1 line, the same other language exept Greek which need 2 lines. ;)   ALPHABET.BAS - type CONT or press "F." to change language. :)

Title: Re: basic programming tips
Post by: Morri on 07:05, 17 January 16
Wow! You blew my mind with the GUI code.
I didn't know the multi-coloured characters were even possible. How did you do that? I assume it has something to do with your .fnt file but to be fair, trying to read control codes is near impossible so I have no idea what happening past the first line setting MODE and INKS.  :laugh:
Title: Re: basic programming tips
Post by: pelrun on 08:49, 17 January 16
Being able to set colours and move the cursor using control codes lets you do overprinting. Set transparent mode, set the first colour, print a character, move the cursor back, set the second colour, print the second character, repeat.
Title: Re: basic programming tips
Post by: Morri on 09:02, 17 January 16
Being able to set colours and move the cursor using control codes lets you do overprinting. Set transparent mode, set the first colour, print a character, move the cursor back, set the second colour, print the second character, repeat.
I might be wrong, but this seemed different. I created my sprite using the technique you have just mentioned (my sprite is 16x16, 2 colour, 8 characters in all), but if you *break* ZbyniuR's code and type the character set, there are multi-coloured characters immediately (i.e. no over printing). This is what I am referring to.
Title: Re: basic programming tips
Post by: pelrun on 11:42, 17 January 16
Hmm, you're right. Looks like the firmware print routine has been modified to interpret the character data as 4x8 4-colour instead of 8x8 2 colour.
Title: Re: basic programming tips
Post by: ZbyniuR on 12:15, 17 January 16
Half of games on CPC is using narrow font 4x8 in 4 colors in MODE 0, quite often with horizontal stripes or antialiasing with pixels in different color on corners.
To do that just type   MODE 1:CALL &BD1C  of course you have to prepare right font because original it that case look terrible. :)
Similar way is possible to made 80 column with narrow font in MODE 1.
Just type   MODE 2:CALL &BD1C,1    Font for this mode have to be different than to previous mode. In character's matrix 8x8 left half defines color 1, right half defines color 2, like this: 11112222. If the same pixel is on, on both halves that mean color 3.

If both halves look exactly the same then  POKE 46895 change color of pen, but this is mask not color, so: 240=color1, 15=2, 255=3, 0=0. Others values makes vertical stripes. Next byte do it the same with PAPER. :)
I mean those POKEs work in 6128, cause in 464 this address is 45711.
If both halves look different proper value is 255 or just type PEN 3. Copy cursor work only in thats case.

In 40 column in MODE 0, font and colors are more complicated. In sign matrix  first half with first color and second are mixed like this: 11221122. PEN and PAPER from 0 to 3 you can change like in normal MODE 1. Others colors you can choose by POKE like above, but value for color 0 to 15 are: 0, 192, 12, 204, 48, 240, 60, 252, 3, 195, 15, 207, 51, 243, 63, 255. Others values makes vertical stripes. :)

To change between those 2 different narrow font 11112222 and 11221122 you have do with each byte of this font:
b=PEEK(a):POKE a,(b AND 195)+(b AND 48)/4+(b AND 12)*4

One more thing. If you planning use colorful font in graphics mode TAG as sprite, you have to move left-right by whole byte, or prepare least 2 version for mode 0 or least 4 version for mode 1. In other case will be look disaster. ;)

Try program NARROW.BAS  type CONT to look next trick. ;)  80IN3COL.BAS is funny too. Program 0.BAS can show fonts.
Title: Re: BASIC programming tips
Post by: ZbyniuR on 22:27, 17 January 16
I hope now is everything clear how to do narrow text in CPC.
Only thing we still need is editor for narrow color fonts.
Title: Re: BASIC programming tips
Post by: Gryzor on 15:21, 19 January 16
Really lovely sets! And I love the syntax 'highlighting' :D
Title: Re: basic programming tips
Post by: AMSDOS on 23:59, 24 January 16
Wow! You blew my mind with the GUI code.
I didn't know the multi-coloured characters were even possible. How did you do that? I assume it has something to do with your .fnt file but to be fair, trying to read control codes is near impossible so I have no idea what happening past the first line setting MODE and INKS.  :laugh:


I nearly forgot, I typed in this program (http://www.cpc-power.com/index.php?page=detail&num=10865) from Home Computing Weekly from 1985. So the concept is very old, but their using CHR$ instead of control codes to get the job done.  :)
Title: Re: BASIC programming tips
Post by: Morri on 11:07, 30 January 16
I nearly forgot, I typed in this program (http://www.cpc-power.com/index.php?page=detail&num=10865) from Home Computing Weekly from 1985. So the concept is very old, but their using CHR$ instead of control codes to get the job done.  :)
For my new game, I have decided on a mixture of using CHR$ and control codes.
It was far easier to use the SYMBOL command over the control code equivalent CHR$(25) or CTRL-Y, but putting a multicoloured sprite into a string with all needed control codes makes for much shorter code.
I looked at @ZbyniuR (http://www.cpcwiki.eu/forum/index.php?action=profile;u=840) 's font file technique but decided against it as I just want the one basic file for this project (In case I enter it into this year's CPCretroDev2016 comp as last years rules did not allow any machine code call's or multi-load games. ::)
Title: Re: BASIC programming tips
Post by: AMSDOS on 12:17, 30 January 16
For my new game, I have decided on a mixture of using CHR$ and control codes.
It was far easier to use the SYMBOL command over the control code equivalent CHR$(25) or CTRL-Y, but putting a multicoloured sprite into a string with all needed control codes makes for much shorter code.


In that case, you might want to check out @HAL 6128 (http://www.cpcwiki.eu/forum/index.php?action=profile;u=365) Color Flash game (http://www.cpcwiki.eu/forum/programming/silly-programming-ideas-turning-text-into-graphics/msg83537/#msg83537), which also makes good use of Control Codes for the Graphics it's producing. The project wasn't completed and there were a few bugs with it, I tried to isolate where the bugs were in the program, though couldn't figure how to address the issue. Not that I'm trying to pass the program around for it to get fixed, there might be some useful code snippets for your program.
Title: Re: BASIC programming tips
Post by: ZbyniuR on 19:06, 25 March 16
Walking Lemming in one line. :)

Code: [Select]
10 MODE 1:SYMBOL 244,60,56,60,24,24,24,56,24:SYMBOL 245,20,56,60,24,58,26,52,48:SYMBOL 246,0,40,56,60,24,24,60,102:SYMBOL 247,24,60,60,24,24,28,120,76:WHILE-1:FOR a=244 TO 247:PRINT CHR$(a)CHR$(8 );:FOR b=1 TO 80:NEXT:NEXT:WEND
Title: Re: BASIC programming tips
Post by: Morri on 20:27, 25 March 16
Basic Help Wanted: Is there a way in Basic only (no asm) to disable multiple button presses? The game I am currently making uses the left & right cursor keys and the space bar. If you press two of these keys at the same time, the character on screen does all sorts of weird things.
I've disabled it to a point by using - IF INKEY(button1)=0 AND INKEY(button2)=0 then loop but the odd weird movement still gets through every now and then.

Just wondering if theres a different / better way.
Title: Re: BASIC programming tips
Post by: ZbyniuR on 20:42, 25 March 16
Do you need combination space and arrow simultaneous?

If not, maybe better is use INKEY$ insted INKEY(). One more benefit is you can KEY DEF  left, right and fire in joystick the same codes as arrows and space and both way of steering will be work without extra INKEY$.

If yes, you can check both keys in one line with ELSE like this:
IF INKEY(b1)=0 THEN (how react on b1...) ELSE IF INKEY(b2)=0 THEN (how react on b2...)
Now is not possible to notice both keys together.
Or use both my ideas. :)


Title: Re: BASIC programming tips
Post by: ZbyniuR on 23:55, 25 March 16
The first rule of inventiveness is: Everything can be done better.

Here you are. Bunch of colorful Lemmings moving on screen.
Still in 1 line of pure Basic. I'm proud about myself. ;)

If you swap digits 7 and 2 in RIGHT$ and LEFT$ they walk backward. :D

Title: Re: BASIC programming tips
Post by: AMSDOS on 00:26, 26 March 16
Very impressive.
Title: Re: BASIC programming tips
Post by: Morri on 07:18, 26 March 16
The first rule of inventiveness is: Everything can be done better.

Here you are. Bunch of colorful Lemmings moving on screen.
Still in 1 line of pure Basic. I'm proud about myself. ;)

If you swap digits 7 and 2 in RIGHT$ and LEFT$ they walk backward. :D

[youtube]https://www.youtube.com/watch?v=gXN1yxax448[/youtube]

Amazing!
Title: Re: BASIC programming tips
Post by: Morri on 07:20, 26 March 16
Do you need combination space and arrow simultaneous?

If not, maybe better is use INKEY$ insted INKEY(). One more benefit is you can KEY DEF  left, right and fire in joystick the same codes as arrows and space and both way of steering will be work without extra INKEY$.

No simultaneous key presses. Your KEY DEF suggestion intrigues me, do you have an example?
Title: Re: BASIC programming tips
Post by: ZbyniuR on 12:30, 26 March 16
To read keys left and right and space by INKEY()
50 IF INKEY(8 )=0 THEN x=MAX(x-1,1) ELSE IF INKEY(1)=0 THEN x=MIN(x+1,40) ELSE IF INKEY(47)=0 THEN 100
60 GOTO 50
MAX and MIN makes x never is smaller than 1 or bigger than 40. Of course you can put other value. :)
If you want steering joystick <-, -> & fire in the same way
55 IF INKEY(74)=0 THEN x=MAX(x-1,1) ELSE IF INKEY(75)=0 THEN x=MIN(x+1,40) ELSE IF INKEY(76)=0 THEN 100
but loop checking keys become longer, so work little bit slower.

So we can use INKEY$ for the same keys:
50 i$=INKEY$:IF i$=CHR$(242) THEN x=MAX(x-1,1) ELSE IF i$=CHR$(243) THEN x=MIN(x+1,40) ELSE IF i$=" " THEN 100
To add serring by joystick KEY DEF before keys loop:
30 KEY DEF 74,1,242:KEY DEF 75,1,243:KEY DEF 76,0,32
In that case keys loop is still in 1 line, so still work fast. :)
Insted CHR$(242) can be "<"   to made it, type without line number:
?CHR$(242)     and EDIT 50   use copy cursor to put there arrow character.

If someone need checking up and down too. May just add more keys in the same line. INKEY() or INKEY$ whatever. But if need possibility to steering diagonal move too, it have to be done with INKEY() and vertical keys must be check in other line than horizontal. I made it in my GUI in post #53. Thats all. :)


PS.: My Lemmings can be little bit shorter, just instead LOCATE use PRINT"0LJ"l$l$.... of course 3 characters in quotation marks must be type with Control. I made in line 20 in the same way walking Smurfs. :) And I try in 30 made Minions, but they look terrible. I can't make them pretty in 8x8 in 4 colors in 4 frame, because they have so big bellies and so small legs. :(
Maybe I will try in the future, it could be fun to do simple game with Minions, including crazy violet Minions. :D
Title: Re: BASIC programming tips
Post by: arnoldemu on 08:28, 07 August 16
The first rule of inventiveness is: Everything can be done better.
I missed this post until now. This is awesome.
Title: Re: BASIC programming tips
Post by: AMSDOS on 09:22, 07 August 16
I missed this post until now. This is awesome.


It's blows my BASIC code away!  :o
Title: Re: BASIC programming tips
Post by: Morri on 06:43, 12 March 17
Does anyone have some simple BASIC code to have the following sequence of numbers...
1 2 3 2 1 2 3 2 1 2 3 etc...
I can do it in the following code but just wondering if someone can do it faster.

Code: [Select]
10 a=1
20 IF a=1 THEN b=1
30 IF a=3 THEN b=-1
40 PRINT a
50 a=a+b
60 GOTO 20
Title: Re: BASIC programming tips
Post by: AMSDOS on 07:59, 12 March 17
Code: [Select]
5 DEFINT a-b


??

Title: Re: BASIC programming tips
Post by: Morri on 20:51, 12 March 17
Code: [Select]
5 DEFINT a-b
??
Not quite was I was meaning (but you are right). More like is there a BASIC command (i.e. MOD, XOR, AND etc...) that could do this sequence without the IF statements. I feel that is the section that slows the program down.
Title: Re: BASIC programming tips
Post by: remax on 21:25, 12 March 17
perhaps use "x MOD y"


Code: [Select]
5 DEFINT a
10 a=1
20 a=a+1
30 PRINT a MOD 3+1
40 GOTO 20
Title: Re: BASIC programming tips
Post by: ronaldo on 22:05, 12 March 17
Easiest way I can think of  :D
Code: [Select]
10 PRINT "1 2 3 2 ":GOTO 10
Title: Re: BASIC programming tips
Post by: AMSDOS on 22:29, 12 March 17
Not quite was I was meaning (but you are right). More like is there a BASIC command (i.e. MOD, XOR, AND etc...) that could do this sequence without the IF statements. I feel that is the section that slows the program down.
I've seen the functions MAX & MIN being used instead of IF statements to determine if a variable reaches a certain value. There's a little bit of information about it in Basically BASIC Amstrad Amstrad article in Issue 112. The idea there is MIN can be used so the variable never becomes larger than the number you want and MAX is used so the number never drops below the number you want.

Does that make sense?

Though the @remax (http://www.cpcwiki.eu/forum/index.php?action=profile;u=314) example probably does the trick.  :D


Okay, so the example relax posted just goes 1 2 3 1 2 3 instead of 1 2 3 2 1 2 3. Using MAX & MIN there are a couple of ways of writing it, though it still involves IF statements and to be honest they are probably going to be slower than what you've written. So my examples look like this:


Code: [Select]
20 IF a=MAX(a,3) THEN b=-1
30 IF a=MIN(a,1) THEN b=1
40 PRINT a;
50 a=a+b



or:


Code: [Select]
20 IF MAX(a,3)=3 THEN b=-1
30 IF MIN(a,1)=1 THEN b=1



and so on...




Easiest way I can think of 
Code: [Select]
10 PRINT "1 2 3 2 ":GOTO 10



It just needs a semi-column ";" then you will be in business.  :)
Title: Re: BASIC programming tips
Post by: remax on 23:35, 12 March 17
My bad, i had read the goal a bit too fast...


Code: [Select]
5 DEFINT a
10 a=0
20 a=a MOD 4+1
30 PRINT 2-(8*a)/3+2*a*a-a*a*a/3
40 GOTO 20



:P
Title: Re: BASIC programming tips
Post by: Urusergi on 23:46, 12 March 17
 The sequence without the IF statements... not sure if it's faster than yours
Code: [Select]
10 DEFINT a,b:a=3
20 DIM b(3):b(0)=1:b(1)=2:b(2)=3:b(3)=2
30 a=(a+1) MOD 4
40 PRINT b(a):GOTO 30
Title: Re: BASIC programming tips
Post by: Urusergi on 00:07, 13 March 17
Code: [Select]
30 PRINT 2-(8*a)/3+2*a*a-a*a*a/3
a better approach...
Code: [Select]
30 PRINT ((a+2)*a*a+2) MOD 4
Title: Re: BASIC programming tips
Post by: AMSDOS on 00:32, 13 March 17
The array approach is noticeably faster than the mathematical approach. I made some adjustments to @Urusergi (http://www.cpcwiki.eu/forum/index.php?action=profile;u=923) routine to do a more complicated stringed approach to this problem:

Code: [Select]
10 DEFINT a:a=0
20 c$="1 2 3 2 "
30 a=a MOD 8+1
40 PRINT MID$(c$,a,1);:GOTO 30

Note the Spaces in Line 20 are important if you want the spacing right.  :laugh:
Title: Re: BASIC programming tips
Post by: Morri on 01:58, 13 March 17
Thanks for all the great answers guys. I knew there would be a formula to work this out.
I have gone for the combination of @remax (http://www.cpcwiki.eu/forum/index.php?action=profile;u=314) and @Urusergi (http://www.cpcwiki.eu/forum/index.php?action=profile;u=923) formula as it is the result I am after not the actual printing on the screen.

I must admit the array solutions were clever but of no use in what I am hoping to do.  :P
Title: Re: basic programming tips
Post by: mr_lou on 14:39, 10 July 17
How to put machine code in Basic without FOR READ POKE NEXT DATA. Short and fast way, just in one PRINT. :D

Line 5 is need only once to generate 32 characters. Place them in quotes in PRINT in line 10. Now line 5 is no needed any more. Save and RUN and press any keys. ;)

This is LDIR demonstration. Working after reset or after SYMBOL AFTER 240. :)

Of course CAT is just tu show whatever on screen. Because CALL HIMEM+13 make copy from &c000 to &4000, and CALL HIMEM+1 opposite. :)

5 FOR a=1 TO 63 STEP 2:PRINT CHR$(1)CHR$(VAL("&"+MID$("19F02101401101C001D019F13FEDB0C92101C01119F2014001D03FEDB0C90432",a,2)));:NEXT:EDIT 10
10 PRINT"":CAT:CALL HIMEM+13:WHILE-1:CLS:CALL &BB18:CALL HIMEM+1:CALL &BB18:WEND

I'm rather intrigued by this technique.

Shouldn't it also be possible to achieve the same using variables?

Code: [Select]
code$="<the code string>"
address = peek(@code$+1)+peek(code$+2)*256
call address

The advantage of adding machine code this way is obvious, but what are the disadvantages (if any)?
Title: Re: basic programming tips
Post by: roudoudou on 16:14, 10 July 17
The advantage of adding machine code this way is obvious, but what are the disadvantages (if any)?


this is specific to Amstrad, not compatible with all basic (or CPU)
Title: Re: basic programming tips
Post by: mr_lou on 16:28, 10 July 17
this is specific to Amstrad, not compatible with all basic (or CPU)

That's ok. I only have eyes for the CPC anyway.  ;)
Title: Re: BASIC programming tips
Post by: ronaldo on 17:55, 10 July 17
Thanks for all the great answers guys. I knew there would be a formula to work this out.
I have gone for the combination of @remax (http://www.cpcwiki.eu/forum/index.php?action=profile;u=314) and @Urusergi (http://www.cpcwiki.eu/forum/index.php?action=profile;u=923) formula as it is the result I am after not the actual printing on the screen.

I must admit the array solutions were clever but of no use in what I am hoping to do.  :P
Don't know what your exact goal is, but here you are 2 proposals that may be of interest:
Code: [Select]
10 DEFINT a-z
' Generate numbers consecutively
20 FOR i=1 to 3:?i:NEXT:?2:GOTO 20

Code: [Select]
10 DEFINT a-z
' initialize: i=generated num, m,n=generation parameters
20 i=1:m=3:n=2
30 GOSUB 100:?i:GOTO 30
' Generate next i number
100 i=i XOR m:m=m XOR n:n=n XOR 2:RETURN
Title: Re: BASIC programming tips
Post by: Morri on 03:45, 11 July 17
Thanks @ronaldo (http://www.cpcwiki.eu/forum/index.php?action=profile;u=1227)
I like your use of the XOR to get the desired sequence.
I wanted something for an animation sequence (in BASIC) where I have 3 frames of animation but wanted frame 2 to be displayed every 2nd time. 1,2,3,2,1,2,3 etc...
I haven't ever had time to do anything with it though as I ended up doing other things instead.
Title: Re: BASIC programming tips
Post by: Morri on 08:44, 30 July 17
I have another question which I'm hoping is very simple.

I have a variable counting down from 100. I would like it to print as a 3 digit number therefore adding 0's in front if below 100.
i.e. 99 = 099 and 9 = 009.

I have tried using the PRINT USING command but can't get it to work.

Any ideas?
Title: Re: BASIC programming tips
Post by: mr_lou on 09:18, 30 July 17
Probably not the best solution, but....

Code: [Select]
10 for n=100 to 0 step -1
20 a$=right$("00"+mid$(str$(n),2),3)
30 print a$
40 next
Title: Re: BASIC programming tips
Post by: AMSDOS on 09:29, 30 July 17
I have another question which I'm hoping is very simple.

I have a variable counting down from 100. I would like it to print as a 3 digit number therefore adding 0's in front if below 100.
i.e. 99 = 099 and 9 = 009.

I have tried using the PRINT USING command but can't get it to work.

Any ideas?


I'm doing something like that with this program (http://www.cpcwiki.eu/forum/programming/silly-programming-ideas-turning-text-into-graphics/msg129754/#msg129754), though it's using the Condensed number set to count down.


So what I did was setup an Array n%(0) to n%(2) with the values, so in your case it would be n%(0)=1 n%(1)=0 n%(2)=0 the main guts of the program is Lines 610 to 710 to count down the values. To put those values from an the array into an integer Line 620 puts it into an Integer called Result, 630 to 650 converts the numbers from the Integer array, into a String Array and to impose the Condensed Number set I've added 220 to get the condensed set character.


But having said all that, PRINT USING I think is the proper approach (the reason I did the above related to my Pascal game and incorporating a Condensed Number counting system into it), unfortunately it's an technique I'm not too familiar with But if it can be done, then it would be somewhat faster than the approach I've used. The approach I used was also the technique used for my Silly Number Guessing Game.
Title: Re: BASIC programming tips
Post by: andycadley on 18:20, 06 August 17
Alas PRINT USING doesn't have any way of specifying leading zeroes in the format specifier, so you're best of with the MID$ option.
Title: Re: BASIC programming tips
Post by: Urusergi on 20:14, 06 August 17
Another arithmetic solution:

Code: [Select]
10 FOR i=100 TO 0 STEP -1
20 a$=HEX$(96*(i\100)+6*(i\10)+i,3)
30 PRINT a$
40 NEXT

If it's limited to 99 then we can delete the "96*(i\100)+" part
Title: Re: BASIC programming tips
Post by: AMSDOS on 12:43, 10 September 17
Thanks @ronaldo (http://www.cpcwiki.eu/forum/index.php?action=profile;u=1227)
I like your use of the XOR to get the desired sequence.
I wanted something for an animation sequence (in BASIC) where I have 3 frames of animation but wanted frame 2 to be displayed every 2nd time. 1,2,3,2,1,2,3 etc...
I haven't ever had time to do anything with it though as I ended up doing other things instead.


Ironically I found myself writing an INK animation sequence, though mine alternates between 2 colours going back and forth. Another line is added which begins with the opposite colour back and forth, so when INK is used with alternating colours it appears to be moving. It works through I think the coding could be improved.  ???
Title: Re: BASIC programming tips
Post by: SRS on 23:19, 20 February 18
One thing I just (re)learned today:

Code: [Select]
FOR I=0 to 1000:NEXT
ist FASTER than

Code: [Select]
FOR I=0 TO 1000:NEXT I
I sometimes forget we have interpreter not compiler ;)
Title: Re: BASIC programming tips
Post by: Urusergi on 00:25, 21 February 18
This reminds me that depending on how you write the code you can save bytes in basic.

Code: [Select]
10 FOR I=100 TO 0 STEP -1:NEXT
this take 3 bytes more than:

Code: [Select]
10 FOR I=100TO 0STEP-1:NEXT
Title: Re: BASIC programming tips
Post by: skylas on 15:18, 05 April 18
Hello all

I have a question regarding BASIC command REMAIN
I use AFTER command, and for disabling it i use <PRINT REMAIN(1)>, as it is said in Amstrad handbook.
I would like to ask if this number can be calculated as a variable.
 I tried <PRINT REMAIN(1), X>, but this prints both remaining time and X variable.
I tried also <if PRINT REMAIN>1> or X=REMAIN but these does not help as it results in syntax error.
Is there a way to use this number on calculations?
Title: Re: BASIC programming tips
Post by: skylas on 15:31, 05 April 18
Ok sorry, my mistake! i just had to write x=REMAIN(1) just before entering PRINT REMAIN(1) and disabling it!
Title: Re: BASIC programming tips
Post by: AMSDOS on 02:38, 07 April 18
I'm not really sure if it's necessary to use REMAIN with AFTER, though I don't know the circumstances for which what you're using AFTER in, I suppose if AFTER was in a Loop you may need REMAIN in that situation. What I found with AFTER, is a single jump to a subroutine. I used one in my short game Find Red (http://www.cpc-power.com/index.php?page=detail&num=14243) to clear the mosaic grid, which seemed to work fine in that situation, I don't recall having any REMAIN statements in that program, though all that is, is a single jump to clear the mosaic AFTER that time had elapsed.
Title: Re: BASIC programming tips
Post by: skylas on 14:43, 07 April 18
Yes, generally REMAIN is not necessary to be used with AFTER. It is necessary when, depending on player choices in the game, you want to stop the execution of AFTER subroutine.
Imagine for example that (for your game) you give 30 seconds to the player to make his choice. For this, you can use AFTER, and after 30 seconds print <you lose!>. In this case, AFTER has to be disabled by REMAIN in most cases, in which the player has made the choice before 30 seconds. If it is not, then <You lose> will appear in the next turn

Another problem i had has to do with where the program goes after calling the AFTER subroutine, as it doesn't seem to go in the same line every time (as i wanted to send the execution back to the next turn). Using GOTO in the subroutine wasn't the best choice, because the end of the subroutine by RETURN will not be recognized. What i did is setting a variable to 1 in the AFTER subroutine, and then using IF/THEN commands in 2-3 lines in different places in the list i send the program back to the next turn. Hopefully, this works

Nice game in only 10 lines! Well done! Needing careful observing!
Title: Re: BASIC programming tips
Post by: AMSDOS on 07:07, 08 April 18
Yes, generally REMAIN is not necessary to be used with AFTER. It is necessary when, depending on player choices in the game, you want to stop the execution of AFTER subroutine.
Imagine for example that (for your game) you give 30 seconds to the player to make his choice. For this, you can use AFTER, and after 30 seconds print <you lose!>. In this case, AFTER has to be disabled by REMAIN in most cases, in which the player has made the choice before 30 seconds. If it is not, then <You lose> will appear in the next turn


I see, so in your situation you need to cancel the countdown and reinitialize for the next choice. That will need a REMAIN.

Quote
Another problem i had has to do with where the program goes after calling the AFTER subroutine, as it doesn't seem to go in the same line every time (as i wanted to send the execution back to the next turn). Using GOTO in the subroutine wasn't the best choice, because the end of the subroutine by RETURN will not be recognized. What i did is setting a variable to 1 in the AFTER subroutine, and then using IF/THEN commands in 2-3 lines in different places in the list i send the program back to the next turn. Hopefully, this works


Not quite sure I follow, the AFTER statement is a condition where by when it's set, that will initiate after the time has elapsed from within anywhere in the program, it's possible that if your program is doing something else with variables which might interfere with variables within your programs AFTER subroutine, that may cause problems. Normally what happens in that case is if you have areas of code you need protected from the Event Jump from AFTER, you can use DI to Disable Interrupts & EI to Enable Interrupts.

Quote
Nice game in only 10 lines! Well done! Needing careful observing!


Thanks! :)
Title: Re: BASIC programming tips
Post by: skylas on 16:54, 08 April 18

Not quite sure I follow, the AFTER statement is a condition where by when it's set, that will initiate after the time has elapsed from within anywhere in the program, it's possible that if your program is doing something else with variables which might interfere with variables within your programs AFTER subroutine, that may cause problems. Normally what happens in that case is if you have areas of code you need protected from the Event Jump from AFTER, you can use DI to Disable Interrupts & EI to Enable Interrupts.
I looked again to what i had done, as it couldn't remember exactly. We are talking about the situation when player loses, because he has not made the choice and we want to go to the next turn.
So, in that case, AFTER subroutine has been runned. I create a new unused variable in the subroutine, and define A=1. So, in case the subroutine has been runned, A=1.
Then, in the area of inputing, just after X$=inkey$ and before the input choices i entered a line <if A=1 then GOTO> (go the next turn). Of course, A is set to 0 in the start of each turn.
In that way, the game does not wait for inputing of the player choice, and goes to next turn. If not done, after displaying <you lose>, the game will continue the same turn, not the next turn
Title: Re: BASIC programming tips
Post by: ZbyniuR on 06:23, 09 April 18
Short Basic Screensaver with frames. :)

10 MODE 1:INK 0,0:BORDER 0:DEFINT a-z:a$=CHR$(150):b$=CHR$(156):c$=CHR$(147):d$=CHR$(153):e$=CHR$(154):f$=CHR$(149):g$=CHR$(9)
20 INK RND*2+1,RND*26:PEN RND*15 MOD 3+1:x=RND*29+1:y=RND*22+1:s=RND*10+2:w=RND*6+2:LOCATE x,y:PRINT a$STRING$(s,e$)b$:y=y+1:FOR a=1 TO w:LOCATE x,y:PRINT f$STRING$(s,g$)f$:y=y+1:NEXT:LOCATE x,y:PRINT c$STRING$(s,e$)d$;:GOTO 20
Title: Re: BASIC programming tips
Post by: AMSDOS on 11:45, 09 April 18
I looked again to what i had done, as it couldn't remember exactly. We are talking about the situation when player loses, because he has not made the choice and we want to go to the next turn.
So, in that case, AFTER subroutine has been runned. I create a new unused variable in the subroutine, and define A=1. So, in case the subroutine has been runned, A=1.
Then, in the area of inputing, just after X$=inkey$ and before the input choices i entered a line <if A=1 then GOTO> (go the next turn). Of course, A is set to 0 in the start of each turn.
In that way, the game does not wait for inputing of the player choice, and goes to next turn. If not done, after displaying <you lose>, the game will continue the same turn, not the next turn


I'm not quite sure I follow, but it sounds like the A=1 needs to be in the <You Win> subroutine to take it to the next turn, with the <You Lose> routine to clear variables and restart (RUN) the program.
Title: Re: BASIC programming tips
Post by: skylas on 19:51, 09 April 18
No, maybe my english is not so good and i could't make it clear.
imagine that we expect for an answer from the player.
10 A=0:REM DEFINING VARIABLES, GIVING CHOICES-TURN STARTS
20 AFTER 250 GOSUB 100
30 A$=inkeys
35 if A=1 then goto 10
40 if a$=""A""  then..... etc
50 goto 30
100 PRINT '' YOU LOSE'': A=1:RETURN

In the case of a later answer, LINE 100 IS CALLED. Player loses, but program is still between 30 and 50, and waiting for a player choice.
So, we added line 35 to send the programme to the next turn

Many thx for your interest AMSDOS!
Title: Re: BASIC programming tips
Post by: EgoTrip on 20:07, 09 April 18
Wouldn't it be better to have a WHILE A=0 on line 10, and WEND on line 35, instead of the GOTO?
Title: Re: BASIC programming tips
Post by: AMSDOS on 03:10, 10 April 18
No, maybe my english is not so good and i could't make it clear.


No I think I'm misreading. Thought you had a problem, when in fact you've found a solution.



Title: Re: BASIC programming tips
Post by: skylas on 04:58, 10 April 18
Maybe WHILE can work too. Due to my very limited knowledge, i use mostly very few commands
Yes, AMSDOS, hopefully is working!
Title: Re: BASIC programming tips
Post by: skylas on 04:36, 16 April 18
One more simple question!
When we have an INPUT question of an a$ variable, is there a way to specify that characters must not be over a specific number? (for example i want a$ variable to be not more than 15 characters).
I found LEN, but i wonder if there is a quicker way
Title: Re: BASIC programming tips
Post by: AMSDOS on 10:07, 16 April 18
One more simple question!
When we have an INPUT question of an a$ variable, is there a way to specify that characters must not be over a specific number? (for example i want a$ variable to be not more than 15 characters).
I found LEN, but i wonder if there is a quicker way


As far as I know, INPUT only accepts up to the 255 characters, you could have in your program (15 characters max) and then do some MID$, with something like this (I hope):


Code: [Select]
1000 if LEN(a$)>15 THEN r=15 ELSE r=LEN(a$)
1010 w$=MID$(a$,1,r)


A couple of years ago, I wrote this silly little Condensed Text Name selector using Cursor keys to force the amount of areas to let the user select. It works, but it's a bit cumbersome to use.


EDIT: LEFT$ could also be used, which may have a speed advantage to MID$, so:


Code: [Select]
1010 w$=LEFT$(a$,r)

will do the same job as MID$ in this case.
Title: Re: BASIC programming tips
Post by: skylas on 14:06, 16 April 18
Many thx AMSDOS! I think LEFT$ is fine!!! I just did'nt know it!
Title: Re: BASIC programming tips
Post by: Urusergi on 20:54, 29 April 18
*** Prime numbers from 2 to 997 in ~5 seconds in one Basic line ***

Code: [Select]
10 MODE 2:ZONE 6:DEFINT a-y:z=TIME:p=3:m=1000:c=SQR(m+1):DIM s(m+1):PRINT 2,:WHILE p<c:PRINT p,:FOR i=p+p*2 TO m STEP p*2:s(i)=1:NEXT:p=p+2:WHILE s(p):p=p+2:WEND:WEND:WHILE p<m:PRINT p,:p=p+2:WHILE s(p):p=p+2:WEND:WEND:PRINT:PRINT(TIME-z)/300;"seconds"
The table is reusable. For example, adding this line:

Code: [Select]
20 CALL &BB18:MODE 2:z=TIME:p=3:PRINT 2,:WHILE p<m:PRINT p,:p=p+2:WHILE s(p):p=p+2:WEND:WEND:PRINT:PRINT(TIME-z)/300;"seconds"
it takes only 4 seconds to show the same prime numbers.
Title: Re: BASIC programming tips
Post by: mr_lou on 09:28, 13 October 18
I think this question has been asked before, but:

Is there anyway to (from BASIC) fetch the character or character-code currently on the screen on a certain position?

This would require a character-memory-map of some kind, which I don't think the CPC has?

Something like CHARCODE(x,y)

Or what about using PEEKS?
Title: Re: BASIC programming tips
Post by: ervin on 10:19, 13 October 18
I think this question has been asked before, but:

Is there anyway to (from BASIC) fetch the character or character-code currently on the screen on a certain position?

This would require a character-memory-map of some kind, which I don't think the CPC has?

Something like CHARCODE(x,y)

Or what about using PEEKS?

Not sure if it's what you're after, but COPYCHR$ might be worth a look:
http://www.cpcwiki.eu/index.php/Locomotive_BASIC#COPYCHR.24_.28st.29 (http://www.cpcwiki.eu/index.php/Locomotive_BASIC#COPYCHR.24_.28st.29)

I don't think it works on a 464, but I seem to remember a discussion on cpcwiki a few years ago about a 464 equivalent.
(I could be remembering incorrectly though).
Title: Re: BASIC programming tips
Post by: mr_lou on 10:31, 13 October 18
Not sure if it's what you're after, but COPYCHR$ might be worth a look:
http://www.cpcwiki.eu/index.php/Locomotive_BASIC#COPYCHR.24_.28st.29 (http://www.cpcwiki.eu/index.php/Locomotive_BASIC#COPYCHR.24_.28st.29)

I don't think it works on a 464, but I seem to remember a discussion on cpcwiki a few years ago about a 464 equivalent.
(I could be remembering incorrectly though).
Interesting. Will take a closer look. But it would be nice with something for the 464, since that's the one I'm using for this. (Small enough to be on the same desk as my PC).
EDIT: found this in another thread:
10 FOR a=1 TO 17:POKE 39999+a,VAL("&"+MID$("DD7E00CDB4BBF5CD60BB320000F1C3B4BB",a*2-1,2)):NEXT
20 LOCATE x,y:CALL 40000,0:PRINT CHR$(PEEK(0)) '  this works as COPYCHR$ in any CPC

I used to found this in some magazine. :)

And remember about TEST command, could be usefull to detect pixel color. :)
http://www.cpcwiki.eu/forum/programming/basic-efficient-collision-detection-routine/
Title: Re: BASIC programming tips
Post by: AMSDOS on 14:19, 13 October 18
I think this question has been asked before, but:

Is there anyway to (from BASIC) fetch the character or character-code currently on the screen on a certain position?

This would require a character-memory-map of some kind, which I don't think the CPC has?

Something like CHARCODE(x,y)

Or what about using PEEKS?


Would have to know more about how that character onscreen got there. If it's a program placing and removing characters onscreen, using an array is faster than copychr$(#0) or it's TXT RD CHAR (#BB60) counterpart.
If you were gathering information from a Disk Catalog (#BC9B) through, the firmware can be told where to place that information with the DE register being the address to where the buffer is to go.
Title: Re: BASIC programming tips
Post by: mr_lou on 15:30, 13 October 18
Would have to know more about how that character onscreen got there. If it's a program placing and removing characters onscreen, using an array is faster than copychr$(#0) or it's TXT RD CHAR (#BB60) counterpart.
If you were gathering information from a Disk Catalog (#BC9B) through, the firmware can be told where to place that information with the DE register being the address to where the buffer is to go.

Well the idea is to draw levels for a game, using normal PRINT commands. But these levels only contain very little data - as in most of it would be empty spaces. Therefor I figure an array (or map) would only take up unnecessary much space. Hence why I thought it would be better to be able to simply check what's on the screen.

Of course, speed matters too, so..... dunno.

EDIT:Did a short test. First 1000 checks using the PEEK method above. Took 26,44 seconds. Then 1000 outputs from an array. Took 23,92 seconds. So it's not like a huge difference. 2½ seconds for 1000 cycles. So that's 2½ ms slower per cycle to use PEEK rather than an array. I can live with that.
Title: Re: BASIC programming tips
Post by: AMSDOS on 23:20, 13 October 18
Sorry I got the impression that it was 2.5 seconds (not microseconds), which sounds like a lot if the game is running through a cycle of checks.

I converted this game (http://www.cpcwiki.eu/forum/programming/bomb-drop-by-trevor-johnson/) earlier this year, which had a lot of copychr$(#0) in it. The game is also MODE 1, so I setup a Memory array with a Function pointing to it, which gave me the advantage of having a byte sized array from BASIC, even on a MODE 1 screen I was able to have some redefined characters and the Array situated at &A000. I'm unsure what happened to the original program, though may have somewhere, actually I think the program maybe within this attached file with this message (http://www.cpcwiki.eu/forum/programming/basic-efficient-collision-detection-routine/msg149542/#msg149542).
Title: Re: BASIC programming tips
Post by: AMSDOS on 08:49, 09 July 19
I made this following programme to test the various timings between storing values to an array or poking to memory, along with the Loops they should be using (Single or Nested). I guess my finds won't come as much of a Surprise to other BASIC coders, though I just thought it would be worthwhile including it in this thread anyway.


The results I found was:
* Test 1 which stores a value to a 2D array through a Nested Loop is the Slowest, taking a little over 2 seconds to complete.
* Test 2 which Pokes to value to memory through a Nested Loop took just under 2 seconds.
* Test 3 was the Fastest, which Pokes values to Memory using a Single Loop takes a little over 1 Second.
* Test 4 which stores a value to a single array through a Single Loop took 1 and a half Seconds.


From Lines 460-530 I've setup a Nested Loop and used a calculation similar to the POKE from Nested Loop demo to calculate the correct position within the Single Array in Test 4 just to show how to correctly calculate a position based from 2 variables (x & y), y is multiplied by 20 which represents the Length of the array.


Code: [Select]

100 MODE 2:CALL &BC02
110 DIM g$(20,25)
120 DEFINT a-z
130 ' Test 1 - Store value into 2D Array through Nested Loop
140 t!=TIME
150 FOR y=1 TO 25
160  FOR x=1 TO 20
170    g$(x,y)=CHR$(32)
180  NEXT x
190 NEXT y
200 t1!=TIME:PRINT"Time 1 (2D Array in Nested Loop) =";(t1!-t!)/300
210 CLEAR:DEFINT a-z
220 ' Test 2 - Poke from Nested Loop
230 MEMORY &9FFF
240 t!=TIME
250 FOR y=1 TO 25
260   FOR x=1 TO 20
270     POKE &9FEB+x+(y*20),32
280   NEXT x
290 NEXT y
300 t1!=TIME:PRINT"Time 2 (Memory Poke in Nested Loop) =";(t1!-t!)/300
310 ' Test 3 - Single Loop
320 DEFINT a-z
330 t!=TIME
340 FOR a=1 TO 500
350   POKE &A1FF+a,32
360 NEXT a
370 t1!=TIME:PRINT"Time 3 (Memory Poke in Single Loop) =";(t1!-t!)/300
380 CLEAR:DEFINT a-z:DIM g$(500)
390 ' Test 4 - Store value into Single Array through Single Loop
400 t!=TIME
410 FOR a=1 TO 500
420   g$(a)=CHR$(225)
430 NEXT a
440 t1!=TIME:PRINT"Time 4 (Singular Array in Single Loop) =";(t1!-t!)/300
450 CALL &BB18
460 MODE 0:b=-20
470 FOR y=1 TO 25
480   FOR x=1 TO 20
490     PEN 15:LOCATE x,y:PRINT g$(b+x+(y*20));
500   NEXT x
510 NEXT y
520 CALL &BB18
530 PEN 1:PRINT CHR$(224):CLEAR:END
Title: Re: BASIC programming tips
Post by: AMSDOS on 08:04, 10 July 19
With my programme yesterday producing some fast results from a Single Loop, I wanted to have a look at the problem of calculating the correct X & Y positions from a Single Loop. I'm unsure how performance compares, but I was able to eventually work it out based on the 20x25 Text Screen and Screenshot as Proof.


Code: [Select]

100 MODE 0:BORDER 0:INK 14,15:INK 15,9
110 DEFINT a-z
120 w=20
130 FOR a=1 TO 500
140   y=(a+19)\w
150   x=(a+w)-(y*w)
160   PEN INT(RND*15)+1:LOCATE x,y:PRINT CHR$(248);
170 NEXT a
180 CALL &BB18:PEN 1:END


To make it work I had to calculate 'y' first which adds 19 (width-1) to the position of the loop and then divide by 'w' which is the width. Once I know where 'y' is positioned, I can add the position of the loop with the width and y is multiplied by the width and subtracted to get the value of x, so for example if a=21, 20 is added to get 41, y at that point equals 2, so 2*20 = 40 and 41-40 = 1.
The formulas will slow things down, I'm unsure if it's past the point where Nested Loops would have the edge over this approach, though at least it's solved now.
Title: Re: BASIC programming tips
Post by: ervin on 08:54, 10 July 19
That's a very interesting test.
I couldn't help but try it with nested loops, and then compare the running times.

Code: [Select]
130 for y=1 to 25
140 for x=1 to 20
150 PEN INT(RND*15)+1:LOCATE x,y:PRINT CHR$(248);
160 next x
170 next y

The following times are SCIENTIFICALLY CALCULATED with a stopwatch app on my phone.  :laugh:

single loop: 10.6 seconds
nested loops: 7.8 seconds
Title: Re: BASIC programming tips
Post by: AMSDOS on 12:05, 10 July 19
That's a very interesting test.
I couldn't help but try it with nested loops, and then compare the running times.

The following times are SCIENTIFICALLY CALCULATED with a stopwatch app on my phone.  :laugh:

single loop: 10.6 seconds
nested loops: 7.8 seconds


Cool, I wondered if those calculations were going to a bearing on the timings. As it turns out that test I performed is faster compared to if I had made x & y functions, which overall is nearly a whole second slower again.



Title: Re: BASIC programming tips
Post by: Urusergi on 00:47, 11 July 19
Another approach, with an improvement of ~1 second:
Code: [Select]
...
130 FOR a=0 TO 499
140 y=(a+20)\20
150 x=1+a MOD 20
...
Title: Re: BASIC programming tips
Post by: AMSDOS on 08:48, 11 July 19
 :-[  Unfortunately I'm guilty of over complicating as usual by applying Math for LOCATE to use when the same result works without LOCATE and getting the job done in under 7 seconds with a Single Loop:


Code: [Select]
100 MODE 0:BORDER 0:INK 14,15:INK 15,9
110 DEFINT a-z
120 t1!=TIME
130 FOR a=1 TO 500
140   PEN INT(RND*15)+1
150   PRINT CHR$(248);
160 NEXT a
170 t2!=TIME
180 PEN 1:PRINT"Time =";(t2!-t1!)/300
190 END


I'm following up on this example from above to create a Game Screen with Ladders and Platforms using a Memory Array. It comes two parts, the 1st part fills the memory array with 32 (Lines 1010-1030), poke the platforms & pen colours to the memory array (Lines 1040-1070, Lines 140-180), Line 1060 also places gaps within the Platforms where the Ladders go, this is not needed through as Lines 1080-1090 place the Ladders over those areas from the Routine on lines 200-240. Lines 250-260 handles the Screen Character data and Lines 270-280 is used to store PEN colours.
Once that's done a Time is display onscreen for the Setup Time & Press any key to launch the 2nd part which draws the screen Lines 300-360 followed by the time it takes to do that. Just to play around with the Code a bit I added Line 311 which if the platform character is detected use PAPER 8 for it's background, otherwise it uses PAPER 0. Having that Line there also determines how fast the screen draws with different timings becoming the result as well as if Line 311 is commented, that also changes the Timings too.


Code: [Select]

100 MODE 2:BORDER 0:INK 1,26:INK 3,3
110 SYMBOL 255,255,0,24,36,66,129,0,255
120 SYMBOL 254,129,129,255,129,129,129,255,129
130 GOTO 1000
140 FOR x=1 TO 20
150   POKE &9FEB+x+(y*20),255
160   p=3:GOSUB 270
170 NEXT x
180 RETURN
200 FOR y=3 TO 20
210   GOSUB 250
220   GOSUB 270
230 NEXT y
240 RETURN
250 POKE &9FEB+x+(y*20),v
260 RETURN
270 POKE &A1EB+x+(y*20),p
280 RETURN
300 MODE 0:t1!=TIME
310 FOR a=0 TO 479
311   IF PEEK(&A000+a)=255 THEN PAPER 8 ELSE PAPER 0
320   PEN PEEK(&A200+a):PRINT CHR$(PEEK(&A000+a));
330 NEXT a
340 t2!=TIME
350 PEN 1:LOCATE 1,23:PRINT"Draw Time = ";USING"#.##";(t2!-t1!)/300
360 RETURN
1000 DEFINT a-z:MEMORY &9FFF:t1!=TIME
1010 FOR a=1 TO 500
1020   POKE &9FFF+a,32
1030 NEXT a
1040 FOR y=3 TO 21 STEP 3
1050   GOSUB 140
1060   IF y<>21 THEN x=2:v=32:GOSUB 250:x=18:GOSUB 250
1070 NEXT y
1080 x=2:v=254:p=1:GOSUB 200
1090 x=18:GOSUB 200
1100 t2!=TIME
1110 LOCATE 1,1:PRINT"Setup Time =";(t2!-t1!)/300
1120 CALL &BB18
1130 GOSUB 300
1140 END





Title: Reading character at given screen
Post by: Fabrizio on 23:32, 19 January 20
Is there a way in pure BASIC (+ possibly ROM-only routines) to read the character at a given position, e.g., cursor position.
I know the CPC only has graphics screens. I supposed that a text buffer is used. I have seen simply Assembly used in BASIC to do that. I have not found a BASIC-only (+ROM-only routines) solution. My solution has been to implement myself a buffer to remember the characters on the screen.

Does anyone here have a better idea?
Title: Re: BASIC programming tips
Post by: pelrun on 04:06, 20 January 20
BASIC 1.1 has COPYCHR$(), which will return the character at the cursor position. It does a graphical comparison against the character set, there's no internal char buffer.
Of course, that means 464's are out of luck, but the function is available in the ROM and can be accessed with a small asm routine. There's an example here: https://www.sean.co.uk/books/amstrad/amstrad2.shtm#COPYCHR (https://www.sean.co.uk/books/amstrad/amstrad2.shtm#COPYCHR)
There's also some other examples of how to do it at http://www.cpcwiki.eu/forum/programming/study-of-464-only-game-helicopter-destination-saturn-from-acu/
Title: Re: Reading character at given screen
Post by: AMSDOS on 07:35, 20 January 20
Is there a way in pure BASIC (+ possibly ROM-only routines) to read the character at a given position, e.g., cursor position.I know the CPC only has graphics screens. I supposed that a text buffer is used. I have seen simply Assembly used in BASIC to do that. I have not found a BASIC-only (+ROM-only routines) solution. My solution has been to implement myself a buffer to remember the characters on the screen.Does anyone here have a better idea?



As @pelrun (https://www.cpcwiki.eu/forum/index.php?action=profile;u=1106) mentioned COPYCHR$() is the function to use, apart from it being BASIC 1.1 only, the only other reason I can think of, of why not to use it, is a speed decrease during gameplay.


Other examples include Diamond Hunt (https://www.cpcwiki.eu/forum/cpcwiki-discussion/amstrad-action-type-in-project/msg148195/#msg148195). In those examples the BASIC 1.1 version uses COPYCHR$() and the screen is setup quicker than in my Revised version which used an array, but once the gameplay starts my version is faster than the original. In Bomb Drop (https://www.cpcwiki.eu/forum/programming/bomb-drop-by-trevor-johnson/msg159057/#msg159057) which had a few COPYCHR$() in them, I replaced all that with a MEMORY Array and used POKE & PEEK to store and retreive the contents, it too has some setup time before the game starts, the original game can be found within the Depth Charge zip file in this thread (https://www.cpcwiki.eu/forum/programming/basic-efficient-collision-detection-routine/msg149542/#msg149542), from memory the original had some problems if the Bombs were placed undernearth one another, which meant you couldn't get to them. When I switched the game to using a MEMORY Array, I was able to rectify that problem too.
Another approach (compatable on all systems) if you don't like Array's, is to use TEST(). Unfortunately TEST() only checks for a single Pixel using Graphical co-ordinates, though with Block calculations being used to obtain the XPOS & YPOS. The only problem with that is each graphic would need it's own Identifying Colour pixels and the TEST would have to be done on a spot where all Graphics has it's Colour Representation. I wrote a little BASIC game here (https://www.cpcwiki.eu/index.php/Programming:Coding_a_simple_BASIC_game_into_Assembly#BASIC_Version), which does that.


It really up to you to decide what works best.
Title: Re: BASIC programming tips
Post by: Sykobee (Briggsy) on 14:28, 21 January 20
If you have a map or similar in-memory representation/abstraction of what is being displayed on screen, then always compare against the map rather than the very slow COPYCHR$ command.


Most games should have a map, or at least the current screen, in memory (2K for a full BASIC MODE 1 screen built up on character sized blocks, using an INT% array) - it all depends on the game really.
Title: Re: BASIC programming tips
Post by: AMSDOS on 04:12, 25 January 20

For anyone interested, I made a simple comparision programme to test COPYCHR$, TEST using Functions, TEST using direct values and a BASIC Array (results in the Screenshot below).


The Test simply prints a Man moving Left to Right of the screen replacing and restoring background as it moves, the Gaps in the scenary are a part of testing TEST and have setup a short 2 byte array (a$) so if a gap is found 0 is returned and restores as printing that gap, otherwise it's CHR$(127), which represents 1.


Obviously with the test including COPYCHR$, this will be BASIC 1.1 only, the other tests are compatable in BASIC 1.0 and will work if Lines 210-350 are omitted.




Code: [Select]
100 MODE 1:DEFINT a-z:a$(0)=CHR$(32):a$(1)=CHR$(127):DIM m$(40):DEF FNxp=((x-1)*16):DEF FNyp=398-((y-1)*16)
110 BORDER 0:INK 0,11:INK 1,26
120 LOCATE 1,10:PRINT STRING$(40,127);
130 FOR x=1 TO 40:m$(x)=CHR$(127):NEXT x
140 FOR a=1 TO 3
150   x=(RND*38)+1
160   WHILE TEST(FNxp,398-(9*16))=0
170     x=(RND*38)+1
180   WEND
190   m$(x)=" ":MOVE FNxp,398-(9*16):TAG:PRINT CHR$(32);:TAGOFF
200 NEXT a
210 x=1:y=10
220 LOCATE x,y:a$=COPYCHR$(#0)
230 PRINT CHR$(250);
240 t1!=TIME
250 WHILE x<>40
260   LOCATE x,y
270   x=x+1
280   PRINT a$;
290   LOCATE x,y:a$=COPYCHR$(#0)
300   PRINT CHR$(250);
310 WEND
320 t2!=TIME
330 LOCATE 1,1:PRINT "COPYCHR$ Time =";USING"##.##";(t2!-t1!)/300
340 CALL &BB18
350 LOCATE 40,10:PRINT CHR$(127);
360 x=1:y=10
370 p=TEST(FNxp,FNyp)
380 MOVE FNxp,FNyp:TAG:PRINT CHR$(250);:TAGOFF
390 t1!=TIME
400 WHILE x<>40
410   MOVE FNxp,FNyp
420   x=x+1
430   TAG:PRINT a$(p);
440   MOVE FNxp,FNyp
450   p=TEST(FNxp,FNyp)
460   PRINT CHR$(250);:TAGOFF
470 WEND
480 t2!=TIME
490 LOCATE 1,2:PRINT "TEST using Functions Time =";USING"##.##";(t2!-t1!)/300
500 CALL &BB18
510 LOCATE 40,10:PRINT CHR$(127);
520 x=0:y=254
530 p=TEST(x,y)
540 MOVE x,y:TAG:PRINT CHR$(250);:TAGOFF
550 t1!=TIME
560 WHILE x<>624
570   MOVE x,y
580   x=x+16
590   TAG:PRINT a$(p);
600   MOVE x,y
610   p=TEST(x,y)
620   PRINT CHR$(250);:TAGOFF
630 WEND
640 t2!=TIME
650 LOCATE 1,3:PRINT "TEST with direct values Time =";USING"##.##";(t2!-t1!)/300
660 CALL &BB18
670 LOCATE 40,10:PRINT CHR$(127);
680 x=1:y=10
690 v$=m$(x)
700 LOCATE 1,10:PRINT CHR$(250);
710 t1!=TIME
720 WHILE x<>40
730   LOCATE x,y
740   x=x+1
750   PRINT v$;
760   LOCATE x,y
770   v$=m$(x)
780   PRINT CHR$(250);
790 WEND
800 t2!=TIME
810 LOCATE 1,4:PRINT "Array Time =";USING"##.##";(t2!-t1!)/300
820 END



Title: Re: BASIC programming tips
Post by: mv on 20:05, 25 January 20
For anyone interested, I made a simple comparision programme to test COPYCHR$, ...
Hey, great! Without knowing about your test (I will try it later), I also made a test for COPYCHR$, today. It has also some machine code for all CPCs...

Did you know that COPYCHR$ (or call &BB60, TXT RD CHAR) not only recognizes a character with the selected PEN, but also its inverse with the selected PAPER?
That means the PEN or PAPER can be pixelized without affecting the result, but not both. This also means that an inverted character cannot be recognized.
And if no PEN bits and no PAPER bits are set, there is a slight difference: The CPC 664/6128 returns space (char 32) and the CPC 464 the inverse of space (char 143).
Any more subtle findings?

Demo:
Code: [Select]
10 REM fancy - test copychr$
20 MODE 1:CLEAR:DEFINT a-z:RANDOMIZE TIME:PEN 1:PAPER 0
30 useCopychr=0: 'set this to use copychr$
40 c$=CHR$(&CD)+CHR$(&60)+CHR$(&BB)+CHR$(&32)+CHR$(0)+CHR$(0)+CHR$(&C9): 'call &BB60 (TXT RD CHAR)
50 fancy$="F"+CHR$(15)+CHR$(2)+"a"+CHR$(15)+CHR$(3)+"n"+CHR$(15)+CHR$(1)+CHR$(24)+"cy"+CHR$(24)+" s"+CHR$(14)+CHR$(2)+"t"+CHR$(14)+CHR$(3)+"u"+CHR$(14)+CHR$(0)+"ff"+"!"
60 LOCATE 1,1:PRINT fancy$;" ";fancy$
70 ' pixelize the first 12 characters
80 xstart=0
90 FOR i=1 TO 12
100 ckeep=1:IF i=2 OR i=3 OR i=7 OR i=10 THEN ckeep=0: 'the color to keep (1=pen or 0=paper)
110 FOR y=0 TO 7:FOR x=0 TO 7
120 xp=xstart+x*2:yp=399-y*2
130 IF TEST(xp,yp)=ckeep THEN 160
140 c=INT(RND*3+0.5):IF c=ckeep THEN 140
150 PLOT xp,yp,c :'set random color which is not the kept color
160 NEXT x,y
170 xstart=xstart+2*8
180 CALL &BD19:NEXT i
190 ' recognize characters in first line
200 a$="":FOR i=1 TO 12*2+1:LOCATE i,1:
210 GOSUB 290
220 a$=a$+t$
230 NEXT
240 LOCATE 1,5:PRINT a$;
250 t!=TIME+600:WHILE TIME<t! AND INKEY$="":CALL &BD19:WEND:LOCATE 1,5:PRINT CHR$(18);
260 GOTO 80
270 '
280 'call &BB60 (TXT RD CHAR) as COPYCHR$ on any CPC
290 IF useCopychr OR PEEK(&BB60)<>&CF THEN t$=COPYCHR$(#0):GOTO 330: 'flag set or CPCBasic
300 a%=UNT(PEEK(@c$+1)+PEEK(@c$+2)*&100)
310 CALL a%:b%=PEEK(0)
320 t$=CHR$(b%)
330 RETURN

(Can be found on: https://benchmarko.github.io/CPCBasic/cpcbasic.html?example=test/fancy)
Title: Re: BASIC programming tips
Post by: AMSDOS on 22:14, 25 January 20
Did you know that COPYCHR$ (or call &BB60, TXT RD CHAR) not only recognizes a character with the selected PEN, but also its inverse with the selected PAPER?


Yes, I found out about that when I was fixing up Destination Saturn.

Quote
That means the PEN or PAPER can be pixelized without affecting the result, but not both. This also means that an inverted character cannot be recognized.


Quote
And if no PEN bits and no PAPER bits are set, there is a slight difference: The CPC 664/6128 returns space (char 32) and the CPC 464 the inverse of space (char 143).



er? As long as the PEN & PAPER checks match the colour when the COPYCHR$ check is done, it should be fine, but I found on a 464 it doesn't seem to matter.




Quote
Any more subtle findings?


If a different Graphical colour is used, COPYCHR$ won't pickup on it unless it's in the same colour scheme, I made a test programme of it here (https://www.cpcwiki.eu/forum/programming/basic-programming-tips/msg81806/#msg81806), which you've proabably seen.


Title: Re: BASIC programming tips
Post by: AMSDOS on 11:37, 15 February 20
I made a little routine to print out the Line Lengths of a BASIC programme, useful for working out which category your BASIC programme belongs in or adjusting it to size for those 10-Liners.


Code: [Select]
60000 a=&170
60010 v=PEEK(&170)
60020 n=0
60030 FOR l=1 TO 10
60040 PRINT v
60050 n = a + v
60060 a = n
60070 v = PEEK(n)
60080 CALL &BB18
60090 NEXT l
Title: Re: BASIC programming tips
Post by: Gryzor on 12:31, 15 February 20
But this will increase your listing length 😄
Title: Re: BASIC programming tips
Post by: AMSDOS on 14:04, 15 February 20
But this will increase your listing length 😄


Only to show a rundown of the process involved, it's that short a routine that it can be entered as a series of statements like this:


Code: [Select]
a=&170:v=PEEK(&170):n=0:FOR L=1 TO 10:PRINT v:n = a + v:a = n:v = PEEK(n):CALL &BB18:NEXT L

or from a KEY <num>,... statement.


The code works by obtaining the actual length of each line, BASIC programmes begin at &170 or 368 with the 1st byte representing that Line Length, so to get the Line Length of the second line and so on, the address (a) is added with the Line Length value (v) to get the new value (n) and then address (a) becomes that new value (n) and v becomes the next Line Length and so on until L = 10.


So I tested it on the Red's Exit game I wrote back in 2017 for the BASIC 10-Liners Contest and to my surprise 1 of the 10 Lines exceeded the Length category the game was put in, the funny thing about that is when a character count is carried out, it comes up shorter, which kind of explains why in some cases if too many variables are used on the same line a "Line Too Long" Error results.


Though here are the results I got from Red's Exit:
Code: [Select]
102
80
89
115
66
113
127
108
91
71
Title: Re: BASIC programming tips
Post by: GFXOR on 03:41, 30 April 20
The normal use of TAG / MOVE is that if you PRINT"HELLO EVERYBODY" too close from the right side of the screen, the text will be cut, where I would prerfer it to go on writing on the left. I guess the system is detecting if the text is exceeding the right limit of "640 pixels", where I would prefer it to going on writing wherever it is.

Do you know if there is a magical POKE to let the text write ?

Off-Topic:
Out of this subject :
As I am not really fluent english speaker, I don't read all the topics, so I juste discovered the old Morri question, 3 years after.
Quote from: Morri
Does anyone have some simple BASIC code to have the following sequence of numbers...
1 2 3 2 1 2 3 2 1 2 3 etc...
I can do it in the following code but just wondering if someone can do it faster
I am surprised not to have read a such simple solution :
Code: [Select]
10 FOR x=-1 to 2:PRINT 1+ABS(x);:NEXT
Title: Re: BASIC programming tips
Post by: ZbyniuR on 09:11, 07 May 20
Limits of graphics area are sets by command ORIGIN. If setting this way doesn't work, you can try by POKE.

Kernel guide shows adress of pixel x margin:

for 6128 at &B69B and &B69D (2byte both)
for  464 at &B330 and &B332

Let me know is it help. :)