User errors versus compiler bugs in Acorm C//C++
tymaja (278) 178 posts |
I set up the latest DDE, and have compiled a RISC OS ROM – showing that it works (and works between reboots etc); Using a fresh reboot to avoid clashes with the ROM build system, I am writing a small test app in C. The lack of an 0b0101011 or a 2-11010101 operator for binary is annoying (I guess we have to use hex instead for binary); I have come across an issue: I am using h and c folders, no subdirectories (need to learn how to make those work!): I have, in the c folder, main.c I have, in the h folder, hello.h main.c #includes hello.h and world.h hello.c does printf(“hello”); and returns on compilation, I get a warning “extern ‘world’ not declared in header’. The application still compiles and runs. hello.c and hello.h, and world.c and world.h, are identical (except hello and world are used in function names, and printf); hello.h and world.h are #included in main. As an experiment, Fixes the issue. This is interesting, as it is a copy and paste, meaning typos aren’t responsible. I will probably spend all day chasing down this issue. What I glean so far is that : It feel weird making header files, including those header files, but also, having to manually copy the header file contents after you include it, and redefine + extern all the functions again. Has anyone come across an issue like this? |
tymaja (278) 178 posts |
Figured it out – it was just a difference between Acorn C and gcc; you need to declare functions before defining them! It makes sense – the solution is just to do void function1(uint32_t); This prevents the warning message from occurring |
Chris Mahoney (1684) 2168 posts |
I think you’ve still done something wrong, because you shouldn’t need to declare like that. I’m not at my Risc OS machine at the moment so can’t knock up an example right now though. Edit: Which I then completely forgot about :) |
Rick Murray (539) 13908 posts |
It’s a warning from the compiler. You have the (IIRC) ‘h’ flag defined in the options given to the compiler (like If a function is only to be used within a specific bit of code and not have global scope, it should be declared as static void function1(uint32_t variable) and if it is a globally visible function, then yes, it should be defined (as I’m surprised gcc doesn’t warn about this sort of thing (unless, like the DDE, it’s an option you can choose). This applies to variable definitions as well. Refer to https://leimao.github.io/blog/Static-Extern-C/ for more details (and tell me if you think it was written by AI). |
Rick Murray (539) 13908 posts |
Though it is worth clarifying that with variables, 1, if a variable is being declared at module level (outside of a function), then it means that the variable only has scope within that module, that is to say is is non-existent elsewhere in the program. Something declared static in hello.c cannot be seen or used in world.c. 2, if a variable is declared as static within a function, this instructs the compiler to place the variable in the static storage area and not on the stack. Which effectively means the value is retained across function calls. This gives it the benefit of persistence available to globals or module level variables without being global or module-visible. Also if you initialise it to a specific value, like zero, that will only happen the very first time. From that point onwards the value on function entry will be “whatever it was last set to”. |
tymaja (278) 178 posts |
All very useful info – will look into static more too. I realised my issue was not declaring functions before defining them … I was using a DDE on the Pi5 (CodeLite) – it is good, not too intrusive, but I think it ‘patched up’ my not including h files for c files within the c files! I started coding an ARM32 emulator in C (will take a few days as a learning project) before I go back to vdu drivers. I had already started coding ARM BBC BASIC into C! Since I am being a newbie, I may as well ask : In Acorn C, how do we specify binary? 0b01010101 doesn’t work, 2_0101000 doesn’t work, and … How do we specify subfolders? I have ‘c’ and ‘h’ folders, but if I wanted to make a sub folder, how do I do that? h.function1 can be included as function1.h, for example, but I tried subfolders in a few ways, and they aren’t found. The DDE examples don’t use subfolders. I found the C ShellCLI module by Julie in the RO sources, which I am using to learn from, but it didn’t need subfolders either! :) |
David J. Ruck (33) 1649 posts |
This very much sounds like running before you can walk. An ARM32 emulator is potentially a very big project, given the number of different 32 bit cores there are and the large number of instructions which have been introduced. It is much larger than most RISC OS emulators which only emulate one of a small number of old 26 bit cores with far smaller instruction sets. You could use it as a learning project, and you have certain have learned a lot by the time you’d have finished. On the other hand there are number of off the shelf 32 bit ARM emulators which you could use such as qemu, or Unicorn which pyromaniac uses. |
tymaja (278) 178 posts |
I agree it does! However – I would be using it as a learning project to get used to Acorn C / C++; Although unreleased (due to being learning projects), I originally made an ARM emulator that ran ARM BBC BASIC, then later a Risc PC emulator that booted to desktop. Both projects were on RISC OS, and the experience making the user-mode ARM emulator (for BBC BASIC) helped me learn a lot about the ‘core’ ARM32 instruction set. The Risc PC emulator also ran under RISC OS, and booted to desktop (RISC OS 5), but I hadn’t yet understood how the keyboard / mouse worked, so it didn’t do much more than this! Since my last post, I have done some work on an ARM emulator. I will try to find an example of how to specify binary, and how to use subdirectories in Acorn C. I do agree that emulating a modern 32-bit ARM CPU is a major project (the sheer number of instructions!), but it can be done (as an example, a few days ago, I got RISC OS 5 (BCM2835) right up until the point where the MMU switches on, in a few days, all in C!) What I want to do is learn how to use Acorn C, so I can write code that can be modified with minimal effort to compile under gcc as well. Will keep learning! |
Chris Mahoney (1684) 2168 posts |
OK, having sat down and tried it (it’s been quite a while since I’ve done any plain old C!) I think you didn’t #include your .h from its .c. For example, your hello.c probably doesn’t/didn’t This Zip file contains a working example of the project you described in your first post. It should build (via amu) with no warnings. |
tymaja (278) 178 posts |
You are 100% correct – I wan’t including the h file in the c file 🤪 – and because I was just getting started in Acorn C, I didn’t get the issue of ‘calling a functiom further down in the file’ which would have shown me the issue! Either gcc, or the DDE I was using on the Pi5, was correcting mistakes like that, at least in the simple test code I tried there, causing added confusion as it did it without warnings or anything. I also found out the answer to the issue of specifying binary numbers in C … you can’t! (at least, you couldn’t until C14, which is when they added the 0b notation; I looked through the Acorn C/C++ manual in the hope of finding a macro language to define binary numbers, but couldn’t find anything, so; I just made a file, binary.h. It start#: #define b0 0 #define b00 0×0 … and finishes #define b11111111 0xFF so, I can at least use any bit pattern from 0-255, including stuff like b00000001 etc :) |
Jeff Doggett (257) 234 posts |
If you are using #defines to declare a sequence then you might want to consider using an enum. |
Simon Willcocks (1499) 540 posts |
FWIW, I wrote a couple of ARM emulators 15 years ago. You might like to have a look at arm_arm_emulator.c in https://sourceforge.net/p/ro-lf/code/HEAD/tree/ROLF/rolf/Libs/Compatibility/ (there’s also an x386 version in the same directory). It might be a bit dusty, but it won’t have rusted! |
nemo (145) 2611 posts |
tymaja wrote
As it happens, I’ve had b0 to b31 defined in my Aasm headers for decades and use the idiom so often I was considering adding it to nemoBasic – even though it already supports the <base>_<digits> syntax. Why the Acorn tools never gained any of these Acorn idioms (&hex, %bin, base_digits) I don’t know. “Portability” perhaps. Boo. |
David J. Ruck (33) 1649 posts |
Even C99 was long after Acorn. |
tymaja (278) 178 posts |
I do agree – I have simplified things by focusing on the Cortex-A72, so ARMv8. The system registers are mapped in dynamically, so things like the reset values can be changed (so not tied to the A72); Things are so much more complex now – GICv4 for example – nearly 1000 pages for just the core specification of the interrupt controller! So (as a learning project), I am writing a RPi4 emulator, in C, using Acorn C/C++. Very early days, but: I swapped Desktop and BASIC105 around, so it boots to BASIC. So far, the ARM CPU itself is done, the v6 MMU is done, a fair amount of hardware mapping is done. The MBOX interface is done, and some of most other peripherals. Am currently working on interrupts (which are currently working in a rudimentary way; the 100Hz timers work, but the GIC-400 emulation is in the very early stages). Current aim is to get RTSupport to not complain (the Thread vector test code it runs doesn’t yet work as interrupt support is basic). In the meantime, I hijacked the SWI instruction so that OS_ReadLine and OS_ReadLine32 use code on the host Pi, so I can use BASIC (and *MEMORYI etc) to explore various aspects of the system. This is just for fun, but definitely learning a lot! |
Colin Ferris (399) 1822 posts |
Have you thought of adding bits to the Rpcemu emulator – ie later Cpu’s instructions – 512Mb of memory? |
tymaja (278) 178 posts |
I have thought of this; I wrote a Risc PC emulator on RISC OS several months ago now, in assembler (assembled within BASIC); it has all been learning! (the ARM64 BBC BASIC I made was only after writing a ‘simple’ ARM emulator (USR mode, no memory map) to run BASIC on. I did start working extending it (I still have the folder ‘BigRPC’ which specifically was going to be a RPC with a lot more memory – it wouldn’t be too difficult to do – would require slight modifications to the IOMD source code to make a ‘custom’ HAL for it to allow more memory. I haven’t looked at RPCEmu’s source code; I know it is in C or C++. I think there is development ongoing on RPCEmu, which is one reason I haven’t looked, but also it is GPL, and while I want to do stuff ‘open source’, I kind of like the license that RISC OS Open uses, as opposed to GPL. Adding later instructions is actually easy – and possible without slowing down the emulator. The new instructions are in ‘extension areas’ (a major example being ‘TST, TEQ, CMP, CMN’ instructions without the ‘S’ bit set; the instructions added in these ‘extension spaces’ are often useful, not used that often, but can speed things up a lot. The data processing opcodes are %cccc00xo oooSRnnn Rddd____ ________ (from memory); where The obvious thing to do, when emulating, would be to do: What I do is : Using the first four bits (00xo) and bits 7,4 (1xx1) can sort instructions really quickly. I then process the data instructions without any overhead, and have the branches out for the ‘10xx0’ data processing extension instructions actually in the code that handles the other data instructions. The end result is an extra cycle or two for a CLZ or PKHTB etc, but no increase in cycles for all the data processing ops that are done a lot more often! A reason for trying to make a RISC OS RPi4 emulator to run RISC OS on a RPi4 is to learn more about the hardware and the OS. A major reason is debugging; I want to test out integrating my C VDU drivers into RISC OS, without needing to remove and update SD cards with test ROM images for every little change. I am actually doing most work on a debugging interface that would allow the emulator to capture everything that happens on the system, with only a minor slowdown, but also to capture everything that happens into a very compact format, which is also fully ‘reversible’, so you can run the emulator in reverse when something goes wrong, save that as a snapshot, then examine what happens if you change certain register values etc. All that aside, one thing I have thought of doing is getting the RPCEmu source code, and porting it to RISC OS. This should be easy to do, and could be useful as a base for debugging stuff. I would be worried that ‘all other emulation work I do’ becomes GPLed because I looked at the RPCEmu source code … which is a worry, as I would want to use a RISC OS Open type open source license, rather than GPL! |