On 2/17/2024 3:49 AM,
alb...@spenarnc.xs4all.nl wrote:
> In article <uqivkh$2ontb$
2...@dont-email.me>,
> Don Y <blocked...@foo.invalid> wrote:
>> On 2/14/2024 6:05 AM,
alb...@spenarnc.xs4all.nl wrote:
>>> Nobody hu? Smith does. Written a compiler in hex code using only
>>> a hex to bin converter.
>>>
https://dacvs.neocities.org/SF/
>>> The take away is, it is easier than you expect.
>>
>> One writes code to be *read*. Just because you CAN do something
>> doesn't mean you SHOULD do that something. People spend inane
>> amounts of time arranging dominos... just to knock them over
>> (what's the point in that?)
>
> This project is meant to be read. You can't be serious suggesting
> that this is a tool to be used.
Why write code that isn't going to be used?
> If you spend the time looking at the code, you'd discover that it
> is quite educational, and make you wonder where the software bloat
> comes from.
Why does your PHONE have a clock in it? AND a calendar?
Don't you remember today's date? Don't you wear a wristwatch?
Why does your PC have these data, too? Don't you have a
*phone* (er, wristwatch and a memory for dates)?
Which of these has the CORRECT information? How do you know?
[I have an "atomic clock" that is supposed to keep correct
time/date. Except, twice a year, it diddles the time for
DST start end. *But*, we don't observe DST! So, while
all of the DUMB clocks in the house correctly track the REAL
time all year round, this one has to be "fixed", twice
a year, to get it to IGNORE the DST changes! So, twice
a year I look at the clock and wonder why *my* notion of
the current time differs from it's -- and then tell
it to move me to the next time zone, east or west, as
appropriate]
Why do I *need* an icon IN my word processor to invoke the
spell-checker? Thesaurus? Grammar check? Reading complexity
assessment? How does having them accessible *in* the word
processor help me if I am writing an email -- in an entirely
different "text editor" that mimics much of the functionality
of the word processor?
Does each possible application that allows for text entry
merit the availability of these tools? What if I wanted
to send an SMS?
Are you just LAZY and can't invoke each of these as STAND-ALONE
tools, as needed?
So, EVERY app gets burdened with new implementations of
tools that should be separate entities. To economize on
resources AND ensure for more consistent results!
[Windows lists files in "user-friendly order" -- which differs
from ALPHABETICAL order! Other applications opt for a more
traditional LRAlpha. Comparing file lists between the two
just ADDS to confusion (loss of productivity).]
Here is a trivial, ubiquitous, algorithm written in machine code:
DD217A00DD7E01FE323805DD3401185FDD360100DD7E02FE3C3805DD3402184F
DD360200DD7E03FE3C3805DD3403183FDD360300DD7E04FE183805DD3404182F
DD360400216F000600DD4E0609DD7E05BE3805DD34051817DD360500DD7E06FE
0C3805DD34061807DD360600DD34071F1F1C1F1E1F1E1F1F1E1F1E1F
No comments -- machines don't read comments! And, machines don't
need the code to be formatted to show instruction breaks -- it's
just a large array of bytes!
Of course, you would have to sort out where the code resides in
memory as all of the transfers of control *could* use absolute
addresses (I deliberately chose a processor that offers relative
addressing as a native addressing mode AND USED THAT to make the
binary less obscure).
Likewise, all absolute DATA addressing would require knowing WHAT
was being referenced, esp if the data are not as interrelated as
in this example.
Machine code *teaches* nothing -- beyond as a historical curio.
A bit easier to understand in ASM:
LD IX,TIMING
LD A,(IX+JIFFY)
CP A,JPS
JR C,NXTSEC
INC (IX+JIFFY)
JR DONE
NXTSEC:
LD (IX+JIFFY),0
LD A,(IX+SECOND)
CP A,SPM
JR C,NXTMIN
INC (IX+SECOND)
JR DONE
NXTMIN:
LD (IX+SECOND),0
LD A,(IX+MINUTE)
CP A,MPH
JR C,NXTHR
INC (IX+MINUTE)
JR DONE
NXTHR:
LD (IX+MINUTE),0
LD A,(IX+HOUR)
CP A,HPD
JR C,NXTDAY
INC (IX+HOUR)
JR DONE
NXTDAY:
LD (IX+HOUR),0
LD HL,DAYMON
LD B,0
LD C,(IX+MONTH)
ADD HL,BC
LD A,(IX+DAY)
CP A,(HL)
JR C,NXTMON
INC (IX+DAY)
JR DONE
NXTMON:
LD (IX+DAY),0
LD A,(IX+MONTH)
CP A,MPY
JR C,NXTYR
INC (IX+MONTH)
JR DONE
NXTYR:
LD (IX+MONTH),0
INC (IX+YEAR)
DONE:
six-significant-character labels (external linkage) not being unusual,
hysterically. (In practice, one would use a pointer to walk through the
list of counters instead of accessing them directly.)
In this form, the structure and intent is clearer. And, it uses exactly
the same run-time resources as the machine language variant, before!
This is how people THINK about the algorithm (assuming 0-based ordinals):
ASSERT( ( (jiffy >= 0) && (jiffy <= JIFFIES_PER_SECOND) ) )
if !(jiffy >= JIFFIES_PER_SECOND-1) {
jiffy++;
} else {
jiffy = 0;
ASSERT( ( (second >= 0) && (second <= SECONDS_PER_MINUTE) ) )
if !(second >= SECONDS_PER_MINUTE-1) {
second++;
} else {
second = 0;
ASSERT( ( (minute >= 0) && (minute <= MINUTES_PER_HOUR) ) )
if !(minute >= MINUTES_PER_HOUR-1) {
minute++;
} else {
minute = 0;
ASSERT( ( (hour >= 0) && (hour <= HOURS_PER_DAY) ) )
if !(hour >= HOURS_PER_DAY-1) {
hour++;
} else {
hour = 0;
ASSERT( ( (day >= 0) && (day <= DAYS_PER_MONTH[month]) ) )
if !(day >= DAYS_PER_MONTH[month]-1) {
day++;
} else {
if ( (0 == year % 4)
&& ((0 != year % 100) || (0 == year % 400)) ) {
day++;
} else {
day = 0;
ASSERT( ( (month >= 0)
&& (month <= MONTHS_PER_YEAR) ) )
if !(month >= MONTHS_PER_YEAR-1) {
month++;
} else {
month = 0;
year++;
}
}
}
}
}
}
}
Use of *longer* symbolic names makes the app easier to understand -- and
recognize! Even in the absence of explanatory comments (Joe Average likely
doesn't understand that 1900 and 2100 would NOT be leap years as the only
"century mark" in their lifetime -- 2000 -- was).
Note that the inclusion of the leap year test is intuitive -- yet absent in
the ASM and machine language versions. It would *likely* occur to Joe
Average if asked to explain how "time" is tracked.
And, I can add contractual declarations in a HLL that improve the quality
of the code, it's readability AND detect *some* types of data corruption
at run time (as most folks code in a single process container so no
protection from *themselves* or other threads, co-executing, there) Note
that people *do* think of these constraints, even if on a purely intuitive
level. Expressing them explicitly makes this more formal (and allows the
compiler, runtime and future developers to be aware of these "intuitions")
But only a newbie would code it like that! THIS form is fraught with the
potential for syntax and logic errors. Quick: which of the parens goes
with each -- if the indentation (which may be incorrect as the compiler
doesn't enforce indentation rules!) wasn't there to "help"? (Have *I*
botched it??)
A smarter way of expressing the algorithm:
{
while (FOREVER) {
ASSERT( ( (jiffy >= 0) && (jiffy <= JIFFIES_PER_SECOND) ) )
if !(jiffy >= JIFFIES_PER_SECOND-1) {
jiffy++;
break;
}
jiffy = 0;
ASSERT( ( (second >= 0) && (second <= SECONDS_PER_MINUTE) ) )
if !(second >= SECONDS_PER_MINUTE-1) {
second++;
break;
}
second = 0;
ASSERT( ( (minute >= 0) && (minute <= MINUTES_PER_HOUR) ) )
if !(minute >= MINUTES_PER_HOUR-1) {
minute++;
break;
}
minute = 0;
ASSERT( ( (hour >= 0) && (hour <= HOURS_PER_DAY) ) )
if !(hour >= HOURS_PER_DAY-1) {
hour++;
break;
}
hour = 0;
ASSERT( ( (day >= 0) && (day <= DAYS_PER_MONTH[month]) ) )
if !(day >= DAYS_PER_MONTH[month]-1) {
day++;
break;
}
if ( (0 == year % 4) && ((0 != year % 100) || (0 == year % 400)) ) {
day++;
break;
}
day = 0;
ASSERT( ( (month >= 0) && (month <= MONTHS_PER_YEAR) ) )
if !(month >= MONTHS_PER_YEAR-1) {
month++;
break;
}
month = 0;
ASSERT( (year >= 0) )
year++;
break;
}
ASSERT( ( (jiffy >= 0) && (jiffy <= JIFFIES_PER_SECOND) ) )
ASSERT( ( (second >= 0) && (second <= SECONDS_PER_MINUTE) ) )
ASSERT( ( (minute >= 0) && (minute <= MINUTES_PER_HOUR) ) )
ASSERT( ( (hour >= 0) && (hour <= HOURS_PER_DAY) ) )
ASSERT( ( (day >= 0) && (day <= DAYS_PER_MONTH[month]) ) )
ASSERT( ( (month >= 0) && (month <= MONTHS_PER_YEAR) ) )
ASSERT( (year >= 0) )
return;
}
This leads to a more structured way of recognizing and exploiting
the similarities in the code:
index = 0;
while (0 != divisors[index]) {
if (counters[index] < divisors[index]) {
counters[index]++;
break;
} else {
counters[index] = 0;
index++;
}
}
const int
divisors[] = {
JIFFIES_PER_SECOND,
SECONDS_PER_MINUTE,
MINUTES_PER_HOUR,
HOURS_PER_DAY,
DAYS_PER_MONTH,
MONTHS_PER_YEAR,
EOF
};
int
counters[] = {
0, // jiffy
0, // second
0, // minute
0, // hour
0, // day
0, // month
0, // year
};
STATIC_ASSERT( ( sizeof(counters[])/sizeof(counters[0]) )
== ( sizeof(divisors[])/sizeof(divisors[0]) ) );
but, this requires a "fixup" routine to address the leap-year handling
(which happens at 00:00 on 29 Feb when we reset the date to 1 Mar in
NON-leap-years). It, also, makes it harder to add the rest of the
contractual constraints!
Should we use signed/unsigned chars instead of ints to save a few bytes
of DATA *and* TEXT? How hard would it be to make that change, here -- vs.
in a machine language implementation?
Which of all of these implementations will be easiest to *accurately* modify
to handle Daylight Savings Time? Not just knowing *how* to apply the time
shift but *when* (date AND time-of-day) to apply it?? Or, to generalize
timezone handling (Newfoundland, anyone?)
How does a person set the time, in Boston, to 2024 Mar 10 02:45? And,
what point in time does 2024 Nov 3 01:45 reference? What time is it in
Chicago, then?? Or, the date to 5 October 1582?
Of course, all of this data would have to be wrapped in a mutex to
ensure some other actor doesn't see partial updates (like Mar 1 00:00
while the time was being updated to Apr 1 on at Mar 31 00:00!)
Or, better, provide an accessor instead of exposing the raw data!
An OOPS approach would make each of these "counters" as *objects* and then
just increment one, see if it signals a wrap-around and, if so, increment
the next more significant one, etc. The "day" counter would have friends
in the month and year counters so it could decide whether 28 becomes 29,
or not.
And, all of those annoying details would be hidden from the person
READING the code; jiffies become seconds, seconds become minutes,
minutes become hours, etc. Do we care if the hours are presented
as [0..23]? Or, [1..24]? Or, [1..12][AP]?
Adding textual names would bury those *in* their respective definitions
instead of further polluting the namespace:
month_names[] = {
"January",
"February",
...
"November",
"December",
};
STATIC_ASSERT( ( sizeof(month_names[])/sizeof(month_names[0]) )
== MONTHS_PER_YEAR )
And, what if you had to I18N these?
month_names_french[] = {
"Janvier",
"Février",
...
"Novembre",
"Décembre",
};
STATIC_ASSERT( ( sizeof(month_names_french[])/sizeof(month_names_french[0]) )
== MONTHS_PER_YEAR )
month_names_italian[] = {
"Gennaio",
"Febbraio",
...
"Novembre",
"Dicembre",
};
STATIC_ASSERT( ( sizeof(month_names_italian[])/sizeof(month_names_italian[0]) )
== MONTHS_PER_YEAR )
And, then likely have to add abbreviations for each:
month_abbrevs_french[] = {
"Janv.",
"Février",
...
"Nov.",
"Déc.",
};
STATIC_ASSERT( ( sizeof(month_abbrevs_french[])/sizeof(month_abbrevs_french[0]) )
== MONTHS_PER_YEAR )
[Oh, my! They don't all fit in a 3+1 character representation!! So much for
fixed-width date fields... would you have noticed that and coded to accommodate
it? Or, would you blindly copy the abbreviation into a char[3] because
that's how you *think* of month abbreviations???]
You'd likely add a tool to determine day-of-week from date. And,
I18N that, as well. Along with their abbreviations.
And, as this is utility that many apps would likely avail themselves
of, you'd probably wrap it in a library -- that other apps could
link into their own executables! But, gee, what if I don't really
want/need support for Tamil? Or, Marathi? Why does my app have
to bear that cost?
How much of this is bloat? Feeping Creaturism? Essential functionality?
MARKETABLE functionality? E.g., I like being able to let my PC tell
me what day of the week it is as I don't have normal exogenous
synchronizer in my lifestyle! Should the PC assume that functionality?
What's wrong with a WRISTWATCH? Why does a cell phone provide that?
Surely, I could be REQUIRED to use something other than the PC (or
cell phone) for timekeeping, right (in the interest of minimizing bloat!)?
The *justifiable* reasons for "code growth" (which differs from "bloat")
is to add functionality, structure and readability to the codebase.
These improve the quality of the code as well as its accuracy, reliability
and maintainability.
My first commercial product had 12KB of code and 256 bytes of RAM. And,
took 3 engineers to develop it. Entirely in ASM. Largely because there
were insufficient resources (memory, MIPS, real-time and tools) to solve the
problem as tasked. Yet this was a huge improvement on the i4004 design
that preceeded it -- both from the development point of view and the UX!
I suspect I could recreate the entire codebase in a HLL in a few weekends.
I wouldn't have to write -- and PROFILE (to verify real-time performance!)
and debug -- a floating point package; I could just write expressions in
infix notation. I wouldn't have to consider which values were represented
in binary vs. BCD.
I likely wouldn't have to share *bits* in a byte as flags for the code
to govern it's execution -- just write what you *want* the code to do and
let the compiler sort out the most efficient way of doing so! And, I
wouldn't have to read the code of my fellow developers to see if THEY
had decided to use a previously unused bit for THEIR routines; I'd declare
MY data PRIVATE to my modules and count on the compiler to enforce that
barrier -- yet still allow each of them to reuse names that I had
"already" used -- privately!! (i, j, x, y, index, count, etc.)
I could stub the code to emit key values for me to verify its operation
"on the bench" as well as in the actual implementation. I could sprinkle
invariants through the code to catch *my* errors.