The first word Linux driver written in Rust for apple m1 is interesting
QuoteCalling it a trick or a hack is silly. I designed a bit slice processor as a DMA controller for an array processor (the supercomputer of the 70s). I wanted to use some instructions that I created, which were not part of the assembler by default. I added them. QA didn't complain. I just had to document it.
Whatever, emitted how, so?
If it's inline asm DC. Then it's an hack.
QuoteQA didn't complain. I just had to document it.
Doc is good but doesn't pay money.
Code rejected means no money.
QuotePostScript works best in a non-interactive batch processing mode.
This is the exact opposite of Forth... well, I shouldn't say opposite. Forth handles batch mode just fine. But the interactivity of Forth is a powerful debugging technique.
EVERY programming language works better in non-interactive mode for large and complex programs.
It's not the opposite of Forth, it's similar to Forth except BETTER for interactive programming and debugging, because Postscript is late-binding by default.
This means that you can define your words in any order, whether the words they use have been defined already or not, and you can re-define a word while debugging and existing words will automatically use the new definition.
This is not possible (or at least is not usually done) in address-threaded or subroutine-threaded Forth. It is more easily done in token-threaded Forth (the slowest implementation).
When speed is needed, individual functions or even bodies of IFs or loops can be early-bound (address-threaded, essentially)
The first word Linux driver written in Rust for apple m1 is interesting
Have you looked at it?
EVERY programming language works better in non-interactive mode for large and complex programs.
That's very bogus. Any large program is developed in pieces, which are defined in pieces, etc., until they constitute small programs.
When people run an ICE on a project, they are using the same methods used interactively, but with much more complexity because of the massive code base.
Forth is actually a bit famous for simplifying most coding. Once you learn the philosophy, it becomes second nature to work in the "Forth" way testing the lower level code thoroughly before integrating at the next level.
But people who are entrenched with using tools like ICE, don't appreciate, or possibly understand simpler ways of working.
QuoteIt's not the opposite of Forth, it's similar to Forth except BETTER for interactive programming and debugging, because Postscript is late-binding by default.
I was responding to this quote from the web page...
"PostScript works best in a non-interactive batch processing mode."
Now you are saying Postscript is interactive. Someone needs to make up their minds.
QuoteThis means that you can define your words in any order, whether the words they use have been defined already or not, and you can re-define a word while debugging and existing words will automatically use the new definition.
I'm no expert on compilation, but it sounds to me that late-binding would be a bit slow.
QuoteThis is not possible (or at least is not usually done) in address-threaded or subroutine-threaded Forth. It is more easily done in token-threaded Forth (the slowest implementation).
When speed is needed, individual functions or even bodies of IFs or loops can be early-bound (address-threaded, essentially)
Actually, Forth supports something similar, using words DEFER and IS. This must be set up at compile time, but can be changed at run time. So interactive debugging with alternate definitions for a given word is supported. There is no run time penalty on most systems, while on others it would be slight. It is essentially like changing the subroutine call by using an intermediary.
QuoteThese bit patterns can be emitted directly by the compiler without using an assembler.emitted how? By inline asm DC?
Instructions are emitted sequentially and emitted by the four procedures Put0, Put1, Put2, Put3. They directly correspond to the instruction formats of the RISC processor (see Chapter 11). The instructions are stored in the array code and the compiler variable pc serves as running index.
EVERY programming language works better in non-interactive mode for large and complex programs.
That's very bogus. Any large program is developed in pieces, which are defined in pieces, etc., until they constitute small programs.
When people run an ICE on a project, they are using the same methods used interactively, but with much more complexity because of the massive code base.
Forth is actually a bit famous for simplifying most coding. Once you learn the philosophy, it becomes second nature to work in the "Forth" way testing the lower level code thoroughly before integrating at the next level.
But people who are entrenched with using tools like ICE, don't appreciate, or possibly understand simpler ways of working.
I wouldn't know an ICE if it bit me in the arse.
BASIC is interactive. Python is interactive. Both are better written in a text editor, except in rare circumstances where you don't know WTF the the language does or want to poke at some peripheral.
QuoteQuoteIt's not the opposite of Forth, it's similar to Forth except BETTER for interactive programming and debugging, because Postscript is late-binding by default.
I was responding to this quote from the web page...
"PostScript works best in a non-interactive batch processing mode."
Now you are saying Postscript is interactive. Someone needs to make up their minds.
Dude, that's not my web page. It's the opinion of someone who is not in this discussion.
Something being interactive doesn't mean you want to work that way all the time.
QuoteQuoteThis means that you can define your words in any order, whether the words they use have been defined already or not, and you can re-define a word while debugging and existing words will automatically use the new definition.
I'm no expert on compilation, but it sounds to me that late-binding would be a bit slow.
OF COURSE it is. It's also far more flexible. That's why lots and lots of people program most of their application logic in slow-as-molasses Python, and do the few bits where you can tell the difference in C.
It's better if you can do it all in one language.QuoteQuoteThis is not possible (or at least is not usually done) in address-threaded or subroutine-threaded Forth. It is more easily done in token-threaded Forth (the slowest implementation).
When speed is needed, individual functions or even bodies of IFs or loops can be early-bound (address-threaded, essentially)
Actually, Forth supports something similar, using words DEFER and IS. This must be set up at compile time, but can be changed at run time. So interactive debugging with alternate definitions for a given word is supported. There is no run time penalty on most systems, while on others it would be slight. It is essentially like changing the subroutine call by using an intermediary.
Which means Forth has is bass-ackwards, as the flexible slow method should be the DEFAULT. not something you have to jump through hoops to get.
I've been using Forth, on and off for more than 40 years, when appropriate. And PostScript for 37 years.
typedef int8_t apple_t;
typedef int8_t orange_t;
apple_t apples = 0;
orange_t oranges = 0;
apples = oranges; /* This should be flagged as an error without explicit cast */
Here are my 2c (Did not read through the whole thread, so please forgive me if these are dupilcates):
1. In C the arrays are quite problematic when passed to functions, because the length of the array is not implicitly passed as an argument. So, the new language should pass the length of an array implicitly to the function to be called. The length of the passed array needs to be accessible inside the called function. Enabling/disabling run-time checks for validating array accesses should be supported globally / by compilation unit / by function, and during unit testing. Also, when passing a variable or an array as a void*, the caller should pass the total size of the item in bytes to the called function.
2. In C handling of endianess when declaring structs and bit fields required some work in order to make code portable across different architectures: Keywords and language constructs for native / big endian / little endian should be provided, and the compiler should take care of producing code for the correct endianess.
3. In C the padding of structs requires careful design to get it right, especially when porting to different architectures. The compiler should provide simple and intuitive means for specifying and asserting the padding.
4. Design by contract (https://en.wikipedia.org/wiki/Design_by_contract) should be supported natively by the compiler so that the verification of the contracts can be enabled during compilation time and when running the unit tests. The run-time validation of the contract asserts, pre- and postcondition contracts should be controllable globally / by compilation unit / by function when creating for the actual production build. In C this can be done using macros, but this should be built-in into the language, and should be supported during run-time system.
5. Modern C compilers complain when comparing unsigned value to a signed value, which is good. But modern C compilers allow assigning unsigned variables to signed variables, and vice versa. Assigning unsigned variables to signed variables, and vice versa, should be flagged as an error by default in general. Literal values should be casted automatically by the compiler, of course. If the user wants to assign signed variables to unsigned variables, and vice versa, the operation should be done explicitly by the programmer.
6. Native 8-bit unsigned and signed data types, and a separate 8-bit char data type which is not compatible with the integer types without proper casting.
7. Strict type checking. For example the following should not be allowed without explicit cast, because they are declared to be different data types although they are both int8_t types at the end of the day:Code: [Select]typedef int8_t apple_t;
typedef int8_t orange_t;
apple_t apples = 0;
orange_t oranges = 0;
apples = oranges; /* This should be flagged as an error without explicit cast */
Edit:
8. More intuitive way to work with bits/bitmasks.
I don't recall, that was nearly 50 years ago. I seem to recall one supervisor being initially upset that technically, this used a different assembler, so it must not have been a DC, rather a "hack" to the assembler code.
But once it was thoroughly documented in the project, he was happy.
You should never learn Forth coding. I am confident it would blow your mind... and not in the good way. Some people aren't bound so rigidly.
Sorry, you are incomprehensible. Can you speak English?
7. Strict type checking. For example the following should not be allowed without explicit cast, because they are declared to be different data types although they are both int8_t types at the end of the day:Code: [Select]typedef int8_t apple_t;
typedef int8_t orange_t;
apple_t apples = 0;
orange_t oranges = 0;
apples = oranges; /* This should be flagged as an error without explicit cast */
Would you also object to apples = apples + 1; ?
If not, why not? It's a new type (casting away the fact that typedef in C does not introduce new types, only synonyms: C11 6.7.8 §3).
Shouldn't it be apples = (apple_t)((int8_t)apples + 1);?
After all, apple_t and int8_t are no longer the same type (if they were, so would be orange_t and we are back to C semantics), so why should I expect to have the same operations defined on them.
apples = apples + 1;
apples++;
apples = (apples << 1) | 1;
apples = 1 + oranges;
apples = (oranges << 1) | 1;
apples = apples + oranges * 2
BTW: what would be the if-then-else if-else problem?
typedef in C does not introduce new types, only synonyms: C11 6.7.8 §3)
4. Design by contract
Here are my 2c (Did not read through the whole thread, so please forgive me if these are dupilcates):
1. In C the arrays are quite problematic when passed to functions, because the length of the array is not implicitly passed as an argument. So, the new language should pass the length of an array implicitly to the function to be called. The length of the passed array needs to be accessible inside the called function. Enabling/disabling run-time checks for validating array accesses should be supported globally / by compilation unit / by function, and during unit testing. Also, when passing a variable or an array as a void*, the caller should pass the total size of the item in bytes to the called function.
dcl matrix(10,20) string(64) varying;
func scan_for_text(table) returns bool
arg table(*,*) string(64);
var x = Dim(table,0); // i.e 10
var y = Dim(table,1); // i.e 20
var isup = is_uppercase(table(3,7));
end
func is_uppercase(text) returns bool
arg text string(*);
end
2. In C handling of endianess when declaring structs and bit fields required some work in order to make code portable across different architectures: Keywords and language constructs for native / big endian / little endian should be provided, and the compiler should take care of producing code for the correct endianess.
3. In C the padding of structs requires careful design to get it right, especially when porting to different architectures. The compiler should provide simple and intuitive means for specifying and asserting the padding.
4. Design by contract (https://en.wikipedia.org/wiki/Design_by_contract) should be supported natively by the compiler so that the verification of the contracts can be enabled during compilation time and when running the unit tests. The run-time validation of the contract asserts, pre- and postcondition contracts should be controllable globally / by compilation unit / by function when creating for the actual production build. In C this can be done using macros, but this should be built-in into the language, and should be supported during run-time system.
5. Modern C compilers complain when comparing unsigned value to a signed value, which is good. But modern C compilers allow assigning unsigned variables to signed variables, and vice versa. Assigning unsigned variables to signed variables, and vice versa, should be flagged as an error by default in general. Literal values should be casted automatically by the compiler, of course. If the user wants to assign signed variables to unsigned variables, and vice versa, the operation should be done explicitly by the programmer.
6. Native 8-bit unsigned and signed data types, and a separate 8-bit char data type which is not compatible with the integer types without proper casting.
dcl counter bin(8) signed;
dcl totals bin(16) unsigned;
dcl rate bin(16,4) signed;
dcl interim bin(12); // defaults to signed (perhaps)
7. Strict type checking. For example the following should not be allowed without explicit cast, because they are declared to be different data types although they are both int8_t types at the end of the day:Code: [Select]typedef int8_t apple_t;
typedef int8_t orange_t;
apple_t apples = 0;
orange_t oranges = 0;
apples = oranges; /* This should be flagged as an error without explicit cast */
8. More intuitive way to work with bits/bitmasks.
I know that Arm supports both types, how would you envisage this looking to a developer? what aspects of code are influenced by this?
I know that Arm supports both types, how would you envisage this looking to a developer? what aspects of code are influenced by this?
Endianness
At early boot, SONY PSX1 (MIPS R3000), as well as POWER10, can be configured LE or BE with one bit.
Once configured, it is active and cannot be easily changed until the hardware is reset.
Sony configured the PSX1 CPU as LE. Years ago I hacked the firmware with a ROMemulator to force it as BE. Kind of toy-hack, funny to do, nothing serious.
Taylos sells their POWER10 configured as LE, so I reflashed my boss's POWER10 workstation firmware to be PPC64/BE compliant. This saved me houndred hours of work, because I only have PPC64/BE stages available and prepairing a PPC64/LE .... costs no less than two weeks.
Sure, with POWER10, MIPS5+ and some modern ARMs there are also instructions that convert the data { 16, 32 } bits LE <----> BE.
Mac_part, which is BE because designed for PowerMac, is one of the recent things I fixed on ARM64, which is LE and can't be forced BE without pain.
So, I wrote a C function to convert endianness. Easy job, you can even "inline" it. When you mind its needs, well, you find it only requires endianness conversion when mounting a partition, and it's fine that way until unmount.
Coprocessors and high-internet-working like Tulip chip can offload CPU but when they shoot ... they wants BE-only (and your target is LE) large-block data ready, so it can be worth the effort of an assembly module that directly use hardware instructions.
It depends on how often you need it.
I mean, nice to have feature, but it's not a primary need
I don't recall, that was nearly 50 years ago. I seem to recall one supervisor being initially upset that technically, this used a different assembler, so it must not have been a DC, rather a "hack" to the assembler code.
No way, if it was a C file with assembly inline DC, it was an hack!
my-c can fix this, but it requires an advanced set of internal mechanisms to define new operators that map to new hw opcodes in a consistent way to let the compiler know their boundaries, their negative side effect (if any), provide ways to observe and verify (this is useful for ICE test cases) and also provide code rejection on error.
[] -> { ok, maybe, nothing }But once it was thoroughly documented in the project, he was happy.
documenting hacks, well ... yes, bettern than nothingYou should never learn Forth coding. I am confident it would blow your mind... and not in the good way. Some people aren't bound so rigidly.
I have ZERO interest in Forth. It's not even close to what I think.
I make programs in C and Ada for a living.
Recently I am looking at RUST with interest.
Sorry, you are incomprehensible. Can you speak English?
You said that QA doesn't complain (when) you just have to document your code.
I said that doc is always a good practice but doesn't pay money.
to pay : give someone something in return for { work done, goods received, debt incurred, favor done }
pay(someone, something) ->
{
work done: your clients(code commited, QA passed) -> return you money
favor done: your colleagues(code documented, hacks demystified) -> return you gratitude
}
Documenting things is always a great thing, it helps you with future work on the same code, it repays your colleagues gratitude in return (if they have to work on your code), but what only matters to you at the end of the week is whether the code passes or fails QA.
My customers won't pay a cent for the document, and won't pay a cent if the code fails QA.
Thus, documenting has a lower priority.
It's not good, but it's how it works.
So what are the code constructs that are not symmetrical with respect to endianness? For example a = b + c ; will (semantically) behave the same way yes? what code does not or would not behave symmetrically?
7. Strict type checking. For example the following should not be allowed without explicit cast, because they are declared to be different data types although they are both int8_t types at the end of the day:Code: [Select]typedef int8_t apple_t;
typedef int8_t orange_t;
apple_t apples = 0;
orange_t oranges = 0;
apples = oranges; /* This should be flagged as an error without explicit cast */
Such a strict type checking might be good, or not. Opinions differ.
Not even Pascal does what you are suggesting.
Would you also object to apples = apples + 1; ?
If not, why not? It's a new type (casting away the fact that typedef in C does not introduce new types, only synonyms: C11 6.7.8 §3).
Shouldn't it be apples = (apple_t)((int8_t)apples + 1);?
After all, apple_t and int8_t are no longer the same type (if they were, so would be orange_t and we are back to C semantics), so why should I expect to have the same operations defined on them.
BTW: what would be the if-then-else if-else problem?
I don't need you to be a dictionary. I just need you to use the English language well.
Porn website.
Spoken like a hack.
Where you live perhaps.
Last thing for now: Checked assignments, which will be checked at run-time so that the compiler will generate code to check that the assigned value is within the allowed numerical range of the variable where the results will be assigned to. Trying to assign a value outside the allowed range will generate a run-time error. This feature should be enabled during unit testing, and should be controllable (enable/disable) globally / by compilation unit / by function during build time.
Just some random thoughts: Probably a new operator := could be defined for checked assignment. Probably a checked block could be defined so that all assignments inside the checked block will be checked during run-time if the compile-time option for the checked assignment was enabled globally or by compilation unit / by function. **. I dunno.
Edit:
** Maybe some kind of saturated assignment could be defined so that if the value to be assigned is outside the valid numerical range, the programmer can provide the upper and lower limits of a value to be assigned. If the programmer doesn't provide the limits, the compiler will use the minimum and maximum values automagically? Just thinking aloud here.
dcl table(10,10) bin(16);
table(4,11) = 100; // passes shallow check because it access absolute element 51 out of 100, a valid array element.
So what are the code constructs that are not symmetrical with respect to endianness? For example a = b + c ; will (semantically) behave the same way yes? what code does not or would not behave symmetrically?
Once BE/LE defined at boot (x86 is LE-only, it's hw wired that way), a CPU has an endianness, and so it operates untill reset.
Problems are with the load/store only when it has to access devices that has/need different endianness.
Here you need endianess conversion.
The simplest example is mac_part.
Fields are BE when you read them from the partition on the disk.
You read BE, you convert LE, so your LE-CPU (x86) can understand fields.
You keep them LE in your CPU memory space untill you need to modify something and update its copy on disk. So, you convert LE back to BE, and force sync.
With the Co-Processor, you prepare a temporary buffer, LE to BE, to feed its needs.
And you consume its block-response, BE to LE, to serve your kernel driver needs.
dcl IsBigEndian builtin;
dcl LittleEndian builtin;
// assume this data item has been populated by some IO read, and some_ptr has therefore been set
dcl partition_data bin(32) signed based(some_ptr);
if IsBigEndian() then // Both builtin functions are included for convenience, so we also have IsLittleEndian
partition_data = LittleEndian (partition_data);
end
data_header = LittleEndian (data_header);