I'm trying to get a simple SDCC example working. More specifically, I'm looking at the tutorial from NoRecess (http://www.norecess.net/sdcc-part-2---compiling-our-first-c-program.html).
I'm running Ubuntu, where SDCC is part of the repository (and for some reason already installed... maybe I've tried it earlier).
It seems to compile fine. But I can't make it run in emulator.
run"main" just gives me either Ready or Line too long.
memory &3999:load "main",&4000 just gives me Syntax error.
Who can tell me what I'm doing wrong?
EDIT:
sdcc --version returns this:
SDCC : mcs51/gbz80/z80/avr/ds390/pic16/pic14/TININative/xa51/ds400/hc08 2.9.0 #5416 (Feb 3 2010) (UNIX)
Quote from: mr_lou on 09:50, 11 December 11
I'm trying to get a simple SDCC example working. More specifically, I'm looking at the tutorial from NoRecess (http://www.norecess.net/sdcc-part-2---compiling-our-first-c-program.html).
I'm running Ubuntu, where SDCC is part of the repository (and for some reason already installed... maybe I've tried it earlier).
It seems to compile fine. But I can't make it run in emulator.
run"main" just gives me either Ready or Line too long.
memory &3999:load "main",&4000 just gives me Syntax error.
Who can tell me what I'm doing wrong?
EDIT:
sdcc --version returns this:
SDCC : mcs51/gbz80/z80/avr/ds390/pic16/pic14/TININative/xa51/ds400/hc08 2.9.0 #5416 (Feb 3 2010) (UNIX)
Have you used the HEX2BIN program to generate the BIN file? SDCC compiles two files from the original C file, one with a ".asm" extension the other with ".ihx", to get it working on the Z80 you use HEX2BIN on the ".ihx" file to produce the BIN file which can be executed on the Amstrad! :)
Quote from: CP/M User on 10:24, 11 December 11Have you used the HEX2BIN program to generate the BIN file? SDCC compiles two files from the original C file, one with a ".asm" extension the other with ".ihx", to get it working on the Z80 you use HEX2BIN on the ".ihx" file to produce the BIN file which can be executed on the Amstrad! :)
Yes I have. I'm following the tutorial. It is the BIN file generated by hex2bin I can't get running.
EDIT: I also tried this way (http://cpcwiki.eu/index.php/SDCC_and_CPC) without luck, although without his libs.
Hmm.... part of the problem seems to be with cpcxfs command:
cpcxfs disk.dsk -f -p main.bin
Looking at that file in ManageDsk afterwards, says the file is of unknown type.
But adding the bin file manually with ManageDsk, telling it it's a binary file that starts at &4000 and has entrypoint at &4000 doesn't make it work. It just resets when I call &4000
#include <stdio.h>
int main()
{
int a;
for ( a = 0; a < 10; a++ )
{
printf("Hello");
}
return 0;
}
Quote from: mr_lou on 10:39, 11 December 11
Yes I have. I'm following the tutorial. It is the BIN file generated by hex2bin I can't get running.
EDIT: I also tried this way (http://cpcwiki.eu/index.php/SDCC_and_CPC) without luck, although without his libs.
I'm just wondering if that file has a header? If it hasn't that's the only other thing I can think of which maybe giving you that problem! If it hasn't got a Header then a program like the Lara Monitor will be able to Load it as a '.com' file and from that you can save it as a Binary file (I think).
When importing it into ManageDsk, it says "AMSDOS header not found".
Then I exported it with Amsdos header, and imported it again. Then it said "AMSDOS header found".
But the result in emulator hasn't changed.
Quote from: mr_lou on 11:55, 11 December 11
When importing it into ManageDsk, it says "AMSDOS header not found".
Then I exported it with Amsdos header, and imported it again. Then it said "AMSDOS header found".
But the result in emulator hasn't changed.
Unfortunately that's the only thing I can think of which does that error. :o I've tried that approach using .COM files and ASCII files on my Emulator which returns that sort of error. Perhaps put Protext on there if you haven't got it and open the file just to see what it's doing, maybe it's still an ASCII ASM file disguised as a BIN file which isn't. At least that would help to know what it is. If it's all Rubbish coming up in the editor, then it maybe a .COM file, get Lara the monitor program which can load COM files and save them as Standard Binary Files. Fraid I don't really know why that error is coming up after the Header has been attached, ASCII files don't have Headers and if they aren't BASIC programs saved as ASCII file:
SAVE"<filename.bas>",a
They won't load with the regular:
LOAD"<filename.bas>"
command.
.COM files are Binary though without a header, everytime one of those is loaded is assumes the program is located at &100 - length can vary, sometimes ending with a EOF marker, files generally works in chunks (blocks) of 128 bytes I think (in CP/M Stat can be used to show the number of Blocks in a detailed directory listing).
Hi,
can you please attach the compiled file and the ".opt" file, which contains the SDCC output? I guess this will help to see what's wrong with the file. Maybe without the printf (because of the massive overhead)?
I also would update to a more recent version of SDCC, because the compiler got significant improvements for the Z80 backend during the last versions - especially the brand new 3.1.0. --opt-code-size option :).
Files attached.
Ok, I checked the compiled file (main.bin) with WinCPC and loaded the file directly into the emulator memory (Load binary file - Memory address &4000). It get's correctly loaded and works fine. So you have a problem loading the file.
I created a DSK image with ManagaDSK, too. To do this, I used "Create DSK" and added the file with "Force BINARY". You then have to enter the start address (&4000) and the entry point of your program (&4000) - ManageDSK then can create the AMSDOS header. After that, you can save the disk image.
In the emulator you can load it from BASIC as usual: MEMORY &3FFF:LOAD"main.bin",&4000:CALL &4000
That's all :-).
I've tried that procedure you describe, several many times, and I always got the same error.
I just tried again, and didn't get any errors. It just types Ready.
I then tried adding a printf("Hello"); in the source, compiled again, used the same procedure - and I still only get a Ready. No output.
Files attached. Where's my output?
EDIT: Never mind. I see now that the compiler gives an error on the printf command. I guess that's why nothing ever changed.... :-X
Ok, I had a look at your new test example and got an error when compiling the main.c file. The problem was (besides the missing include for the stdlib), that you have a cross-compiler here, which doesn't know its execution environment, which means that the printf() function needs a implementation of the putchar(char c) function. I changed your code to provide that function:
#include <stdio.h>
int main()
{
char a;
for ( a = 0; a < 10; a++ )
{
printf("Hejsa");
}
return 0;
}
void putchar (char c) {
__asm
ld a,4(ix)
call 0xBB5A
__endasm;
}
The great thing about that is, that you can also implement a putchar() version, which uses another hardware interface (e.g. the CPC Booster or an other serial interface - yay, remote debugging ;)) for your debug messages.
Btw, don't be scared by the code size of your example. The printf() function is really a beast and wastes a lot of memory to support all those different format encodings. It is better to implement your own printf() function instead.
Thanks for the help Octoate. It would seem that all the trouble I had was mostly because of that missing putchar thingy then...
I'm wondering, is there any official CPC lib for SDCC, like there is CPCRSlib for Z88DK?
Well, during the last months, the maintainer of the Z80 backend of SDCC and the developers of Z88dk are working together to make SDCC compatible to Z88dk (have a look at the release notes of SDCC 3.1.0 and the Z88dk forums). It also shouldn't be a problem to port the CPCRSlib to SDCC, because it's mainly the assembler syntax, which needs to be adapted.
A problem with an own library is that you mostly have different requirements for such an library. E.g. do you want to use the firmware to save memory or do you want to use your own routines for speed? If you don't want to use the firmware, you have to provide everything by yourself, but what? Sound routines? Disk routines? Graphic routines? It is also possible that you need special, for your environment optimized routines, which aren't available in this library. All this points are problems for a general purpose library.
Well, if you use the firmware ... then why use C? Just use Basic, it will make no difference - in speed.
Quote from: TFM/FS on 04:33, 13 December 11
Well, if you use the firmware ... then why use C? Just use Basic, it will make no difference - in speed.
Good point.
I would like to avoid slow firmware calls in the main loop, because as TFM/FS says, otherwise I could just use BASIC. Everything outside the main loop can use firmware calls.
I'd love to have this project (http://www.cpcwiki.eu/forum/programming/graphics-framework-idea/) started up with someone who's a genius at CPC assembler. I'm even willing to pay for it.
Well, I've been trying to get several stuff working with SDCC today without luck.
I think examples here are there are contradicting themselves. Some say that variables passed to assembler is always on ix+4, while I see other example that seems to pick them from other places, e.g. ld hl,#2.
I don't get why some examples (all about SDCC) contains numbers like 0x12 while others do #12, and some even do #0x12.
Can someone write me an SDCC example that simply fills the screen? This is what I thought would do it:
void main() {
int addr;
for (addr=0xc000;addr<0xffff;addr++) {
poke(addr);
}
}
void poke(int a) {
__asm
; Tried several stuff here:
ld (a),255 ; poke a,255? No?
; ok then what about this:
ld hl,a
ld (a),255 ; Just another way of poking? No?
; Then what?
__endasm;
}
I don't know SDCC, but I've read NoRecess's tutorial, and I think you should try this :
void main() {
int addr;
for (addr=0xc000;addr<0xffff;addr++) {
poke(addr);
}
}
void poke(int a) {
__asm
ld h,(ix+5)
ld l,(ix+4)
ld (hl),255
__endasm;
}
Or you can try this :
poke(a)
int a;
{
_asm
ld h,(ix+5)
ld l,(ix+4)
ld (hl),255
_endasm;
}
main() {
int addr;
for (addr=0xc000;addr<0xffff;addr++) {
poke(addr);
}
}
Thanks for trying, but I keep getting different errors, like these:
main.c(5) : error 101: too many parameters
main.asm:55: Error: <a> machine specific addressing or addressing mode error
main.c(1) : error 7: Old style C declaration. IGNORED 'poke'
I'm not familiar with SDCC specifically, but moving the variable declaration inside the function should fix at least that last error.
eg:
poke(a)
{
int a;
_asm
...
Isn't helping. No matter how I put it (and I've tried a ton of different ways now), I keep getting a shitload of errors.
main.c(4) : warning 112: function 'poke' implicit declaration
main.c(4) : error 101: too many parameters
main.c( 8) : error 7: Old style C declaration. IGNORED 'poke'
main.c(15) : error 141: two or more data types in declaration for 'poke'
main.c(15) : error 1: Syntax error, declaration ignored at 'poke'
Back when I tried Z88DK I don't recall have this many problems. But SDCC seems to be the preferred choice nowadays. So far I don't quite understand that.
Quote from: TFM/FS on 04:33, 13 December 11
Well, if you use the firmware ... then why use C? Just use Basic, it will make no difference - in speed.
Rubbish, there is a big difference in Speed, perhaps you should try it someday instead of criticising it! >:(
Well, it can easily be settled by coding a simple drawing in C and BASIC and time both of them.
Demoniak is on the right way, but he uses the Maxam-style assembler syntax, but to do this, you need to use Norecess' assembler syntax converter, because SDCC uses the ASXXX assembler, which uses a different syntax. You have to change the code to this (untested):
void main() {
int addr;
for (addr=0xc000;addr<0xffff;addr++) {
poke(addr);
}
}
void poke(int a) {
__asm
ld h,5(ix)
ld l,4(ix)
ld (hl),255
__endasm;
}
Another important thing is that you can't use variables, which are passed in the function declaration, directly in the assembler code. You have to read them from the stack. That's why you have to manually load 'hl' with the value of 'a'.
The speed should be faster than BASIC even when using the firmware routines, because the code wouldn't be interpreted and it creates smaller executeable code, of course.
Quote from: CP/M User on 08:55, 14 December 11
Rubbish, there is a big difference in Speed, perhaps you should try it someday instead of criticising it! >:(
Bullshit! If the firmware calls are the same, you just have CALLs left in the program, so the speed difference can be ignored. Instead of telling me shit you should better make a test on a real CPC. I already did decades ago.
Wow... I can't get anything at all to work with SDCC
__asm
ld a,( ix + 4 ) ; Notice spaces. Gives error: <q> missing or improper operators, terminators, or delimiters
ld a,(ix+4) ; Notice no spaces. Gives error: ?ASlink-Warning-Undefined Global 'ix' referenced by module 'main'
__endasm;
So whether my code works or not depends on something as simple as spaces in my code...? Wtf?
Who'll step up and defend SDCC here?
Not me. I intended to set it up for me too, but since it makes you so much problems... :(
Quote from: Octoate on 21:39, 14 December 11
Demoniak is on the right way, but he uses the Maxam-style assembler syntax, but to do this, you need to use Norecess' assembler syntax converter, because SDCC uses the ASXXX assembler, which uses a different syntax. You have to change the code to this (untested):
Doesn't matter one bit how I write it. I've tried 3 million 7 thousand 2 hundred and 5 different ways now.
Trying your version still gives me
main.c(5) : error 101: too many parameters
That's the line poke(addr);
Ok, I had a look at it and used this code:
void poke(int a) {
__asm
ld h,5(ix)
ld l,4(ix)
ld (hl),#255
__endasm;
}
void main() {
int addr;
for (addr=0xc000;addr<0xffff;addr++) {
poke(addr);
}
}
Which compiles, but crashed when I started it in the emulator. This code assembles to (.lst file):
1 ;--------------------------------------------------------
2 ; File Created by SDCC : free open source ANSI-C Compiler
3 ; Version 3.1.0 #7066 (Nov 22 2011) (MINGW32)
4 ; This file was generated Wed Dec 14 23:05:17 2011
5 ;--------------------------------------------------------
6 .module pokeexample
7 .optsdcc -mz80
8
9 ;--------------------------------------------------------
10 ; Public variables in this module
11 ;--------------------------------------------------------
12 .globl _main
13 .globl _poke
14 ;--------------------------------------------------------
15 ; special function registers
16 ;--------------------------------------------------------
17 ;--------------------------------------------------------
18 ; ram data
19 ;--------------------------------------------------------
20 .area _DATA
21 ;--------------------------------------------------------
22 ; overlayable items in ram
23 ;--------------------------------------------------------
24 .area _OVERLAY
25 ;--------------------------------------------------------
26 ; external initialized ram data
27 ;--------------------------------------------------------
28 ;--------------------------------------------------------
29 ; global & static initialisations
30 ;--------------------------------------------------------
31 .area _HOME
32 .area _GSINIT
33 .area _GSFINAL
34 .area _GSINIT
35 ;--------------------------------------------------------
36 ; Home
37 ;--------------------------------------------------------
38 .area _HOME
39 .area _HOME
40 ;--------------------------------------------------------
41 ; code
42 ;--------------------------------------------------------
43 .area _CODE
44 ;pokeexample.c:1: void poke(int a) {
45 ; ---------------------------------
46 ; Function poke
47 ; ---------------------------------
0000 48 _poke_start::
0000 49 _poke:
0000 DD E5 50 push ix
0002 DD 21 00 00 51 ld ix,#0
0006 DD 39 52 add ix,sp
53 ;pokeexample.c:6: __endasm;
0008 DD 66 05 54 ld h,5(ix)
000B DD 6E 04 55 ld l,4(ix)
000E 36 FF 56 ld (hl),#255
0010 DD E1 57 pop ix
0012 C9 58 ret
0013 59 _poke_end::
60 ;pokeexample.c:9: void main() {
61 ; ---------------------------------
62 ; Function main
63 ; ---------------------------------
0013 64 _main_start::
0013 65 _main:
66 ;pokeexample.c:11: for (addr=0xc000;addr<0xffff;addr++) {
0013 11 00 C0 67 ld de,#0xFFFFC000
0016 68 00101$:
0016 7B 69 ld a, e
0017 6A 70 ld l, d
0018 D6 FF 71 sub a, #0xFF
001A 7D 72 ld a,l
001B DE FF 73 sbc a, #0xFF
001D D0 74 ret NC
75 ;pokeexample.c:12: poke(addr);
001E D5 76 push de
001F D5 77 push de
0020 CDr00s00 78 call _poke
0023 F1 79 pop af
0024 D1 80 pop de
81 ;pokeexample.c:11: for (addr=0xc000;addr<0xffff;addr++) {
0025 13 82 inc de
0026 18 EE 83 jr 00101$
0028 C9 84 ret
0029 85 _main_end::
86 .area _CODE
87 .area _CABS
Here you can see that the poke() function is inserted before the main() code. So you have to call &4013 and then, the example works.
You get this behaviour, because the example of Norecess uses no crt0.s startup code, which directly jumps to the program code. I'm not a C programmer and would have to look deeper in the SDCC documentation to find more about that issue. A small workaround is to always define the main() function before all other functions you want to call.
Quote from: TFM/FS on 21:39, 14 December 11
Bullshit! If the firmware calls are the same, you just have CALLs left in the program, so the speed difference can be ignored. Instead of telling me shit you should better make a test on a real CPC. I already did decades ago.
Your the one talking the Bull Dust, there is no way you can expect to get the same speed out of a Generic BASIC code Vs using the Firmware and you did say:
QuoteJust use Basic, it will make no difference - in speed.
Even my TP examples enhance through using the Firmware, as tested on a real CPC, because it's Compiler Vs Interpreter - a C Compiler will return the same.
Thankyou! Finally an explanation for that weirdness. Finally I feel I'm getting somewhere.
So I guess that means I need a bunch of CPC specific lib-files? I guess I need to copy the sources from here (http://www.cpcwiki.eu/index.php/SDCC_and_CPC) to somewhere. Or is there a better more updated source?
I can't just place them in my project folder? So I have to replace the original files that comes with SDCC, which might be a problem for two reasons. SDCC is part of the repository and installed that way. I can still replace such files (if I can find their location), but wouldn't they just be overwritten again next time there's an update for SDCC?
I tried downloading the latest version of SDCC via svn, but was unable to compile it. It kept complaining about missing programs I needed, such as flex and I forget the other one. I installed those, and then it complained about some boost library missing, which I'm currently installing....
Quote from: CP/M User on 08:33, 15 December 11
Your the one talking the Bull Dust, there is no way you can expect to get the same speed out of a Generic BASIC code Vs using the Firmware and you did say:
Even my TP examples enhance through using the Firmware, as tested on a real CPC, because it's Compiler Vs Interpreter - a C Compiler will return the same.
I think I asked the question earlier, where Executioner (I think it was) said that there wouldn't be much of a different between a draw call from BASIC and a draw call from assembler. Because it's the same firmware call you're making.
You two stop this silly argument, and simply code a simple draw example in BASIC + C or assembler, and then we can all see the result.
this one works...
Quote
void poke( unsigned short a;)
{
__asm
ld h,(ix + 5)
ld l,(ix + 4)
ld (hl),255
__endasm;
}
main()
{
unsigned short i;
for ( i=0xc000 ; i<0xffff ; i++)
{
poke(i);
}
return 0;
}
"unsigned short" is a integer range from 0 to 65535
...pretty slow mc-code generated (1,2 seconds), but it's fun to programm in C.
in comparison:
0,2 seconds for that mc with the same result:
org &4000
ld hl,&c000
loop ld a,&ff
ld (hl),a
inc hl
ld a,l
or h
jr nz,loop
ret
Quote from: hal 6128 on 14:02, 15 December 11
this one works...
No it doesn't.
main.c(7) : warning 85: in function poke unreferenced function argument : 'a'
main.asm:55: Error: <q> missing or improper operators, terminators, or delimiters
main.asm:56: Error: <q> missing or improper operators, terminators, or delimiters
main.asm:57: Error: <a> machine specific addressing or addressing mode error
Remember, this thread is about (the somewhat troublesome) SDCC - not just general C-coding.
To make it work in SDCC, it must be written like this:
void poke(unsigned short a) {
a;
__asm
ld h,5(ix)
ld l,4(ix)
ld (hl),#255
__endasm;
}
void main() {
unsigned short addr;
for (addr=0xc000;addr<0xffff;addr++) {
poke(addr);
}
}
Quote from: CP/M User on 08:33, 15 December 11
Your the one talking the Bull Dust, there is no way you can expect to get the same speed out of a Generic BASIC code Vs using the Firmware and you did say:
Even my TP examples enhance through using the Firmware, as tested on a real CPC, because it's Compiler Vs Interpreter - a C Compiler will return the same.
TP uses NOT the firmware, it uses CP/M routines. So stop telling crap.
Quote from: mr_lou on 08:44, 15 December 11
I think I asked the question earlier, where Executioner (I think it was) said that there wouldn't be much of a different between a draw call from BASIC and a draw call from assembler. Because it's the same firmware call you're making.
You two stop this silly argument, and simply code a simple draw example in BASIC + C or assembler, and then we can all see the result.
You are right. However I did that before (using BASIC and a Basic Compiler for the native CPC OS, as the IOLIB the allows to run C programs under the native CPC-OS. Result: speed difference less than 5%).
The other guy is obviously not even able to discriminate between firmware and CP/M. Obviously Small C (for example) and TP as well as Modula 2 use CP/M routines
whereas Basic (compiled or not), BCPL and C versions for Amsdos use the native CPC-OS firmware.
interesting:
because both codes works within the tool "Code:Block" (link from octoates website). This tool compiles the code with the help of SDCC.
I have in both cases a working asm/binary for the CPC.
If I'm going to compile directly from Windows-Shell CMD than only your last code generates no error messages and a working asm???
Hello TFM,
is it possible to use your FIOLIB with SDCC? If yes, how?
Quote from: hal 6128 on 16:40, 15 December 11
interesting:
because both codes works within the tool "Code:Block" (link from octoates website). This tool compiles the code with the help of SDCC.
I have in both cases a working asm/binary for the CPC.
If I'm going to compile directly from Windows-Shell CMD than only your last code generates no error messages and a working asm???
I don't know what version of SDCC you have installed, or if there are any changes since v2.9.0 which I'm using. Currently uninstalled though, as I'm trying (again) to install v3.1.0 from svn.
Regardless if I succeed or not, I need to know where I find and replace the files from here: http://www.cpcwiki.eu/index.php/SDCC_and_CPC (http://www.cpcwiki.eu/index.php/SDCC_and_CPC)
crt0.s
putchar.s
and possible the other libs there.
EDIT: The Code::Block thingy is useless to me since I'm on Linux. Even though Code::Blocks IDE does exists for Linux, and even is part of repository, the CPC project file is using Windows executables, so I can't use it.
EDIT 2: It seems I finally managed to install v3.1.1
sdcc -v
SDCC : mcs51/gbz80/z80/z180/r2k/ds390/pic16/pic14/TININative/ds400/hc08 3.1.1 #7107 (Dec 15 2011) (Linux)
But there are no changes.
Where do I put the crt0.s and putchar.s file?
Quote from: hal 6128 on 16:40, 15 December 11
Hello TFM,
is it possible to use your FIOLIB with SDCC? If yes, how?
Well, I assume yes. Originally I inteded to set up SDCC this weekend on my Laptop, but when I see how much problems it creates for others, then I already loose a lot of interrest and will rather use my scare spare time for assembler coding ;-)
To adapt it you have to alter the assembler syntax to that assember used by SDCC - and maybe some smaller stuff. It works well with Small C under CP/M Plus though.
Nurgle (I guess it was him) once told me that FIOLIB runs well with Z88DK. If you get along with SDCC well, it shouldn't be a problem. Let me know if I can help in any way (however I got no idea about SDCC now).
Alright, progress finally.
Here's how to do it, if you're running Linux and want to use SDCC
1) Download SDCC from svn co https://sdcc.svn.sourceforge.net/svnroot/sdcc/trunk/sdcc (https://sdcc.svn.sourceforge.net/svnroot/sdcc/trunk/sdcc) sdcc but don't compile yet
2) Edit device/lib/z80/crt0.s and device/lib/z80/putchar.s and replace the contents with those from here (http://www.cpcwiki.eu/index.php/SDCC_and_CPC).
;; FILE: crt0.s
;; Generic crt0.s for a Z80
;; From SDCC..
;; Modified to suit execution on the Amstrad CPC!
;; by H. Hansen 2003
;; Original lines has been marked out!
.module crt0
.globl _main
.area _HEADER (ABS)
;; Reset vector
.org 0x100 ;; Start from address &100
jp init
.org 0x110
init:
;; Stack at the top of memory.
;; ld sp,#0xffff
;; I will use the Basic stack, so the program can return to basic!
;; Initialise global variables
call gsinit
call _main
jp _exit
;; Ordering of segments for the linker.
.area _HOME
.area _CODE
.area _GSINIT
.area _GSFINAL
.area _DATA
.area _BSS
.area _HEAP
.area _CODE
__clock::
ret
_exit::
ret
.area _GSINIT
gsinit::
.area _GSFINAL
;; FILE: putchar.s
;; Modified to suit execution on the Amstrad CPC
;; by H. Hansen 2003
;; Original lines has been marked out!
.area _CODE
_putchar::
_putchar_rr_s::
ld hl,#2
add hl,sp
ld a,(hl)
call 0xBB5A
ret
_putchar_rr_dbs::
ld a,e
call 0xBB5A
ret
If you accidentially compiled before doing this, or if there for other reasons exists the following files in the device/lib/z80 folder, then delete them first:
putchar.lst
puchar.sym
putchar.rel
crt0.lst
crt0.sym
crt0.rel
3) Now compile using
./configure
make
sudo make install
4) Edit your ~/.bashrc file to include the PATH to the sdcc bins
5) Now you're ready to go.
I still need an explanation to why crt0.s want all programs to start at &100 though. That makes no sense to me since I can't load anything there. I can't run"main" either. So unless you compile using --no-std-crt0 you're fucked.
And I'd still like very much to be able to just run"main" instead of needing to memory &3fff:load"main",&4000:call whatever_address_main_ended_up_on - because that's just plain silly too.
You'll find various examples online which just won't work for you. This example, for example
void DisplayChar( char c )
{
__asm
ld a, ( ix + 4 )
call &bb5a
__endasm;
}
main()
{
const char *textPtr = "Hello World !";
while( *textPtr != 0 )
{
DisplayChar( *textPtr );
textPtr++;
}
__asm
call &bb06
__endasm;
return 0;
}
- won't compile on your Linux box. But if you rewrite it to look like this:
void DisplayChar( char c )
{
__asm
ld a, 4(ix) <-- changed
call 0xbb5a <-- changed
__endasm;
}
main()
{
const char *textPtr = "Hello World !";
while( *textPtr != 0 )
{
DisplayChar( *textPtr );
textPtr++;
}
__asm
call 0xbb06 <--- changed
__endasm;
return 0;
}
- then it works. People say that's seemingly because SDCC is using asxxxx assembler, which can seemingly be changed using the --asm= command parameter. I tried putting --asm=z80asm but that didn't do me any good. So not sure if there is a way out of this, or if we're stuck with asxxxx syntax.
Good work!
Did a small test to compare the draw call from BASIC vs SDCC. Seems to me SDCC is actually slower!
SDCC source
void drawLine(short x1, short y1, short x2, short y2) {
x1;x2;y1;y2;
__asm
ld e,8(ix)
ld d,9(ix)
ld l,10(ix)
ld h,11(ix)
call 0xbbc0; Move to x1, y1
ld e,4(ix)
ld d,5(ix)
ld l,6(ix)
ld h,7(ix)
call 0xbbf6; Draw to x2, y2
ret
__endasm;
}
void main(void) {
short x;
for (x=0;x<600;x+=10) drawLine(x,0,300,400);
__asm
call 0xbb06; Press key
__endasm;
}
BASIC source
for x=-300 to 300 step 10
origin 300,0
draw x,-400
next
For some stupid reason I can't load the binary file into memory, print some stuff and call the starting address later. Then it resets. I have to call the starting address right after load. Can you tell me why?
Quote from: TFM/FS on 17:36, 15 December 11
...
Nurgle (I guess it was him) once told me that FIOLIB runs well with Z88DK. If you get along with SDCC well, it shouldn't be a problem. Let me know if I can help in any way (however I got no idea about SDCC now).
I'm going to try "Code:Block" with that Z88DK compiler at weekend. Small-C under CP/M is also available, but first have to check the handbook for handing it. ("Code:Block" is just a hat for serving compilers. Maybe Phactory from norecess is also an alternative?)
@mr_lou: Is it - maybe - because of the org command in crt0.s that programs will always start at &100? Isn't it also possible to add an header with "Start" and "Entry Point" address with managedsk? (Don't know if it works under Linux & Wine??).
Quote
;; FILE: crt0.s
;; Generic crt0.s for a Z80
;; From SDCC..
;; Modified to suit execution on the Amstrad CPC!
;; by H. Hansen 2003
;; Original lines has been marked out!
.module crt0
.globl _main
.area _HEADER (ABS)
;; Reset vector
.org 0x100 ;; Start from address &100
Quote from: hal 6128 on 21:13, 15 December 11
@mr_lou: Is it - maybe - because of the org command in crt0.s that programs will always start at &100? Isn't it also possible to add an header with "Start" and "Entry Point" address with managedsk? (Don't know if it works under Linux & Wine??).
Yes I am pretty sure it is the crt0.s file that is to blame for the starting address being &100.
But
why was that chosen in the first place? Because Z80 can be found in many other non-CPC devices? Ok, then why does the alternative crt0.s file at CPCwiki
also contain that start address?
Yes, both the SDCC compiler
and ManageDsk can decide what the starting address is. It just has no effect. Starting address is still &100 even though I compile with this:
sdcc -mz80 --code-loc 16384 main.c
wine /home/sirlou/Programmer/ManageDsk.exe -L"disk.dsk" -I"main.bin"/MAIN.BIN/BIN/16384/16384 -S"disk.dsk"
But if I do this:
sdcc -mz80 --no-std-crt0 --code-loc 16384 main.c
wine /home/sirlou/Programmer/ManageDsk.exe -L"disk.dsk" -I"main.bin"/MAIN.BIN/BIN/16384/16384 -S"disk.dsk"
then starting address is correctly set at &4000
(The ManageDsk stuff is running via wine, and I borrowed the command from the CodeBlocks project).
I'm finding all of this very difficult, and often illogical. I can't figure out how to include e.g. the amsgraph library either. Everything seemed much simpler with Z88DK. I suspect I'll just go back to Z88DK.
Quote from: mr_lou on 20:31, 15 December 11
Did a small test to compare the draw call from BASIC vs SDCC. Seems to me SDCC is actually slower!
No, it isn't. Your code isn't ok! You are using signed values, which gives an overhead to the compiler, because it will generate code which checks the sign bit. Just change all "short" values to "unsigned short" and you will get a performance gain about ~9 percent over BASIC.
Then you put a "ret" at the end of the method... If you do that, you will cause a stack overflow, because the clean-up steps won't run, because you added the "ret" command (see compiler output).
Quote from: mr_lou
wine /home/sirlou/Programmer/ManageDsk.exe -L"disk.dsk" -I"main.bin"/MAIN.BIN/BIN/16384/16384 -S"disk.dsk"
If you are already using "wine" with ManageDsk, then why aren't you using it with the Code::Blocks example. All called commands can be changed in the project settings and I tried to comment everything in the README file... Btw, the Pasmo assembler is also available under Linux.
With this setup you then can work with Maxam style assembly syntax, Norecess' examples and my Code::Blocks template...
Quote from: mr_lou
I'm finding all of this very difficult, and often illogical. I can't figure out how to include e.g. the amsgraph library either. Everything seemed much simpler with Z88DK. I suspect I'll just go back to Z88DK.
It's only difficult, because the different tutorials were mixed up (Norecess' and the one by Hans Hansen). The question is, what you want. If you want to use SDCC without different tools, you cannot use Maxam style assembly syntax and have to learn a new assembly syntax.
If you don't care to use an other assembler backend (pasmo) and tools (SDCC2Pasmo), you can use the tutorial by Norecess and my Code::Blocks template - only under Linux you have to use the Linux version of pasmo and need to change the call to SDCC2Pasmo to "wine SDCC2Pasmo".
Btw, the developers of Z88dk are currently in the progress to allow SDCC to be used as a backend for Z88dk. They realized that you need an ANSI-C compiler nowadays and see that the optimization is far superior.
Quote from: Octoate on 22:32, 15 December 11
If you are already using "wine" with ManageDsk, then why aren't you using it with the Code::Blocks example.
Short answer. Because SDCC2Pasmo doesn't run under wine. I tried.
Quote from: Octoate on 22:32, 15 December 11
No, it isn't. Your code isn't ok! You are using signed values, which gives an overhead to the compiler, because it will generate code which checks the sign bit. Just change all "short" values to "unsigned short" and you will get a performance gain about ~9 percent over BASIC.
Well, at first the code did actually use unsigned shorts, but I removed those because I thought there might be cases where I wanted to draw a line from -100 to 100. And in all fairness, BASIC also uses signed values. So saying that the code isn't ok, doesn't add quite up in my opinion.
Quote from: Octoate on 22:32, 15 December 11
Then you put a "ret" at the end of the method... If you do that, you will cause a stack overflow, because the clean-up steps won't run, because you added the "ret" command (see compiler output).
Of course, silly me. I forget that it's really just a "plain" C function that of course returns on its own. doh
Quote from: Octoate on 22:32, 15 December 11
If you are already using "wine" with ManageDsk, then why aren't you using it with the Code::Blocks example. All called commands can be changed in the project settings and I tried to comment everything in the README file... Btw, the Pasmo assembler is also available under Linux.
With this setup you then can work with Maxam style assembly syntax, Norecess' examples and my Code::Blocks template...
It's only difficult, because the different tutorials were mixed up (Norecess' and the one by Hans Hansen). The question is, what you want. If you want to use SDCC without different tools, you cannot use Maxam style assembly syntax and have to learn a new assembly syntax.
If you don't care to use an other assembler backend (pasmo) and tools (SDCC2Pasmo), you can use the tutorial by Norecess and my Code::Blocks template - only under Linux you have to use the Linux version of pasmo and need to change the call to SDCC2Pasmo to "wine SDCC2Pasmo".
I see that the pre-configured Code::Blocks project is using SDCC2Pasmo, which I tried running with Wine without luck. Therefor I'm pretty sure I won't be able to use that preconfigured project.
Initially I did want to use Maxam style assembly, mostly because of the support from all of you I'd need along the way though. Although I must admit, I don't like the "$c000" way of typing values. "0xc000" looks much better to me. But ASXXXX has other silly things.
I have been playing around with the Linux pasmo earlier though, but plain assembly coding will never be me. That's why I went with Z88DK last time.
Quote from: Octoate on 22:32, 15 December 11
Btw, the developers of Z88dk are currently in the progress to allow SDCC to be used as a backend for Z88dk. They realized that you need an ANSI-C compiler nowadays and see that the optimization is far superior.
Yes, all roads point to SDCC, which is why it bugs me that it's giving me such trouble. Each little step takes 3 days. :-(
I think I'll give it a rest now. Sparetime hobbies are supposed to be the relaxing counterpart to the stressing work, not the other way around. ;-)
But I really appreciate the help you've given me so far.
Quote from: mr_lou on 21:31, 15 December 11
Yes I am pretty sure it is the crt0.s file that is to blame for the starting address being &100.
But why was that chosen in the first place? Because Z80 can be found in many other non-CPC devices? Ok, then why does the alternative crt0.s file at CPCwiki also contain that start address?
Maybe it's out of a history manner due to that CP/M apps on Z80 Computer start at &100?? (Just a guess... don't know)
Updated the draw comparison thingy.
SDCC is faster than BASIC now, but not much. (And I still think it's cheating a bit using unsigned shorts).
RUN"compare
EDIT: Could mention, that the SDCC version is only about 4% faster than the BASIC version. So it's debatable if it's worth it.
Can anyone come up with a faster solution?
120 Euro or $150 to the coder who'll assemble (in ASXXXX syntax) my library for SDCC, based on the javax.microedition.lcdui.Graphics (http://docs.oracle.com/javame/config/cldc/ref-impl/midp2.0/jsr118/javax/microedition/lcdui/Graphics.html) class.
I don't need all the methods there though, and some of them needs special adaption for the CPC.
We'll take it slowly, one method after the other. MSN or IRC correspondence necessary.
Example:
drawLine(int x1, int y1, int x2, int y2)
Must take 4 ints (even though you'll probably say unsigned shorts are better).
Must draw a line from coordinates x1,y1 to x2,y2
Coordinates 0,0 is in the upper left corner, as opposed to the lower left corner in BASIC
MSN: mr_lou@vip.cybercity.dk
IRC: mr_lou on proxy.epiknet.org channel #AmstradCPC or irc.freenode.net channel #cpc
Quote from: mr_lou on 08:21, 16 December 11
Updated the draw comparison thingy.
SDCC is faster than BASIC now, but not much. (And I still think it's cheating a bit using unsigned shorts).
RUN"compare
EDIT: Could mention, that the SDCC version is only about 4% faster than the BASIC version. So it's debatable if it's worth it.
Can anyone come up with a faster solution?
Haha, on my CPC (Caprice) its 3% faster. That much about CP/M users weird ideas ;-) Ok, that's all nothing new. Good to see it though.
I can of course come up with a way faster solution, but only for Small-C under CP/M and not for SDCC, because I have no time to rewrite all assembler stuff for that weird SDCC assembler code.
A sample of drawline function using bresenham method:
void DrawLine(int x1, int y1, int x2, int y2)
{
int a, xincr, dx, dy, d,aincr, bincr, x, y;
int steep = abs(y2 - y1) < abs(x2 - x1);
if (steep)
{
a = x1;
x1 = y1;
y1 = a;
a = x2;
x2 = y2;
y2 = a;
}
if (y1 > y2)
{
a = x1;
x1 = x2;
x2 = a;
a = y1;
y1 = y2;
y2 = a;
}
xincr = x2 > x1 ? 1 : -1;
dy = y2 - y1;
dx = abs(x2 - x1);
d = (dx << 1) - dy;
aincr = (dx - dy) << 1;
bincr = dx << 1;
x = x1;
for (y = y1; y <= y2; ++y)
{
if (steep)
plot(y, x,);
else
plot(x,y);
if (d >= 0)
{
x += xincr;
d += aincr;
}
else
d += bincr;
}
}
Quote from: TFM/FS on 22:17, 16 December 11
Haha, on my CPC (Caprice) its 3% faster. That much about CP/M users weird ideas ;-) Ok, that's all nothing new. Good to see it though.
I can of course come up with a way faster solution, but only for Small-C under CP/M and not for SDCC, because I have no time to rewrite all assembler stuff for that weird SDCC assembler code.
Hi TFM,
which Small-C version do you use? In your FIOLIB example (ftest for FOS) you were explaing to compile the file with "cc...", then assemble it with "zmac..." and link it with "zlnk". My Small-C version (for CP/M) has just "cc.com / mac.com / lnk.com" files with different options/switches. And there's no "printf1.h" library?
"
Well, I have written NoRecess asking if there's any chance SDCC2Pasmo will ever be ported to Linux.
If yes, or if I can get the source to make a Java version myself, then it wouldn't be a requirement to use ASXXXX assembler.
So, anyone who'd be interested if you're allowed to use Maxam syntax assembler?
NoRecess released the source for SDCC2Pasmo.
However, it's not as simple as just compiling it on Linux. Some adaptions has to be made first. For example, tchar.h is missing; a compiler-specific header to manage unicode. He also use windows resources system, so one needs to know cpp programming under windows before doing a port to gcc. That's sadly not me.
Anyone else cares to step up and do a Linux port of SDCC2Pasmo?
Meanwhile, what does the --asm=z80asm parameter of SDCC do? It changes the assembler syntax from ASXXXX to Z80 syntax. That's apparently not good enough either for pasmo to compile?
Quote from: hal 6128 on 15:41, 17 December 11
Hi TFM,
which Small-C version do you use? In your FIOLIB example (ftest for FOS) you were explaing to compile the file with "cc...", then assemble it with "zmac..." and link it with "zlnk". My Small-C version (for CP/M) has just "cc.com / mac.com / lnk.com" files with different options/switches. And there's no "printf1.h" library?
"
I use the Small-C 1.2 for Z80. Download it from my homepage, direct link:
http://www.colorado-boys-muenchen.de/users/futureos/files/FIO.zip (http://www.colorado-boys-muenchen.de/users/futureos/files/FIO.zip)
That contains:
- CP/M Plus that reads 700K format on B
- DSK with FIOLIB and all stuff for B-drive in 700K Vortex format.
You need to set up CP/M's submit properly. I have to update that CP/M DSK there, but I miss that stuff on my virtual CPC here (and time too ,-)
Let me know if I can help in any other way :)
EDIT: FIOLIB contains some commands like putchar. The corresponding print-stuff is also on that disc. And a lot more. It was my working disc actually ;-)
Yay!!! SDCC2Pasmo works under wine afterall, after installing vcredist_x86.exe!
So who can provide me an fast drawing routine in Maxam-syntax assembler?
void drawLine(x1, y1, x2, y2) {
// x1,y1,x2,y2 can be byte, unsigned short, whatever you like.
__asm
ld e,(ix+8)
ld d,(ix+9) ; y1 is now in de
ld l,(ix+10)
ld h,(ix+11) ; x1 is now in hl
; Move to x1, y1
ld e,(ix+4)
ld d,(ix+5) ; y2 is now in de
ld l,(ix+6)
ld h,(ix+7) ; x2 is now in hl
; Draw to x2, y2
__endasm;
}
Have compared firmware call draw vs Bresenham algorithm.
Firmware calls are fastest.
RUN"main
The plotting of the Bresenham algorithm is done using the fastplot assembler routine.
No one who can produce a faster line-drawing routine than the firmware call?
EDIT: Forgot to mention, Bresenham (in this case) only works on a CPC464 because of different locations of the pen number in memory. So set your emulator to CPC464.
Hummmm... I can't spot any difference in speed...
Is that right?
I see a big difference, with Caprice emulator and my real CPC464. (For some reason Arnold just gives me a red screen when I try to load from a CPC464).
It's particular noticeable with the more vertical lines. Notice, the more vertical the lines are, the faster the firmware call, while the bresenham method keeps the same slow speed on all lines.
Here is my small contribution, only works in CPC mode 1
2 functions :
SetPen( int numpen)
0<=numpen<=3
DrawLine(int x1,int y1, int x2, int y2)
0<=x1<=319
0<=y1<=199
0<=x2<=319
0<=y2<=199
SetPen:
LD A,(IX+4)
AND 3
ADD A,A
ADD A,A
LD HL,TabPen0
LD B,0
LD C,A
ADD HL,BC
LD (TabPen+1),HL
RET
DrawLine:
LD L,(ix+8)
LD H,(ix+9) ; y1 is now in hl
LD E,(ix+10)
LD D,(ix+11) ; x1 is now in de
LD (X1+1),DE
LD (y1+1),HL
LD E,(ix+4)
LD D,(ix+5) ; y2 is now in de
LD C,(ix+6)
LD B,(ix+7) ; x2 is now in bc
LD (X2+1),BC
LD (Y2+1),DE
SBC HL,DE ; y1-y2
JR NC,DrawLine2
XOR A
SUB L
LD L,A
SBC A,A
SUB H
LD H,A
DrawLine2:
LD (absy+1),HL
LD HL,(x1+1)
SBC HL,BC ; x1-x2
JR NC,absy
XOR A
SUB L
LD L,A
SBC A,A
SUB H
LD H,A
absy:
LD DE,0
XOR A
SBC HL,DE ; HL = absx-absy
JR c,DrawLine3
LD A,#EB ; #EB = EX DE,HL
LD HL,(x1+1)
LD DE,(y1+1)
LD (x1+1),DE
LD (y1+1),HL
LD HL,(x2+1)
LD DE,(y2+1)
LD (x2+1),DE
LD (y2+1),HL
DrawLine3:
LD (exchg),A
LD DE,(y1+1)
LD HL,(y2+1)
SBC HL,DE
JR NC,DrawLine4
LD HL,(x1+1)
LD DE,(x2+1)
LD (x1+1),DE
LD (x2+1),HL
LD HL,(y1+1)
LD DE,(y2+1)
LD (y1+1),DE
LD (y2+1),HL
DrawLine4:
LD HL,(x2+1)
LD DE,(x1+1)
SBC HL,DE
LD HL,-1
JR c,DrawLine5
LD HL,1
DrawLine5:
LD (xincr+1),HL
LD HL,(y2+1)
LD DE,(y1+1)
SBC HL,DE
LD (dy+1),HL
x2:
LD DE,0
LD HL,(x1+1)
SBC HL,DE ; x1-x2
JR NC,DrawLine6
XOR A
SUB L
LD L,A
SBC A,A
SUB H
LD H,A
DrawLine6:
LD (dx+1),HL
ADD HL,HL
dy:
LD DE,0
SBC HL,DE
LD (delta+1),HL
dx:
LD HL,0
SBC HL,DE
ADD HL,HL
LD (aincr+1),HL
LD HL,(dx+1)
ADD HL,HL
LD (bincr+1),HL
LD HL,(y1+1)
BclDrawLine:
EX DE,HL ; DE = y1
y2:
LD HL,0
SBC HL,DE ; HL = y2 - y1
RET C
x1:
LD HL,0 ; HL = x1
exchg:
NOP ; NOP or EX DE,HL
SRA H
LD A,L ; L = lowpart of x (0..319 & 255)
RR L
AND 3
LD (varx+1),A
RR L ; L = x / 4 (0..79)
LD A,E ; E = y (0..199)
LD (vary+1),A
LD E,L
LD D,#C0
AND #F8
LD L,A
XOR A
LD H,A
ADD HL,HL ; HL = ( Y >> 3 ) * 16
LD B,H
LD C,L ; BC = ( Y >> 3 ) * 16
ADD HL,HL ; HL = ( Y >> 3 ) * 32
ADD HL,HL ; HL = ( Y >> 3 ) * 64
ADD HL,BC ; HL = ( Y >> 3 ) * 80
ADD HL,DE
LD E,A ; E = A = 0
vary:
LD A,0
AND 7
ADD A,A
ADD A,A
ADD A,A
LD D,A ; DE = ( y & 7 ) * 2048
ADD HL,DE
EX DE,HL
LD H,L ; L=0
varx:
LD L,0
TabPen:
LD BC,TabPen1
ADD HL,BC
LD A,(HL) ; byte to draw
EX DE,HL
LD C,A
RRCA
RRCA
RRCA
RRCA
OR C ; Bits 0-3 | Bits 4-7
CPL ; Mask
AND (HL) ; video memory's byte
OR C ; byte to draw
LD (HL),A ; in video memory
delta:
LD HL,0
BIT 7,H
JR NZ,bincr
aincr:
LD DE,0
ADD HL,DE
LD (delta+1),HL
LD HL,(x1+1)
xincr:
LD DE,0
ADD HL,DE
LD (x1+1),HL
JR y1
bincr:
LD DE,0
ADD HL,DE
LD (delta+1),HL
y1:
LD HL,0
INC HL
LD (y1+1),HL
JR BclDrawLine
TabPen0:
DB #00,#00,#00,#00
TabPen1:
DB #80,#40,#20,#10
TabPen2:
DB #08,#04,#02,#01
TabPen3:
DB #88,#44,#22,#11
Small optimisation an small "test" program ;-)
ORG #4000
;
; "Sample" program to test functions
;
LD IX,#8000
LD A,1
LD (IX+4),A
CALL SetPen ; Pen 1
LD HL,0
LD (IX+11),H
LD (IX+10),L
LD HL,0
LD (IX+9),H
LD (IX+8),L
LD HL,319
LD (IX+7),H
LD (IX+6),L
LD HL,0
LD (IX+5),H
LD (IX+4),L
CALL DrawLine ; line(0,0,319,0)
LD A,2
LD (IX+4),A
CALL SetPen ; Pen 2
LD HL,319
LD (IX+11),H
LD (IX+10),L
LD HL,0
LD (IX+9),H
LD (IX+8),L
LD HL,319
LD (IX+7),H
LD (IX+6),L
LD HL,199
LD (IX+5),H
LD (IX+4),L
CALL DrawLine ; line(319,0,319,199)
LD A,3
LD (IX+4),A
CALL SetPen ; Pen 3
LD HL,319
LD (IX+11),H
LD (IX+10),L
LD HL,199
LD (IX+9),H
LD (IX+8),L
LD HL,0
LD (IX+7),H
LD (IX+6),L
LD HL,199
LD (IX+5),H
LD (IX+4),L
CALL DrawLine ; line(319,199,0,199)
LD A,1
LD (IX+4),A
CALL SetPen ; Pen 1
LD HL,0
LD (IX+11),H
LD (IX+10),L
LD HL,199
LD (IX+9),H
LD (IX+8),L
LD HL,0
LD (IX+7),H
LD (IX+6),L
LD HL,0
LD (IX+5),H
LD (IX+4),L
CALL DrawLine ; line(0,199,0,0)
LD A,2
LD (IX+4),A
CALL SetPen ; Pen 2
LD HL,0
LD (IX+11),H
LD (IX+10),L
LD HL,199
LD (IX+9),H
LD (IX+8),L
LD HL,319
LD (IX+7),H
LD (IX+6),L
LD HL,0
LD (IX+5),H
LD (IX+4),L
CALL DrawLine ; line(0,199,319,0)
LD A,3
LD (IX+4),A
CALL SetPen ; Pen 3
LD HL,319
LD (IX+11),H
LD (IX+10),L
LD HL,199
LD (IX+9),H
LD (IX+8),L
LD HL,0
LD (IX+7),H
LD (IX+6),L
LD HL,0
LD (IX+5),H
LD (IX+4),L
CALL DrawLine ; line(319,199,0,0)
RET
;
; The functions begin here
;
SetPen:
LD A,(IX+4)
AND 3
ADD A,A
ADD A,A
ADD A,A
LD HL,TabPen0
LD B,0
LD C,A
ADD HL,BC
LD (TabPen+1),HL
RET
DrawLine:
LD L,(IX+8)
LD H,(IX+9) ; y1 is now in hl
LD E,(IX+10)
LD D,(IX+11) ; x1 is now in de
LD (X1+1),DE
LD (y1+1),HL
LD E,(IX+4)
LD D,(IX+5) ; y2 is now in de
LD C,(IX+6)
LD B,(IX+7) ; x2 is now in bc
LD (X2+1),BC
LD (Y2+1),DE
SBC HL,DE ; y1-y2
JR NC,DrawLine2
XOR A
SUB L
LD L,A
SBC A,A
SUB H
LD H,A
DrawLine2:
LD (absy+1),HL
LD HL,(x1+1)
SBC HL,BC ; x1-x2
JR NC,absy
XOR A
SUB L
LD L,A
SBC A,A
SUB H
LD H,A
absy:
LD DE,0
XOR A
SBC HL,DE ; HL = absx-absy
JR c,DrawLine3
LD A,#EB ; #EB = EX DE,HL
LD HL,(x1+1)
LD DE,(y1+1)
LD (x1+1),DE
LD (y1+1),HL
LD HL,(x2+1)
LD DE,(y2+1)
LD (x2+1),DE
LD (y2+1),HL
DrawLine3:
LD (exchg),A
LD DE,(y1+1)
LD HL,(y2+1)
SBC HL,DE
JR NC,DrawLine4
LD HL,(x1+1)
LD DE,(x2+1)
LD (x1+1),DE
LD (x2+1),HL
LD HL,(y1+1)
LD DE,(y2+1)
LD (y1+1),DE
LD (y2+1),HL
DrawLine4:
LD HL,(x2+1)
LD DE,(x1+1)
SBC HL,DE
LD HL,-1
JR c,DrawLine5
LD HL,1
DrawLine5:
LD (xincr+1),HL
LD HL,(y2+1)
LD DE,(y1+1)
SBC HL,DE
LD (dy+1),HL
x2:
LD DE,0
LD HL,(x1+1)
SBC HL,DE ; x1-x2
JR NC,DrawLine6
XOR A
SUB L
LD L,A
SBC A,A
SUB H
LD H,A
DrawLine6:
LD (dx+1),HL
ADD HL,HL
dy:
LD DE,0
SBC HL,DE
LD (delta+1),HL
dx:
LD HL,0
SBC HL,DE
ADD HL,HL
LD (aincr+1),HL
LD HL,(dx+1)
ADD HL,HL
LD (bincr+1),HL
LD HL,(y1+1)
BclDrawLine:
EX DE,HL ; DE = y1
y2:
LD HL,0
SBC HL,DE ; HL = y2 - y1
RET C
x1:
LD HL,0 ; HL = x1
exchg:
NOP ; NOP or EX DE,HL
SRA H
LD A,L ; L = lowpart of x (0..319 & 255)
RR L
AND 3
ADD A,A
LD (varx+1),A
RR L ; L = x / 4 (0..79)
LD A,E ; E = y (0..199)
LD (vary+1),A
LD E,L
LD D,#C0
AND #F8
LD L,A
XOR A
LD H,A
ADD HL,HL ; HL = ( Y >> 3 ) * 16
LD B,H
LD C,L ; BC = ( Y >> 3 ) * 16
ADD HL,HL ; HL = ( Y >> 3 ) * 32
ADD HL,HL ; HL = ( Y >> 3 ) * 64
ADD HL,BC ; HL = ( Y >> 3 ) * 80
ADD HL,DE
vary:
LD A,0
AND 7
ADD A,A
ADD A,A
ADD A,A
ADD A,H
LD H,A
EX DE,HL ; Save video memory's address in DE
varx:
LD HL,0
TabPen:
LD BC,TabPen1
ADD HL,BC
LD C,(HL) ; byte to draw
INC HL
LD A,(HL) ; Mask
EX DE,HL
AND (HL) ; video memory's byte
OR C ; byte to draw
LD (HL),A ; in video memory
delta:
LD HL,0
BIT 7,H
JR NZ,bincr
aincr:
LD DE,0
ADD HL,DE
LD (delta+1),HL
LD HL,(x1+1)
xincr:
LD DE,0
ADD HL,DE
LD (x1+1),HL
JR y1
bincr:
LD DE,0
ADD HL,DE
LD (delta+1),HL
y1:
LD HL,0
INC HL
LD (y1+1),HL
JR BclDrawLine
TabPen0:
DB #00,#77,#00,#BB,#00,#DD,#00,#EE
TabPen1:
DB #80,#77,#40,#BB,#20,#DD,#10,#EE
TabPen2:
DB #08,#77,#04,#BB,#02,#DD,#01,#EE
TabPen3:
DB #88,#77,#44,#BB,#22,#DD,#11,#EE
Beware : The origin (0,0) is at the top left of the screen, and (319,199) is at the bottom right.
Thanks a lot Demoniak.
I can't get the optimized version working, but here is the adapted version of the first version you posted, that works with SDCC.
void setColor(unsigned char color) {
color;
__asm
LD A,(IX+4)
AND 3
ADD A,A
ADD A,A
LD HL,TabPen0
LD B,0
LD C,A
ADD HL,BC
LD (TabPen+1),HL
TabPen0:
DB &00,&00,&00,&00
TabPen1:
DB &80,&40,&20,&10
TabPen2:
DB &08,&04,&02,&01
TabPen3:
DB &88,&44,&22,&11
__endasm;
}
void demoniakLine(unsigned short x1, unsigned short y1, unsigned short x2, unsigned short y2) {
x1;x2;y1;y2;
__asm
LD l,(ix+6)
LD h,(ix+7) ; y1 is now in hl
LD e,(ix+4)
LD d,(ix+5) ; x1 is now in de
LD (x1+1),DE
LD (y1+1),HL
LD e,(ix+10)
LD d,(ix+11) ; y2 is now in de
LD c,(ix+8)
LD b,(ix+9) ; x2 is now in bc
LD (x2+1),BC
LD (y2+1),DE
SBC HL,DE ; y1-y2
JR NC,DrawLine2
XOR A
SUB L
LD L,A
SBC A,A
SUB H
LD H,A
DrawLine2:
LD (absy+1),HL
LD HL,(x1+1)
SBC HL,BC ; x1-x2
JR NC,absy
XOR A
SUB L
LD L,A
SBC A,A
SUB H
LD H,A
absy:
LD DE,0
XOR A
SBC HL,DE ; HL = absx-absy
JR c,DrawLine3
LD A,&EB ; #EB = EX DE,HL
LD HL,(x1+1)
LD DE,(y1+1)
LD (x1+1),DE
LD (y1+1),HL
LD HL,(x2+1)
LD DE,(y2+1)
LD (x2+1),DE
LD (y2+1),HL
DrawLine3:
LD (exchg),A
LD DE,(y1+1)
LD HL,(y2+1)
SBC HL,DE
JR NC,DrawLine4
LD HL,(x1+1)
LD DE,(x2+1)
LD (x1+1),DE
LD (x2+1),HL
LD HL,(y1+1)
LD DE,(y2+1)
LD (y1+1),DE
LD (y2+1),HL
DrawLine4:
LD HL,(x2+1)
LD DE,(x1+1)
SBC HL,DE
LD HL,-1
JR c,DrawLine5
LD HL,1
DrawLine5:
LD (xincr+1),HL
LD HL,(y2+1)
LD DE,(y1+1)
SBC HL,DE
LD (dy+1),HL
x2:
LD DE,0
LD HL,(x1+1)
SBC HL,DE ; x1-x2
JR NC,DrawLine6
XOR A
SUB L
LD L,A
SBC A,A
SUB H
LD H,A
DrawLine6:
LD (dx+1),HL
ADD HL,HL
dy:
LD DE,0
SBC HL,DE
LD (delta+1),HL
dx:
LD HL,0
SBC HL,DE
ADD HL,HL
LD (aincr+1),HL
LD HL,(dx+1)
ADD HL,HL
LD (bincr+1),HL
LD HL,(y1+1)
BclDrawLine:
EX DE,HL ; DE = y1
y2:
LD HL,0
SBC HL,DE ; HL = y2 - y1
JP C,demoniakLineDone
x1:
LD HL,0 ; HL = x1
exchg:
NOP ; NOP or EX DE,HL
SRA H
LD A,L ; L = lowpart of x (0..319 & 255)
RR L
AND 3
LD (varx+1),A
RR L ; L = x / 4 (0..79)
LD A,E ; E = y (0..199)
LD (vary+1),A
LD E,L
LD D,&C0
AND &F8
LD L,A
XOR A
LD H,A
ADD HL,HL ; HL = ( Y >> 3 ) * 16
LD B,H
LD C,L ; BC = ( Y >> 3 ) * 16
ADD HL,HL ; HL = ( Y >> 3 ) * 32
ADD HL,HL ; HL = ( Y >> 3 ) * 64
ADD HL,BC ; HL = ( Y >> 3 ) * 80
ADD HL,DE
LD E,A ; E = A = 0
vary:
LD A,0
AND 7
ADD A,A
ADD A,A
ADD A,A
LD D,A ; DE = ( y & 7 ) * 2048
ADD HL,DE
EX DE,HL
LD H,L ; L=0
varx:
LD L,0
TabPen:
LD BC,TabPen1
ADD HL,BC
LD A,(HL) ; byte to draw
EX DE,HL
LD C,A
RRCA
RRCA
RRCA
RRCA
OR C ; Bits 0-3 | Bits 4-7
CPL ; Mask
AND (HL) ; video memorys byte
OR C ; byte to draw
LD (HL),A ; in video memory
delta:
LD HL,0
BIT 7,H
JR NZ,bincr
aincr:
LD DE,0
ADD HL,DE
LD (delta+1),HL
LD HL,(x1+1)
xincr:
LD DE,0
ADD HL,DE
LD (x1+1),HL
JR y1
bincr:
LD DE,0
ADD HL,DE
LD (delta+1),HL
y1:
LD HL,0
INC HL
LD (y1+1),HL
JR BclDrawLine
demoniakLineDone:
__endasm;
}
And what about this version:
void InitGfxLib() {
__asm
LD HL,#C000
LD IX,PreCalcAdr
LD B,200
InitGfxLib1:
LD (IX+0),L
LD (IX+1),H
LD DE,#800
ADD HL,DE
JR NC,InitGfxLibBcl2
LD DE,#C050
ADD HL,DE
InitGfxLibBcl2:
INC IX
INC IX
DJNZ InitGfxLib1
__endasm;
}
void setColor(unsigned char color) {
color;
__asm
LD A,(IX+4)
AND 3
ADD A,A
ADD A,A
ADD A,A
LD HL,TabPen0
LD B,0
LD C,A
ADD HL,BC
LD (TabPen+1),HL
__endasm;
}
void demoniakLine(unsigned short x1, unsigned short y1, unsigned short x2, unsigned short y2) {
x1;x2;y1;y2;
__asm
LD L,(IX+8)
LD H,(IX+9) ; y1 is now in hl
LD E,(IX+10)
LD D,(IX+11) ; x1 is now in de
LD (x1+1),DE
LD (y1+1),HL
LD E,(IX+4)
LD D,(IX+5) ; y2 is now in de
LD C,(IX+6)
LD B,(IX+7) ; x2 is now in bc
LD (x2+1),BC
LD (y2+1),DE
SBC HL,DE ; y1-y2
JR NC,DrawLine2
XOR A
SUB L
LD L,A
SBC A,A
SUB H
LD H,A
DrawLine2:
LD (absy+1),HL
LD HL,(x1+1)
SBC HL,BC ; x1-x2
JR NC,absy
XOR A
SUB L
LD L,A
SBC A,A
SUB H
LD H,A
absy:
LD DE,0
XOR A
SBC HL,DE ; HL = absx-absy
JR c,DrawLine3
LD A,#EB ; #EB = EX DE,HL
LD HL,(x1+1)
LD DE,(y1+1)
LD (x1+1),DE
LD (y1+1),HL
LD HL,(x2+1)
LD DE,(y2+1)
LD (x2+1),DE
LD (y2+1),HL
DrawLine3:
LD (exchg),A
LD DE,(y1+1)
LD HL,(y2+1)
SBC HL,DE
JR NC,DrawLine4
LD HL,(x1+1)
LD DE,(x2+1)
LD (x1+1),DE
LD (x2+1),HL
LD HL,(y1+1)
LD DE,(y2+1)
LD (y1+1),DE
LD (y2+1),HL
DrawLine4:
LD HL,(x2+1)
LD DE,(x1+1)
SBC HL,DE
LD HL,-1
JR c,DrawLine5
LD HL,1
DrawLine5:
LD (xincr+1),HL
LD HL,(y2+1)
LD DE,(y1+1)
SBC HL,DE
LD (dy+1),HL
x2:
LD DE,0
LD HL,(x1+1)
SBC HL,DE ; x1-x2
JR NC,DrawLine6
XOR A
SUB L
LD L,A
SBC A,A
SUB H
LD H,A
DrawLine6:
LD (dx+1),HL
ADD HL,HL
dy:
LD DE,0
SBC HL,DE
LD (delta+1),HL
dx:
LD HL,0
SBC HL,DE
ADD HL,HL
LD (aincr+1),HL
LD HL,(dx+1)
ADD HL,HL
LD (bincr+1),HL
LD HL,(y1+1)
BclDrawLine:
EX DE,HL ; DE = y1
y2:
LD HL,0
SBC HL,DE ; HL = y2 - y1
JR C,DemoniakLineDone
x1:
LD HL,0 ; HL = x1
exchg:
EX DE,HL ; NOP or EX DE,HL
SRA H
LD A,L ; L = lowpart of x (0..319 & 255)
RR L
AND 3
ADD A,A
LD B,A ; b = varx;
RR L
XOR A
LD D,A
LD A,L ; A = x / 4 (0..79)
LD HL,PreCalcAdr
ADD HL,DE
ADD HL,DE
ADD A,(HL)
INC HL
LD E,A
LD A,(HL)
ADC A,D
LD H,D ; H = 0
LD D,A
LD L,B ; L = varx
TabPen:
LD BC,TabPen1
ADD HL,BC
LD C,(HL) ; byte to draw
INC HL
LD A,(HL) ; Mask
EX DE,HL
AND (HL) ; video memory's byte
OR C ; byte to draw
LD (HL),A ; in video memory
delta:
LD HL,0
BIT 7,H
JR NZ,bincr
aincr:
LD DE,0
ADD HL,DE
LD (delta+1),HL
LD HL,(x1+1)
xincr:
LD DE,0
ADD HL,DE
LD (x1+1),HL
JR y1
bincr:
LD DE,0
ADD HL,DE
LD (delta+1),HL
y1:
LD HL,0
INC HL
LD (y1+1),HL
JR BclDrawLine
TabPen0:
DB &00,&77,&00,&BB,&00,&DD,&00,&EE
TabPen1:
DB &80,&77,&40,&BB,&20,&DD,&10,&EE
TabPen2:
DB &08,&77,&04,&BB,&02,&DD,&01,&EE
TabPen3:
DB &88,&77,&44,&BB,&22,&DD,&11,&EE
PreCalcAdr:
DS 400
demoniakLineDone:
__endasm;
}
Warning : You MUST call InitGfxLib() before drawing lines !
Yup, works after a few adaptions:
All # signs must be replaced with & (what assembler syntax are you using?)
The LD calls in the beginning of demoniakLine() were in wrong order.
Jump relative to demoniakLineDone was out of range, so had to replace with a JP.
void InitGfxLib() {
__asm
LD HL,&C000
LD IX,PreCalcAdr
LD B,200
InitGfxLib1:
LD (IX+0),L
LD (IX+1),H
LD DE,&800
ADD HL,DE
JR NC,InitGfxLibBcl2
LD DE,&C050
ADD HL,DE
InitGfxLibBcl2:
INC IX
INC IX
DJNZ InitGfxLib1
__endasm;
}
void setColor(unsigned char color) {
color;
__asm
LD A,(IX+4)
AND 3
ADD A,A
ADD A,A
ADD A,A
LD HL,TabPen0
LD B,0
LD C,A
ADD HL,BC
LD (TabPen+1),HL
__endasm;
}
void demoniakLine(unsigned short x1, unsigned short y1, unsigned short x2, unsigned short y2) {
x1;x2;y1;y2;
__asm
LD L,(IX+6)
LD H,(IX+7) ; y1 is now in hl
LD E,(IX+4)
LD D,(IX+5) ; x1 is now in de
LD (x1+1),DE
LD (y1+1),HL
LD E,(IX+10)
LD D,(IX+11) ; y2 is now in de
LD C,(IX+8)
LD B,(IX+9) ; x2 is now in bc
LD (x2+1),BC
LD (y2+1),DE
SBC HL,DE ; y1-y2
JR NC,DrawLine2
XOR A
SUB L
LD L,A
SBC A,A
SUB H
LD H,A
DrawLine2:
LD (absy+1),HL
LD HL,(x1+1)
SBC HL,BC ; x1-x2
JR NC,absy
XOR A
SUB L
LD L,A
SBC A,A
SUB H
LD H,A
absy:
LD DE,0
XOR A
SBC HL,DE ; HL = absx-absy
JR c,DrawLine3
LD A,&EB ; #EB = EX DE,HL
LD HL,(x1+1)
LD DE,(y1+1)
LD (x1+1),DE
LD (y1+1),HL
LD HL,(x2+1)
LD DE,(y2+1)
LD (x2+1),DE
LD (y2+1),HL
DrawLine3:
LD (exchg),A
LD DE,(y1+1)
LD HL,(y2+1)
SBC HL,DE
JR NC,DrawLine4
LD HL,(x1+1)
LD DE,(x2+1)
LD (x1+1),DE
LD (x2+1),HL
LD HL,(y1+1)
LD DE,(y2+1)
LD (y1+1),DE
LD (y2+1),HL
DrawLine4:
LD HL,(x2+1)
LD DE,(x1+1)
SBC HL,DE
LD HL,-1
JR c,DrawLine5
LD HL,1
DrawLine5:
LD (xincr+1),HL
LD HL,(y2+1)
LD DE,(y1+1)
SBC HL,DE
LD (dy+1),HL
x2:
LD DE,0
LD HL,(x1+1)
SBC HL,DE ; x1-x2
JR NC,DrawLine6
XOR A
SUB L
LD L,A
SBC A,A
SUB H
LD H,A
DrawLine6:
LD (dx+1),HL
ADD HL,HL
dy:
LD DE,0
SBC HL,DE
LD (delta+1),HL
dx:
LD HL,0
SBC HL,DE
ADD HL,HL
LD (aincr+1),HL
LD HL,(dx+1)
ADD HL,HL
LD (bincr+1),HL
LD HL,(y1+1)
BclDrawLine:
EX DE,HL ; DE = y1
y2:
LD HL,0
SBC HL,DE ; HL = y2 - y1
JP C,demoniakLineDone
x1:
LD HL,0 ; HL = x1
exchg:
EX DE,HL ; NOP or EX DE,HL
SRA H
LD A,L ; L = lowpart of x (0..319 & 255)
RR L
AND 3
ADD A,A
LD B,A ; b = varx;
RR L
XOR A
LD D,A
LD A,L ; A = x / 4 (0..79)
LD HL,PreCalcAdr
ADD HL,DE
ADD HL,DE
ADD A,(HL)
INC HL
LD E,A
LD A,(HL)
ADC A,D
LD H,D ; H = 0
LD D,A
LD L,B ; L = varx
TabPen:
LD BC,TabPen1
ADD HL,BC
LD C,(HL) ; byte to draw
INC HL
LD A,(HL) ; Mask
EX DE,HL
AND (HL) ; video memory's byte
OR C ; byte to draw
LD (HL),A ; in video memory
delta:
LD HL,0
BIT 7,H
JR NZ,bincr
aincr:
LD DE,0
ADD HL,DE
LD (delta+1),HL
LD HL,(x1+1)
xincr:
LD DE,0
ADD HL,DE
LD (x1+1),HL
JR y1
bincr:
LD DE,0
ADD HL,DE
LD (delta+1),HL
y1:
LD HL,0
INC HL
LD (y1+1),HL
JR BclDrawLine
TabPen0:
DB &00,&FF,&00,&FF,&00,&FF,&00,&FF
TabPen1:
DB &80,&77,&40,&BB,&20,&DD,&10,&EE
TabPen2:
DB &08,&77,&04,&BB,&02,&DD,&01,&EE
TabPen3:
DB &88,&77,&44,&BB,&22,&DD,&11,&EE
PreCalcAdr:
DS 400
demoniakLineDone:
__endasm;
}
Quite fast.
I'm putting together another comparison thingy, but need some way of getting the system time in order to time the various methods.
Like in BASIC when I type PRINT TIME, how do I get that TIME value from within assembler?
Can we have a look at that other routine too? :) The one that can only draw a line of 255 pixels. (Is 255 pixels equal to 510 coordinate?)
Alright. Here is another comparison.
BASIC vs SDCC Bresenham algorithm vs SDCC Firmware call vs SDCC Demoniak's routine.
On my CPC464 the results are:
BASIC: 973
Bresenham: 1262
Firmware: 938
Demoniak: 481
So Demoniak's routine is clearly the winner, almost half the time required by firmware calls.
RUN"compare
Can anyone beat that? :D
NOTE: The Bresenham method requires a CPC464, because it's getting the PEN number from a certain address.
P.S.: Demoniak, it would be interesting to make another comparison including your other routine. And then just adapt all draw-stuff to match the limitations of that.
Fast CLS :
void FastCls( unsigned char color )
{
__asm
LD HL,(TabPen+1)
LD A,(HL)
INC HL
INC HL
OR (HL)
INC HL
INC HL
OR (HL)
INC HL
INC HL
OR (HL)
LD L,A
LD H,A
DI
LD (OldSP+1),SP
LD SP,0
LD B,0
BclCls:
PUSH HL
PUSH HL
PUSH HL
PUSH HL
PUSH HL
PUSH HL
PUSH HL
PUSH HL
PUSH HL
PUSH HL
PUSH HL
PUSH HL
PUSH HL
PUSH HL
PUSH HL
PUSH HL
PUSH HL
PUSH HL
PUSH HL
PUSH HL
PUSH HL
PUSH HL
PUSH HL
PUSH HL
PUSH HL
PUSH HL
PUSH HL
PUSH HL
PUSH HL
PUSH HL
PUSH HL
PUSH HL
DJNZ BclCls
OldSp:
LD SP,0
EI
__endasm;
}
Last (?) update for the library with a small optimisation of the DrawLine function :
ORG #4000
;
; "Sample" program to test functions
;
CALL InitGfxLib
XOR A
CALL SetPen ; Pen0
CALL FastCls
LD IX,#8000
LD A,1
LD (IX+4),A
CALL SetPen ; Pen 1
LD HL,0
LD (IX+11),H
LD (IX+10),L
LD HL,0
LD (IX+9),H
LD (IX+8),L
LD HL,319
LD (IX+7),H
LD (IX+6),L
LD HL,0
LD (IX+5),H
LD (IX+4),L
CALL DrawLine ; line(0,0,319,0)
LD A,2
LD (IX+4),A
CALL SetPen ; Pen 2
LD HL,319
LD (IX+11),H
LD (IX+10),L
LD HL,0
LD (IX+9),H
LD (IX+8),L
LD HL,319
LD (IX+7),H
LD (IX+6),L
LD HL,199
LD (IX+5),H
LD (IX+4),L
CALL DrawLine ; line(319,0,319,199)
LD A,3
LD (IX+4),A
CALL SetPen ; Pen 3
LD HL,319
LD (IX+11),H
LD (IX+10),L
LD HL,199
LD (IX+9),H
LD (IX+8),L
LD HL,0
LD (IX+7),H
LD (IX+6),L
LD HL,199
LD (IX+5),H
LD (IX+4),L
CALL DrawLine ; line(319,199,0,199)
LD A,1
LD (IX+4),A
CALL SetPen ; Pen 1
LD HL,0
LD (IX+11),H
LD (IX+10),L
LD HL,199
LD (IX+9),H
LD (IX+8),L
LD HL,0
LD (IX+7),H
LD (IX+6),L
LD HL,0
LD (IX+5),H
LD (IX+4),L
CALL DrawLine ; line(0,199,0,0)
LD A,2
LD (IX+4),A
CALL SetPen ; Pen 2
LD HL,0
LD (IX+11),H
LD (IX+10),L
LD HL,199
LD (IX+9),H
LD (IX+8),L
LD HL,319
LD (IX+7),H
LD (IX+6),L
LD HL,0
LD (IX+5),H
LD (IX+4),L
CALL DrawLine ; line(0,199,319,0)
LD A,3
LD (IX+4),A
CALL SetPen ; Pen 3
LD HL,319
LD (IX+11),H
LD (IX+10),L
LD HL,199
LD (IX+9),H
LD (IX+8),L
LD HL,0
LD (IX+7),H
LD (IX+6),L
LD HL,0
LD (IX+5),H
LD (IX+4),L
CALL DrawLine ; line(319,199,0,0)
RET
InitGfxLib:
LD DE,&C000
LD HL,PreCalcAdr
LD A,200
InitGfxLib1:
LD (HL),E
INC H
LD (HL),D
DEC H
INC HL
EX DE,HL
LD BC,&800
ADD HL,BC
JR NC,InitGfxLibBcl2
LD BC,&C050
ADD HL,BC
InitGfxLibBcl2:
EX DE,HL
DEC A
JR NZ,InitGfxLib1
RET
SetPen:
LD A,(IX+4)
AND 3
ADD A,A
ADD A,A
ADD A,A
LD HL,TabPen0
LD B,0
LD C,A
ADD HL,BC
LD (TabPen+1),HL
RET
FastCls:
LD HL,(TabPen+1)
LD A,(HL)
INC HL
INC HL
OR (HL)
INC HL
INC HL
OR (HL)
INC HL
INC HL
OR (HL)
LD L,A
LD H,A
DI
LD (OldSP+1),SP
LD SP,0
LD B,0
BclCls:
PUSH HL
PUSH HL
PUSH HL
PUSH HL
PUSH HL
PUSH HL
PUSH HL
PUSH HL
PUSH HL
PUSH HL
PUSH HL
PUSH HL
PUSH HL
PUSH HL
PUSH HL
PUSH HL
PUSH HL
PUSH HL
PUSH HL
PUSH HL
PUSH HL
PUSH HL
PUSH HL
PUSH HL
PUSH HL
PUSH HL
PUSH HL
PUSH HL
PUSH HL
PUSH HL
PUSH HL
PUSH HL
DJNZ BclCls
OldSp:
LD SP,0
EI
RET
DrawLine:
LD L,(IX+8)
LD H,(IX+9) ; y1 is now in hl
LD E,(IX+10)
LD D,(IX+11) ; x1 is now in de
LD (X1+1),DE
LD (y1+1),HL
LD E,(IX+4)
LD D,(IX+5) ; y2 is now in de
LD C,(IX+6)
LD B,(IX+7) ; x2 is now in bc
LD (X2+1),BC
LD (Y2+1),DE
SBC HL,DE ; y1-y2
JR NC,DrawLine2
XOR A
SUB L
LD L,A
SBC A,A
SUB H
LD H,A
DrawLine2:
LD (absy+1),HL
LD HL,(x1+1)
SBC HL,BC ; x1-x2
JR NC,absy
XOR A
SUB L
LD L,A
SBC A,A
SUB H
LD H,A
absy:
LD DE,0
XOR A
SBC HL,DE ; HL = absx-absy
JR c,DrawLine3
LD A,&EB ; #EB = EX DE,HL
LD HL,(x1+1)
LD DE,(y1+1)
LD (x1+1),DE
LD (y1+1),HL
LD HL,(x2+1)
LD DE,(y2+1)
LD (x2+1),DE
LD (y2+1),HL
DrawLine3:
LD (exchg),A
LD DE,(y1+1)
LD HL,(y2+1)
SBC HL,DE
JR NC,DrawLine4
LD HL,(x1+1)
LD DE,(x2+1)
LD (x1+1),DE
LD (x2+1),HL
LD HL,(y1+1)
LD DE,(y2+1)
LD (y1+1),DE
LD (y2+1),HL
DrawLine4:
LD HL,(x2+1)
LD DE,(x1+1)
SBC HL,DE
LD HL,-1
JR c,DrawLine5
LD HL,1
DrawLine5:
LD (xincr+1),HL
LD HL,(y2+1)
LD DE,(y1+1)
SBC HL,DE
LD (dy+1),HL
x2:
LD DE,0
LD HL,(x1+1)
SBC HL,DE ; x1-x2
JR NC,DrawLine6
XOR A
SUB L
LD L,A
SBC A,A
SUB H
LD H,A
DrawLine6:
LD (dx+1),HL
ADD HL,HL
dy:
LD DE,0
SBC HL,DE
LD (delta+1),HL
dx:
LD HL,0
SBC HL,DE
ADD HL,HL
LD (aincr+1),HL
LD HL,(dx+1)
ADD HL,HL
LD (bincr+1),HL
LD HL,(y1+1)
BclDrawLine:
EX DE,HL ; DE = y1
y2:
LD HL,0
SBC HL,DE ; HL = y2 - y1
JP C,demoniakLineDone
x1:
LD HL,0 ; HL = x1
exchg:
EX DE,HL ; NOP or EX DE,HL
SRA H
LD A,L ; L = lowpart of x (0..319 & 255)
RR L
AND 3
ADD A,A
LD B,A ; b = varx;
XOR A
LD D,A
LD A,L ; A = x / 4 (0..79)
RRA
LD HL,PreCalcAdr
ADD HL,DE
ADD A,(HL)
LD E,A
INC H
LD A,(HL)
ADC A,D
LD H,D ; H = 0
LD D,A
LD L,B ; L = varx
TabPen:
LD BC,TabPen1
ADD HL,BC
LD C,(HL) ; byte to draw
INC HL
LD A,(HL) ; Mask
EX DE,HL
AND (HL) ; video memory's byte
OR C ; byte to draw
LD (HL),A ; in video memory
delta:
LD HL,0
BIT 7,H
JR NZ,bincr
aincr:
LD DE,0
ADD HL,DE
LD (delta+1),HL
LD HL,(x1+1)
xincr:
LD DE,0
ADD HL,DE
LD (x1+1),HL
JR y1
bincr:
LD DE,0
ADD HL,DE
LD (delta+1),HL
y1:
LD HL,0
INC HL
LD (y1+1),HL
JR BclDrawLine
TabPen0:
DB &00,&77,&00,&BB,&00,&DD,&00,&EE
TabPen1:
DB &80,&77,&40,&BB,&20,&DD,&10,&EE
TabPen2:
DB &08,&77,&04,&BB,&02,&DD,&01,&EE
TabPen3:
DB &88,&77,&44,&BB,&22,&DD,&11,&EE
PreCalcAdr:
DS 512
demoniakLineDone:
RET
Sorry, the arguments are perhaps in bad order, I've only tested this code directly in winape's assembler...
Demoniak you rule.
After that last optimization, your routine is down to 460, which is more than twice the speed of firmware call from SDCC.
RUN"compare
The Bresenham algorithm is no longer part of it.
One thing I've noticed about Demoniak's routine though, is that the first 13 lines seem to be shorter than the rest. Dunno why that is.
Quote from: mr_lou on 15:29, 19 December 11
Can anyone beat that? :D
Maybe. Since your C skills are good, may you can provide the example in proper Small-C so I could check my routine. I have no time to install SDCC, sorry. What I need is just the C program using the drawline() routine. I have no docs for Small-C, so I get errors and don't know why.
Attached is the SDCC source I've done so far. (Rename main.asm to main.c - can't attach files that ends with .c)
All this here is only initial experiments though. It's not yet the framework I have in mind.
Quote from: TFM/FS on 20:58, 19 December 11
Just tell me exactly what you want (email: futuresoft at gmx . de). My stupid quesion about the "what" is not to bother you, I just need to konw. :) However, I'm not into SDCC, but if I get some examples I may can adapt what I have, the assembler part is not problem (as long as I know the syntax of that wired XXXX assembler, so some examples will be good). But let's do that via email, it's IMHO more efficient.
I have asked Gryzor about adding a project page at the Wiki. Then I can describe everything there, and maybe get more people interested and contributing.
I will post a link to the project-page when/if I get to make it.
Quote from: mr_lou on 22:48, 19 December 11
I have asked Gryzor about adding a project page at the Wiki. Then I can describe everything there, and maybe get more people interested and contributing.
I will post a link to the project-page when/if I get to make it.
That's a great idea!
mr_lou: can you test this :
void FillRect(unsigned short x1, unsigned short y1, unsigned short x2, unsigned short y2) {
__asm
LD E,(IX+6)
LD D,(IX+7) ; y1 is now in DE
LD L,(IX+4)
LD H,(IX+5) ; x1 is now in HL
LD (FillRectBcl+1),HL
LD A,(IX+10) ; y2
LD (rectY2+1),A
LD C,(IX+8)
LD B,(IX+9) ; x2 is now in HL
LD (rectX2+1),HL
FillRectBcl:
LD BC,0 ; rectX1
rectX2:
LD HL,0 ; rectX2
PUSH DE
XOR A
SBC HL,BC ; HL=x2-x1;
PUSH HL ; Sauvegarde X2-x1
LD HL,PreCalcAdr
ADD HL,DE
LD D,A
LD A,(HL)
INC H
LD H,(HL)
LD L,A
LD A,B
RRA ; Bit de poids fort x1 dans carry
LD A,C ; bits 0..7 de x1
RRA ; x1/2
AND A
RRA
LD E,A
ADD HL,DE
EX DE,HL ; DE = screen adr
LD HL,(TabPen+1)
LD B,(HL)
LD A,B
LD (DrawLigneHor7+1),A
LD A,C
AND 3
LD C,B
EX DE,HL ; HL = screen adr
LD B,4
DrawLigneHor1:
AND A
JR Z,DrawLigneHor2
RRC C
DEC B
DEC A
JR DrawLigneHor1
DrawLigneHor2:
LD A,(DrawLigneHor7+1)
LD D,A
RRCA
OR D
RRCA
OR D
RRCA
OR D
LD (DrawLigneHor5+1),A
POP DE
INC DE
DrawLigneHor3:
LD A,(HL)
OR C
LD (HL),A
RRC C
DJNZ DrawLigneHor8
LD A,E
DrawLigneHor4:
INC HL
SUB 4
JR C,DrawLigneHor7
LD E,A
DrawLigneHor5:
LD (HL),0
JR DrawLigneHor4
DrawLigneHor7:
LD BC,&400
DrawLigneHor8:
DEC DE
LD A,D
OR E
JR NZ,DrawLigneHor3
POP DE
INC E
LD A,E
rectY2:
CP 0
JR NZ,FillRectBcl
__endasm;
}
Quote from: demoniak on 13:24, 20 December 11
mr_lou: can you test this
You bet.
First I have to submit a project-page on the wiki though, but I'm blocked when I try to submit. :(
Says my page is spam. Then I clicked "Return" and the whole text was gone!!!
Luckily I'd typed everything in gEdit first, so nothing is lost.
Alright. I'm locking this thread now, because it's no longer about Help with SDCC, but instead about the library.
Continue here please: http://www.cpcwiki.eu/forum/programming/smidp2lib-for-sdcc (http://www.cpcwiki.eu/forum/programming/smidp2lib-for-sdcc)