mailto: blog -at- heyrick -dot- eu

Happiness from the tax man

A week ago I received a demand for property/land tax adding up to nearly €500.
As it turns out, it was being paid by me on mom's tax account. Now that the taxman has noticed that she's no longer around (uh, four years later?), the invoice has been sent to me.

I was worried that I would have to do something dumb like "pay that tax bill, file for a reimbursement for the previous payments", but thankfully things were done in a much more logical way.

I just received a message from the tax office in Brest (!?) telling me that the payments made have been transferred to my account and I can ignore the payment demand.
I took a screenshot...just in case.
Also, in order to avoid this situation repeating next year, the current direct debit will be cancelled at the end of the year, and a new one started according to the correct tax account.

And then she warns me that my first payment will be in January, but notification of this might not reach me until afterwards because of delays in the postal service.
That being said, the demand is dated the 31st of August, it is postmarked the 7th of September, sent from a place called Lognes (near Paris), and it got here on the 12th.

As for the tax, because it's rural and my neighbour is using the back field, I'm rated as "terres agricoles", so I'm paying €308 to the commune for the property, plus €14 for the land, plus little bits to the chamber of agriculture, GEMAPI (no idea what that is), and inter-commune. The other main payment, asides from the stuff to the local community, is €136 for the rubbish collection. Yup, they halved the size of the bin and halved the number of collections and, oh look, I'm paying about the same as always rather than logically a quarter of what it should have been. Or, in other terms, it's €5.23 for every bin collection and it seems to be based in part on the figure "306" that keeps appearing on the form which is - amusingly - an estimation of 50% of the potential rental value of the property... which I think is calculated based upon how many square metres of floor space divided into room types (bedroom, living room, etc).
Because, really: no insulation, no heating, no mains water, quite inadequate toilet, electrics that would have been dodgy in the eighties never mind forty years later... shall I go on? ☺

Still, the rubbish. It's always less and less costing more and more.

 

Blind idiot code translation

A newbie to the RISC OS forums has popped up with the idea of C-ifying parts of the kernel. For those who aren't that familiar with the internals of RISC OS, the vast majority of it is written in assembler. Which wasn't so unusual in 1987. It's extraordinary in 2023. But, then, the evolution of RISC OS is... rather akin to the evolution of genomes. Maybe in a half million years it'll have made it to the point of handling GPU assisted graphics, and a further two hundred thousand years in order to get basic Bluetooth functionality. ☺

Anyway, this person took the following code from within RISC OS:

VsyncIRQ_ExtEntry ROUT
        LDRB    R0, CFStime             ; decrement 'CFS' timer !
        SUB     R0, R0, #1
        STRB    R0, CFStime

        VDWS    WsPtr                   ; Do our stuff before issuing VSYNC event
        BL      VsyncCall
        BYTEWS  WsPtr

        MOV     R0, #Event_VSync        ; VSYNC event number
        BL      OSEVEN

        LDRB    R1, FlashCount
        SUBS    R1, R1, #1
        Pull    PC, CC                  ; was zero, so frozen
        STRNEB  R1, FlashCount          ; else if now non-zero, store it back
        Pull    PC, NE                  ; not time to flash yet

        LDRB    R1, FlashState          ; Get the state and
        EORS    R1, R1, #1              ; flip to the other one (setting flags)
        STRB    R1, FlashState

        LDREQB  R2, SpacPeriod          ; get appropriate new period
        LDRNEB  R2, MarkPeriod
        STRB    R2, FlashCount          ; and put into counter

        VDWS    WsPtr
        Push    R4
        BEQ     dothesecondflash

dothefirstflash
        BL      DoFirstFlash
        Pull    "R4, PC"

dothesecondflash
        BL      DoSecondFlash
        Pull    "R4, PC"

That's part of the VSync interrupt handler code.

Anyway, this was apparently passed through ChatGPT in order to arrive at this travesty:

//ChatGPT output (no idea where the Pull macro gets defined)
// Assume the following memory locations based on the assembly code
unsigned char ZeroPage[/* size */];
unsigned char OsbyteVars;
unsigned char VduDriverWorkSpace;
unsigned char CFStime;
unsigned char FlashCount;
unsigned char FlashState;
unsigned char SpacPeriod;
unsigned char MarkPeriod;
unsigned char WsPtr;
unsigned char Event_VSync;

// Macro to point to OsbyteVars
#define BYTEWS(reg) \
    reg = &ZeroPage[OsbyteVars];

// Macro to point to VduDriverWorkSpace
#define VDWS(reg) \
    reg = &ZeroPage[VduDriverWorkSpace];

void VsyncCall(void) {
    // Implement VsyncCall functionality
}

void OSEVEN(unsigned char event) {
    // Implement OSEVEN functionality
}

void Pull(unsigned char *PC, int condition) {
    // Implement Pull functionality
}

void BL(void (*function)(void)) {
    // Implement BL (branch with link) functionality
    function();
}

void DoFirstFlash(void) {
    // Implement DoFirstFlash functionality
}

void DoSecondFlash(void) {
    // Implement DoSecondFlash functionality
}

void VsyncIRQ_ExtEntry(void) {
    CFStime--;  // Decrement CFStime

    // Do stuff before issuing VSYNC event
    BYTEWS(WsPtr);
    VsyncCall();
    BYTEWS(WsPtr);

    OSEVEN(Event_VSync);

    FlashCount--;

    if (FlashCount == 0) {
        // If FlashCount is zero, frozen
        // Assuming Pull function signature and implementation
        Pull(&PC, CC);
    } else {
        // If FlashCount is non-zero, not time to flash yet
        Pull(&PC, NE);
    }

    FlashState ^= 1;  // Flip the state

    if (FlashState == 0) {
        FlashCount = SpacPeriod;
    } else {
        FlashCount = MarkPeriod;
    }

    BYTEWS(WsPtr);

    if (FlashCount == 0) {
        dothesecondflash();
    } else {
        dothefirstflash();
    }
}

void dothefirstflash(void) {
    DoFirstFlash();
    // Assuming Pull function signature and implementation
    Pull(&R4, PC);
}

void dothesecondflash(void) {
    DoSecondFlash();
    // Assuming Pull function signature and implementation
    Pull(&R4, PC);
}

A part of me feels that this was written by hand as an example of what it is hoped that ChatGPT would have said.

The reason that I say that is twofold. Firstly, that doesn't look like the sort of gibberish that ChatGPT would actually return, and secondly, it looks like it was done by somebody who isn't actually all that familiar with how ARM assembly works.

Let's do a quick bug hunt.

The first thing to notice is that FlashCount--; is flat out wrong. If you look at the code, it is loading FlashCount, certainly. But it is only writing it back under a specific condition. This is all done by straight conditional execution, so ChatGPT would have noticed. A person unfamiliar with ARM, not so much.

That lack of unfamiliarity led to translating the conditional block to being if FlashCount is zero then do this, else do that.
This, also, is wrong. It's a three-condition test that is better written as follows:

   if ( FlashCount > 1 )
   {
      // Non-zero, not time to flash yet
      FlashCount--;
      return;
   }
   else
   {
      if ( FlashCount == 0 )
      {
         // Is zero, frozen
         return;
      }
   }
   // else fall through as this is where FlashCount
   // would have *become* zero, so it's time.

Also, somebody not familiar with ARM might not have guessed that Push and Pull were macro wrappers around STM/LDM to handle what's on the stack (including pulling R14 into PC as a function return).

The Push R4 is completely missing.

And, finally, it's calling a function to call a function. This is only being done in assembler because of conditional execution. In C, one could call the functions directly. Oh, and there's another thing, while dothefirstflash has a label, it isn't called. The code just drops right into it if it's not the second flash. Again, familiarity with how the ARM works.

 

While I expected ChatGPT's output to be a lot more gonzo than that, there were too many fundamental errors for me to believe that it was really written by an AI. Some errors, yes.

So I put it to the test. ☺

This is what ChatGPT handed back.

void VsyncIRQ_ExtEntry() {
    unsigned char R0, R1, R2;
    
    R0 = *(volatile unsigned char*)CFStime; // Load value from memory
    R0 = R0 - 1; // Decrement R0 by 1
    *(volatile unsigned char*)CFStime = R0; // Store the updated value back in memory

    VDWS(WsPtr); // Call VDWS with WsPtr as an argument
    VsyncCall(); // Call the VsyncCall function
    BYTEWS(WsPtr); // Call BYTEWS with WsPtr as an argument

    R0 = Event_VSync; // Assign the value of Event_VSync to R0
    OSEVEN(R0); // Call OSEVEN with R0 as an argument

    R1 = *(volatile unsigned char*)FlashCount; // Load value from memory
    R1 = R1 - 1; // Decrement R1 by 1
    if (R1 == 0) {
        // Condition is true, so execute Pull instruction with CC condition
        // This might indicate freezing behavior
        // You would need to implement this part based on your specific requirements
    } else {
        // Condition is false, so store the updated R1 value back in memory
        *(volatile unsigned char*)FlashCount = R1;
        // Execute Pull instruction with NE condition
        // This might be related to flashing behavior
        // You would need to implement this part based on your specific requirements
    }

    R1 = *(volatile unsigned char*)FlashState; // Load value from memory
    R1 = R1 ^ 1; // Toggle the least significant bit (flip to the other one)
    *(volatile unsigned char*)FlashState = R1; // Store the updated value back in memory

    if (R1 == 0) {
        R2 = *(volatile unsigned char*)SpacPeriod; // Load appropriate new period
    } else {
        R2 = *(volatile unsigned char*)MarkPeriod; // Load appropriate new period
    }
    *(volatile unsigned char*)FlashCount = R2; // Store the new period value in FlashCount

    VDWS(WsPtr); // Call VDWS with WsPtr as an argument
    Push(R4); // Push the value of R4 onto the stack
    
    // Conditionally call DoFirstFlash or DoSecondFlash based on some logic
    if (/* some condition */) {
        DoFirstFlash();
    } else {
        DoSecondFlash();
    }
    // Restore the value of R4 from the stack and return
}
Please note that some parts of the code, especially the Pull and Push instructions, are not directly translatable into C without more context about their behavior and the specific memory layout of your embedded system. You may need to adapt these parts according to your platform and requirements.

As it didn't understand Pull, not a surprise as it's an assembler macro, I rewrote the code to use conditional LDMs and... I got back pretty much the same code minus the references to Pull.

Again, it is buggy. It's made a similar error regarding the three-way test, reducing it to an either/or, and then there's the comical Conditionally call DoFirstFlash or DoSecondFlash based on some logic because it seems to have lost track of FlashCount that it twiddled with just moments earlier.
It did, at least, recognise that the double function call was unnecessary, plus it correctly updated FlashCount.
And, this is actually rather important, it marked everything as volatile. This warns C that the value of the variable may change at any time and it should be fetched upon demand and not cached on the stack (or whatever) as an optimisation. When you're writing OS level code, this sort of thing is important.

 

I told ChatGPT that the conditional at the end was based upon the value of FlashCount, so it replied with:

    // Conditionally call DoFirstFlash or DoSecondFlash based on FlashCount
    if (R1 == 0) {
        DoFirstFlash();
    } else {
        DoSecondFlash();
    }

 

I tried to get it to understand that the code following the decrement of FlashCount was actually a three-way test.
This is what I got back. It didn't really seem to understand what I was telling it, so...

I see, you want to optimize the conditional execution based on FlashState higher up in the code when FlashCount is decremented. In this case, you can use the switch statement for FlashState earlier in the code. Here's how you can do that:

void VsyncIRQ_ExtEntry() {
    unsigned char R0, R1, R2, R4;

    R0 = *(volatile unsigned char*)CFStime; // Load value from memory
    R0 = R0 - 1; // Decrement R0 by 1
    *(volatile unsigned char*)CFStime = R0; // Store the updated value back in memory

    VDWS(WsPtr); // Call VDWS with WsPtr as an argument
    VsyncCall(); // Call the VsyncCall function
    BYTEWS(WsPtr); // Call BYTEWS with WsPtr as an argument

    R0 = Event_VSync; // Assign the value of Event_VSync to R0
    OSEVEN(R0); // Call OSEVEN with R0 as an argument

    R1 = *(volatile unsigned char*)FlashState; // Load value from memory
    R1 = R1 ^ 1; // Toggle the least significant bit (flip to the other one)
    *(volatile unsigned char*)FlashState = R1; // Store the updated value back in memory

    // Determine the behavior based on the value of FlashState
    switch (R1) {
        case 0:
            R2 = *(volatile unsigned char*)SpacPeriod; // Load appropriate new period
            break;
        case 1:
            R2 = *(volatile unsigned char*)MarkPeriod; // Load appropriate new period
            break;
        // Handle the third case here based on your requirements
        // case 2:
        //     ... // Handle the third case
        //     break;
        default:
            // Handle unexpected cases here
            break;
    }

    R1 = *(volatile unsigned char*)FlashCount; // Load value from memory
    R1 = R1 - 1; // Decrement R1 by 1
    *(volatile unsigned char*)FlashCount = R1; // Store the updated value back in memory

    VDWS(WsPtr); // Call VDWS with WsPtr as an argument
    R4 = ...; // You'll need to assign a value to R4 here based on your requirements

    // Conditionally call DoFirstFlash or DoSecondFlash based on FlashCount
    if (R1 == 0) {
        DoFirstFlash();
    } else {
        DoSecondFlash();
    }
    // Restore the value of R4, which was saved earlier, and return
}
In this version, we use the switch statement immediately after toggling FlashState. This allows you to conditionally set R2 based on FlashState before FlashCount is decremented. The rest of the code remains the same as previously discussed.

I think it's losing the plot, so enough for now.

 

Suffice to say, I think one will have a limited, negligible, amount of luck in blindly translating assembler to C. Structures and behaviour that makes sense in assembler won't directly translate to a higher level language. And more than that, one should understand the code, not expect an AI to do it for you.

 

Just to bash the dead donkey a little harder, quite a lot of code makes use of jumping around in the code. Here's a short example from the source of the Acorn MIDI module.

; loop and read each MIDI podule (2nd to 4th) and service any requesting irq
10   ADD     r5, r5, #&100 ; increment podule number
     LDR     r0, [r4], #4 ; ACIA/UART address, update r4
; check uart address is sensible otherwise exit
     AND     r1, r0, #&FF000000 ; get top byte of in-podule address to check validity
     CMP     r1, #&03000000 ; check if it is in the right range
     BNE     %FT30         ; exit irq

20   MOVS    r2, r2, LSR#8  ; next IMR byte
     BEQ     %FT30          ; if 0 then finished
     TST     r0, #PoduleTypeTestBit ; test podule type using UART base address
     LDREQB  r1, [r0, #IROffset] ; uart, Check interrupt status reg
     ANDEQ   r3, r2, #&FF    ; IMR copy
     LDRNEB  r1, [r0]            ; acia
     MOVNE   r3, #IRQBit
; r3 contains bit(s) to test to see if this device generating the irq
     TST     r1, r3     ; generating irq?
     ORRNE   r5, r5, #1     ; irq was serviced
     BLNE    DoIRQ
     B       %BT10  ; loop for max 4 midi ports

; exit irq
30  LDR     r2, [r12, #ModeFlagBits]

Pay attention to the numbers on the left, and the branches to %FTxx (forward search) and %BTxx (backward search).

Here's what ChatGPT handed back:

// Define constants for clarity
#define PoduleTypeTestBit 0x10000000
#define IROffset 0x20
#define IRQBit 0x80

void HandleIRQ(int *r4, int *r5, int *r12) {
    // Initialize variables
    int r0, r1, r2, r3;

    // Loop and read each MIDI podule (2nd to 4th) and service any requesting IRQ
    (*r5) += 0x100; // Increment podule number

    r0 = *(int*)(*r4); // Load ACIA/UART address and update r4

    // Check UART address is sensible; otherwise, exit IRQ
    r1 = r0 & 0xFF000000; // Get the top byte of in-podule address to check validity
    if (r1 != 0x03000000) {
        // Exit IRQ
        return;
    }

    r2 >>= 8; // Next IMR byte

    if (r2 == 0) {
        // If IMR byte is 0, then finished
        return;
    }

    // Test podule type using UART base address
    if (r0 & PoduleTypeTestBit) {
        r1 = *(int*)(r0 + IROffset); // UART, check interrupt status reg
    } else {
        r3 = r2 & 0xFF; // IMR copy
        r1 = *(int*)r0; // ACIA
        r3 = IRQBit;
        // r3 contains bit(s) to test to see if this device is generating the IRQ
        if (r1 & r3) {
            // Generating IRQ
            (*r5) |= 1; // IRQ was serviced
            DoIRQ(); // Call the DoIRQ function
        }
    }
}

// Exit IRQ
void ExitIRQ(int *r12) {
    int r2 = *(int*)(r12 + ModeFlagBits);
}

It doesn't seem to understand runs of conditionals, does it? Which means it'll be about as useful as a chocolate teapot when it comes to dealing with ARM code of any complexity.
This leads to such nonsense as the 'else' clause setting R3, then R1, then setting R3 to something else. It's the sort of rookie error that I'd expect a tweeny schoolgirl to spot (assuming she was at all interested in programming). It's just a basic logic fault.

It also doesn't seem to understand the %BT backward branch. I told it that I was using ObjAsm syntax, and it knows what ObjAsm is and claims the code is compliant...yet there's no loop back to check the next port, so if your MIDI podule isn't on the first port you're stuffed.

Brilliant.

 

 

Your comments:

Please note that while I check this page every so often, I am not able to control what users write; therefore I disclaim all liability for unpleasant and/or infringing and/or defamatory material. Undesired content will be removed as soon as it is noticed. By leaving a comment, you agree not to post material that is illegal or in bad taste, and you should be aware that the time and your IP address are both recorded, should it be necessary to find out who you are. Oh, and don't bother trying to inline HTML. I'm not that stupid! ☺ ADDING COMMENTS DOES NOT WORK IF READING TRANSLATED VERSIONS.
 
You can now follow comment additions with the comment RSS feed. This is distinct from the b.log RSS feed, so you can subscribe to one or both as you wish.

C Ferris, 19th September 2023, 14:52
Perhaps the person drinks cold tea :-)
Anon, 19th September 2023, 17:17
I recall back in the day on Fight-o-net [1] that certain people were getting upset that Acorn had started re-implementing chunks of RISC OS in C. Ironic really, as all the work that PACE (and ROL) did to 32-bit the OS could have been avoided if Acorn had finished that job. 
 
Possibly write a new abstraction layer, then recompile everything to target arm32 rather than arm26. 
 
Again, if that job had ever been finished, porting RISC OS to arm64 might even have been possible, if an API emulation layer were to be implemented. 
 
Or, alternatively, it might have been feasible to re-implement huge swathes of RISC OS so that it could multitask properly (ie kernel-level, not a bodge in the window manager), have proper memory protection, access control, etc etc... :-) 
 
[1] Fidonet
David Pilling, 20th September 2023, 12:55
It's a bit like how evolution can go in different directions. Moving to C has to be easy - they knocked off writing RISC OS in a year or less, in ARM code, so writing it in C must not take long. 
Not so, by now we've had 30 years of polishing the ARM code, today you'd not be writing code from the specs, instead code to emulate the ARM code, of which there are several versions. 
Every type of microbe does not after a billion years evolve into a monkey with computers, some have more luck and turn into a better microbe. 
I would have done the Draw module, but encouragement there was none, and there seems little enthusiasm from the powers that be in the RISC OS world. Tales of people doing modules in C and those modules not be adopted. I can see why too, any shortcoming (of which there is a high likelihood) and they will go with the old code. 
David Pilling, 20th September 2023, 13:26
I also don't understand the RISC OS business, possibly the customers are on low computing power systems where ARM code still provides some benefit. 
Rick, 20th September 2023, 15:20
David - it depends, I think, on what you want. It's entirely possible that a new OS written in C (or whatever) could get itself going sort-of rapidly enough to attract people and be useful (even if half the people that comment ask why you're not just skinning Linux). 
 
Writing RISC OS, on the other hand, would be quite a bit harder as there's a lot of API stuff that only really works with assembler. That might be why Acorn never bothered. 
Plus, there's the obvious issue of "if you're going to write a replacement, don't repeat the mistakes of the past". 
 
As for the modules in C, well, how about we actually get the odd build with them in and put it to the test? 
It seems... questionable... to push broken code that clearly wasn't tested properly (or at all) and be quite unwilling to revert instead waiting for a fix (for how long was Menu broken?) while at the same time not exactly willing to allow others to use the OS to test their modifications. 
Behaviour like that is incompatible with the continued development of the OS. It's not the privileged toy of certain individuals, it should be open and easy to all who wish to contribute. 
 
As for the RISC OS business, well, I think people are getting disillusioned with software. It's ever harder to buy "a program". Now so many things are either "monetarily free but your privacy is the cost" or "a subscription model with lock-in to keep you paying". 
 
RISC OS is modern enough to work on today's hardware, but retro enough to be a remembrance of when you bought a product and it just quietly did it's thing and was yours for as long as you wanted to use it. 
 
I guess, also, nostalgia too. 

Sorry, comments cannot be added at this time.
Please try again later.

 

French flagSpanish flagJapanese flag
Calendar
«   September 2023   »
MonTueWedThuFriSatSun
    3
67910
1517
24
25262729 

(Felicity? Marte? Find out!)

Last 5 entries

List all b.log entries

Return to the site index

Geekery

Search

Search Rick's b.log!

PS: Don't try to be clever.
It's a simple substring match.

Etc...


QR code


Valid HTML 4.01 Transitional
Valid CSS
Valid RSS 2.0

 

© 2023 Rick Murray
This web page is licenced for your personal, private, non-commercial use only. No automated processing by advertising systems is permitted.
RIPA notice: No consent is given for interception of page transmission.

 

Have you noticed the watermarks on pictures?
Next entry - 2023/09/20
Return to top of page