In last month article I wrote about how I was thinking of a non-incremental
but still bank based garbage collector. since then (and as that article title
said), I continued thinking about it.
The conclusion of that thinking is that my approach is not going to work
fast enough. This kind of makes sense: I was trying to go for more efficient
memory use (using more than half the memory at a time), but doing that inevitably
comes with more complex and less efficient code. And my experiments showed that
I do need the code to be fast and also reasonably small, rather than making it
memory efficient. The 512K heap is quite large, and "sacrificing" half of it is
not that bad.
So I went ahead and reimplemented the Baker two-space garbage collector, as
was originally used in Little Smalltalk, but in a modified version that works
with two groups of 16 banks. There is a big difference that costs me some
performance however: in Little Smalltalk, most of the objects loaded from the
initial image are not in fact allocated in the heap, but live in a separate
space that's never garbage collected (it's cleaned only when writing back an
image to disk). I made something somewhat smilar by reserving one bank for
that, except it's too small to contain the initial image (that would need 3
banks and a half). As a result, I have two problems: most of the image gets
moved around between the two spaces at each gc cycle, and the remaining part
of it still needs to be scanned as it may contain references to things that
need to be moved.
Despite that, the results are very good! After a few other small optimizations,
I got the GC to run only once for a computation of "4+3", and only for 7 seconds
or so. The total computation time for this test is now just under a minute, instead
of two minutes and a half before. So, that's 2.5x faster, and now most of the
time is spent in the interpreter.
Lesson learned: don't try to reinvent the wheel. Especially if you don't understand very well how wheels work.
This works so well because the Smalltalk interpreter generate a lot of garbage.
As a result, when a gc cycle happens, it turns out that most of the objects do not
need to be moved. And the Baker GC complexity is measured only in terms of the "live"
objects that get moved, the dead/unreachable ones are completely ignored. So they
work quite well together.
I have more ideas to improve on this. For example, now my ROM code has gotten
smaller and I have more than 1K of ROM space free. I could surely move some of
the core Smalltalk objects there, making them read-only and making sure they don't
reference anything in RAM. Then, these objects can be completely ignored by the
GC: no need to scan them, no need to update references to them. And also the image
loading process can be made a bit simpler, as at the moment it's a recursive function
that needs a lot of stackspace, that ends up eating precious main memory. It could
be made simpler if the core objects are already there, because that's the difficult
part with a lot of circular references. If that's already there, it may become much
easier to load the other objects in order.
Anyway, since the GC is not the main contributor to execution time, let's leave
that aside for now, and focus instead on the interpreter. I think acceptable performance
for computing "4+3" would be maybe under 1 second. That means we still need to make
things a further 60 times faster than they currently are. That will surely need
some deep changes.
The Little Smalltalk interpreter is very "by the book" in the sense that it
allocates Smalltalk execution contexts, stacks, and other things as normal Smalltalk
objects. This works fine for the original Little Smalltalk, but not so well with
my banked memory approach, as accessing each of these objects requires a bankswitch.
So my next plan is to move some of that to main (non-banked) memory. Then, the interpreter
can access it without bank switches. This means the interpreter code can be more "normal"
z80 code, that is not constantly interrupted by bank memory register writes.
A quick examination of the interpreter code reveals that the most accessed object
seems to be the current method execution stack (unsurprisingly). So, let's consider
how we may move that to main memory.
In Little Smalltalk, the stack is a typical Smalltalk object, it is an array allocated
with a size indicated in the method object when preparing a method execution. It is freed
by the normal garbage collection cycle. In theory, Smalltalk code can very well grab a
reference to the stack to analyze it (this is how you'd write a debugger that steps
through the code, probably). And it needs to be released as normal in a GC cycle, when
no references to it exist anymore. In most cases the only reference would be the method
context, but I can't be sure that this is always the case for now.
The space in main memory is relatively limited. I may have up to 16K of it if I manage
to reoganize things efficiently, but probably a bit less than that (and it's a good idea
to also keep some RAM for other future uses anyway). This means setting up a two-space solution
is not going to work well here. Some other strategy will be needed.
In any case, the stacks can contain references to other objects, which means
the GC will have to know about them for two reasons: to scan them for references,
and to free them when not needed anymore.
I also know that this will be a relatively "hot" memory area, as there are
a lot of allocations of stacks and contexts during the execution of Smalltalk code.
In fact, it's quite likely that this space will run out much before we need to do
a full GC cycle.
I can think of many ways to handle this. For example, this space could be handled as a simple cache,
where frequently needed objects are simply copied or temporarily moved from their normal space in main
memory. This would be similar to the "working set" (objects stored in RAM, as opposition to disk) in the Alto Smalltalk.
But moving an object isn't that cheap in our case. The Alto used an object indirection table, which means it had a single
place to update to redirect an object to RAM or disk.
So maybe let's try something similar to that: when the object is moved to main RAM, its original place
in the heap is replaced with a redirection marker, that says "this object is not actually here".
That would work, but now all code that references an object has to handle this special case, and so our
speedup of the stack access by the interpreter comes with the cost of everything else having to check if
an object has been moved to main RAM in this way.
Another approach is to allocate the object directly in the main RAM space, without having to mirror it
in the actual heap. This seems simpler for the allocation: it's just a special case of allocation without bank
management and at a special address. Then the object references can be used normally, but also special cased
by the interpreter code that needs specific objects to be in main memory. In fact, it may be OK to move objects
to main memory at the last minute if they were not create there. Certainly slower (since we have to look for all
references to an object when moving it), but if we have to do it only in some very special cases, it should not
have cost repercussions otherwise.
The main problem, then, is to decide when to move things out of that main memory space. Ideally, that would
be never. There is a pretty good chance that when the time comes to make some free space in there, a lot of
the objects using it up are in fact already dead/unreferenced. But how can we be sure of that? The obvious
solution would be to run a GC cycle, that will automatically move such objects into banked heap space. But that
brings us back to a world of very frequent GC cycles, which we don't want.
We are essentially back to the problem of implementing an efficient incremental garbage collector here.
Maybe we can benefit from the fact that this is a special tool used only for execution stacks. There may be
a way to reference count these, or explicitly free them at the end of a method execution unless any reference to
them was established besides the method context.
I had already wrote down some notes yesterday evening, and went to sleep convinced that I had a workable
solution. But I then decided to write an article about it, which helped me see how it doesn't quite work.
I guess I will still be thinking about it over the next few days until I find a way that can work...
This article was updated after feedback from Philippe Matherat, the designer of the EF9365 GPU. It previously stated that the development happened at the LIENS (that laboratory was created much later), and that the goal of the project was to emulate the Tektronix 4010 display (that was added later in the project as a way to test and validate the design).
So, this week I thought about Thomson EFCIS graphic controllers again. I
know that Thomson made at least two families of controllers, the EF934x, which
runs the Minitel, and the EF936x, which is designed for pixel graphics, but I
did not know as much about this one. It seems a bit more of a "high end" thing,
but at the same time, it is loosely similar to the display system used in
Thomson TO and MO microcomputers.
I also knew that Thomson EFCIS sold the color palette circuit originally
developped for the TO9 as a general color palette chip (the TO9 has it described
as a custom chip in its technical manual, but then it became an off the shelf
chip).
Anyway, I became curious about the other chips and why and how they were
designed. And then I read a lot of datasheets and research papers and patents.
Here is what I found.
Note: this is based on some random web searches, I wasn't there at the time
(in fact I wasn't born at the time) and I could be misunderstanding or misremembering
what I read. Don't trust me too much...
What are we talking about, anyways?
EFCIS is a French semiconductor company. It was created by a public research
lab which turned part of its activity into, apparenly, an integrated circuit
design and manufacturing company. At the time, France was a bit behind the USA
in terms of semiconductors manufacturing. The government helped solve this by
injecting money into the sector, but also there would be various merge of companies
to join forces, and partnerships with US companies to first resell their chips,
and then manufacture them locally.
In the case of EFCIS, it would merge with several other companies to become
Thomson Semiconducteurs, which in turn merged with Italian SGS to become
SGS-Thomson, later shortened to ST Microelectronics. That company is still
around today.
But our story does not start there. Instead, we have to dig into what was
happening in the mid-70s in the École Normale
Supérieure (ENS). At that time, the school did not have its own computer,
instead it had a "computation center" that was mainly a terminal connected to
a computer in Orsay faculty. Students had access to that system and were also
working on electronis projects, such as a custom 12 bit computer.
The terminals at the time were either teletypes, or early CRT terminals. Either
of these options would cost quite a lot of money. Not so much when compared to a
full-size computer, but with the development of minicomputers and later of microprocessors,
the terminal was becoming a quite costly part of the system.
And that's just for text. If you wanted to display graphics, you would have to
go for something like the Tektronix 4010.
These terminals are somewhat complex and costly hardware. The 4010 is a strange
device by today's standard, it does not use RAM to store display data, instead,
the cathode ray tube itself keeps the pixels "on". This makes the system cheaper,
but has the major downside that you can only erase the whole screen, not just
subsets of it, and erasing takes a second or so. As a result, this is not giving
a great user experience for interactive graphics.
So, the people at ENS have an idea: what if we could use a TV and some RAM
to make a cheaper terminal? It would allow interactivity, and also maybe color.
Their first try was a text terminal. This was reasonable to do, with 1Kx8bit
of RAM and a character ROM, it is possible to display a 64x16 character grid on
a TV. This allowed to have a first go at generating the display timings, clock,
and pixel shifting.
There was already prior art in doing this, for example, Dan Lancaster's
TV Typewriter from 1973 is
based on very similar concepts (at only half the resolution, 32x16 characters).
That TV typewriter allowed home computers to get some kind of cheap terminal system,
and the video output in early home computers like the Apple I and the SOL-20 were based
on this design.
Anyway, with the goal of making the cost lower, one solution is to reduce
the number of chips by using an LSI (large scale integration) chip. This would
result in the SF.F 96364, with a patent application in early 1977 (FR 77.0524, also known as FR2382049, and its equivalent US patent US4328557A, attributed to Jean Gastinel), I think just
a little time before similar chips like the MC6845 were announced (but it's
hard to find exact dates).
Of course, there would be no point in manufacturing an LSI chip just for use
in the research lab, and so, the SESCOSEM, the chip manufacturer, also introduces
it for sale to the general public. I have not introduced the SESCOSEM yet, but
I don't have much info on it, it seems it merges with EFCIS just a few months
after releasing this chip, and the chip is then rebranded as EF9364.
It's also
worth mentionning that SESCOSEM was also a second source for Motorola products.
Well, or at least that's how they advertised it, it looks like initially, the
chips were manufactured by Motorola but rebadged with SESCOSEM part numbers.
This means you could get both the 6845 and the 96364 from the same company.
However, the two chips, while similar in the way they drive the CRT, are
different on the way they are interfaced on the other side. The 6845 is designed
for use with a microprocessor (initially a 6800, but people figured out how to
make it work with other CPU families as well). On the other hand, the 96364 is
designed instead to interface directly with an UART chip. This means the chip
is less flexible and has a fixed resolution of 64x16 characters, whereas the
6845 can be configured into many videomodes, but on the other hand, and unlike
the 6845, it will manage memory writes and cursor movement, with some special
commands allowing to move the cursor around.
This makes it possible to build a serial terminal ("glass teletype") from a TV
set, this chip, an UART, a character generator and 1Kx7 of RAM, with a bit of
glue logic.
So, we now have this chip that makes it easy and reasonable for DIY computer
builders to assemble a text terminal for their computers. This is taken up
by several electronics magazines and kit distributors, one of the well-known ones
being the Elektor Elekterminal . Suddenly it is possible to build a terminal that
will not require hundreds of TTL chips, or include a microcontroller, and cost
more than the computer it is attached to.
Into graphics controllers
While this text terminal is in development, the cost of DRAM chips continues
to decrease, and larger capacity chips become available. With 4K chips, it becomes
possible to build a 512x512 pixels, black and white bitmap display with 'only' 64
RAM chips. This opens a new world of possibilities, such as working on a CAD application
for electronics, replacing the long and tedious process of hand drawing transistors for
computer chips.
As the SFF96394 is finally made available to the general public in mid-78,
with terminals and VDU cards starting to appear as kits or prebuilt at the end
of that year, the design for the next system is published, as four patents and
a paper in SIGGRAPH 78 from Philippe Matherat.
Now my research was made quite a bit easier because he links to all of this from
his personal website :).
So, this next design retains the basic idea of designing a chip for use in a
Terminal. However, at this time, and for a more complex graphics terminal, it
becomes acceptable to include a microcontroller, or maybe even a microprocessor
and turn the terminal into a kind of simple workstation. This design aims to
once again reuse a standard TV receiver as the display, but support Tektronix's
PLOT10 API or even Tektronix serial line protocol. This way, the existing
applications will run on the new, cheap terminal as a starting point, and then
the terminal can be extended with color display, faster refresh times, etc.
At the time, the main constraint is not so much on the memory size. 16K DRAMs
are now available at an acceptable price for this application. The main problem
is rather the memory speed. It is not possible to output single bits from a single
DRAM chip fast enough to match the needed pixel clock (the project aims for a
512x512 interlaced display window). The soluion to this is to put several DRAM
chips in parallel (as you do when building a computer to address them in bytes),
and then use a shift register to send the bits to the display circuit.
Essentially, this removes the character ROM from the previous text based system,
instead feeding the RAM output directly into the already existing shift register.
This way, the shift register is the only thing that needs to be really fast,
the RAM will only need to fetch one 8-bit word at a time.
The second problem is fast vector drawing. Microprocessors at the time could
not be programmed to do this fast enough to keep up with even the Tektronix
terminals. Part of the reason for this is that the RAM is completely busy with
sending pixels to the display in this design, probably to keep it simpler. Other
machines at the time (such as the Apple ][) are figuring out how to intermix
accesses to the RAM for the display and CPU, but here a different direction is
taken. The CPU will have only limited access (in fact, it's possible to have
no access at all) and instead the hardware will provide high level drawing
commands, with the goal of drawing one pixel per clock cycle (during borders and
blanking, when the memory is not busy with either display or refresh cycles).
The commands start with vector operations. The display controller keeps track
of X and Y pointers and implements Bresenham's algorithm in hardware for drawing
straight lines. Additionally, it can also draw characters (from the ASCII) table,
with optional scaling, rotation, and slanting.
The chip internally manages a 4096x4096 coordinate space, of which a part is
not visible on screen. This allows line vectors to go out of screen and the drawing
to continue correctly with the next vectors. The part that would be outside the screen
are clipped cleanly and the drawing is not distorted.
Access to the memory is done in parallel (all 8 chips) when reading, but writes
are done one bit at a time, by enabling the RAS pin for just one of the chips.
The SFF96364 could fit in a 28 pins DIP package, but the new EF9365 needs a 40
pins package, and even that is a bit too limiting, so a few extra chips are needed:
a demultiplexer to select the RAM chips, a shift register, and also a PROM to derive
some signals from the clock. The EF9366 is a simplified version that can only draw
a 256x256 display, but requires a little less support logic. Both versions of the
chip do include a lightpen controller as well.
Color graphics are possible by adding parallel banks of memory chips, typically
one for each of red, green, and blue. Then, the CPU can program an external register
to store a 3 bit color, that is loaded to the RAMs when a pixel needs to be plotted.
In one of his later articles, Philippe Matherat explains why this approach
turned out not to be such a great idea: it was done with the goal of replacing
the Tektronix displays, and it did a decent job at that. But, a few year laters,
bitmap displays would become more common, and faster CPUs such as the 68000 would
be available, which would do a faster job at drawing lines and text than this
chip could do, and also allowed more advanced operations (scrolling, overlapping
windows, etc).
There were attempts to workaround these limitations, for example by adding
extra operators on the data path between the video controller and the RAM for
write operations. That would allow some operations like XOR with a cursor,
intersection with existing drawings, etc. However, adding more complex drawing
primitives require a larger redesign of the chip, more on that later.
Initially the chip finds some use, both in DIY videocard projects (again with
Elektor offering one) and in scientific hardware where it makes it possible to
show realtime and color display of measurements (for example I read about its
use to display spectrograms). While the initial design was ready by may of 1978
(as you can see in the patents and papers published then), the chip would be
available to customers only in late 1979 or early 1980. At that time, computers
and workstations with raster graphics are quickly catching up, which means the
chip did not really get a chance to be used.
Meanwhile, in Brittany, ...
Meanwhile, other innovations are going on in France. There are experiments to
replace the phonebook with an electronic device, basically a terminal that would
be plugged into a phone line and allow to send and receive data. Experiments
on this start at about the same time in 1978. Of course, the plan is to replace
the phonebook, which means the terminals will have to be produced in a large
quantity, and be cheap. This justifies the use of an LSI chip again.
This time, there is no room for complex graphics: in a desire to keep the costs
low, there are big constraints on the amount of RAM and ROM in the terminal.
And so, it's back to the simple character based system. But maybe not quite so simple
as the EF9364. The people working on this first experiment with hardware and
protocols based on ANTIOPE, the early Teletext system in France. They also want
to make the terminal user friendly, and for this they need various character
attributes, maybe colors (eventually it will be 8 greyscale levels), double
size characters. Hardware vertical scrolling ("roll") also seems useful to render
long pages of text at an acceptable speed.
These requirements will lead to a new design, initially as a set of two
chips based on the earlier combination of a text mode controller with a character
generator. Eventually, both of the chips become a lot more complex than what they
were initially, and, as it seems usual for EFCIS, they are not reserved to the
Minitel and added to the general catalog of available ICs. These are the EF9340 and EF9341,
also known as VIN and GEN and introduced in 1980. They will also find an use in the Philips Videopac+ consoles
where their video incrustation support will be used to mix the video signal with the
existing one from the Intel 8244 in the older generation Videopac.
The Minitel hardare will be revised several times over the years, to introduce
new features (such as a dialer and local phonebook, and a generic ASCII 80 column
terminal mode). This leads to new generations of video controllers, the EF9345
(introduced 1983, also ends up being used in the Matra Alice computers as well as the
Philips VG5000) and the TS9347 (introduced 1988, with EFCIS now renamed Thomson Semiconducteurs,
this one gets a new manufacturer prefix).
These chip are quite far from the initial "dumb terminal" usage. They can
redefine some custom characters in RAM, have a "compression" system where a
short byte sequence can expand into a longer string that is stored in memory,
etc. They also provide a rather large 8x10 font, fitting 40x25 characters in
a 320x256 video output.
Ok, back to the pixel based ones
The EF936x family also gets an upgrade in the form of the EF9367. This chip
allows doubling the horizontal resolution (to 1024x512 pixels), but it does
so in a not so convenient way. It looks like this was first done as an extension
to an EF9365 videocard, and then pushed back into the video chip. It also changes
the video timing: the initial timing for the 9365/66 resulted in a square, 256x256
or 512x512 window at the center of the display. But, this results in a lot of
wasted space to the left and right of that window. The new chip changes the
timing, it now operates on a slower clock but keeps the same horizontal frequency
for the display. As a result, in 256x256 or 512x512 interlaced modes, the pixels
will be slightly wider than high, matching the 4:3 ratio of the display,
and at 512x256 or 1024x512 interlaced modes, they will be slightly higher than
wide, with a ratio around 2:3, still much better than the 1:2 you would get in
the same conditions with the original chip.
Another new feature is the possibility of a 60Hz mode, reducing the video
resolution. The datasheet and appnotes also add a few extra tricks to the
circuitry, showing how to implement vertical scrolling (useful on the 60Hz version
because it reduces the vertical resolution, and so, parts of the pixels stored in
RAM are actually not visible).
Finally, one last iteration is the TS68483. This is a much extended design,
made to interface with a 68000 CPU and its 16 bit databus (but 8 bit access is
also still possible). It adds more complex drawing primitives for curves,
arcs, and so on. It also uses a larger 64 pin DIP package, which allows to have
more of the logic integrated in the chip, for example, external shift registers
are not needed. It can also produce even higher video resolutions. But, at this
point, the design with memory completely isolated from the CPU is not appropriate
anymore. Graphics terminals are a thing of the past, and other computer systems
have shown that it is possible to get quite fast drawing in software, or, if
there is anything to accelerate, it's rather moving blocks of data around, with
something like a blitter.
Anyway, the EF936x series remains an ahead of its time graphics accelerator.
It would take a few more years to see 2D acceleration implemented in hardware,
and 3D acceleration would follow.
But what if 8 colors is not enough?
In the EF936x documentation, it is suggested to use 3 bitplanes for an RGB
image. This is similar to what was used in the Thomson TO7, but it felt quite
limiting. Later models at Thomson Microcomputers added a 4th bit of color,
either darkening or brightening the other colors. This was implemented by feeding
the 4 color bits from the video generator into a lookup ROM, and then a resistor
network. Of course, this can be added to an EF936x system.
But Thomson computers did not stop there. The next step, introduced first in the
Thomson TO9, was to use a dedicated palette chip. This would have 16 color registers that would store a 12 bit color (4 bit each for R, G, and B)
and replace the ROM used in the previous models. Then, the color palette becomes fully programmable. Typically, you can switch to 16 grey levels,
or mix 8 basic colors and 8 greyscales.
Once again, EFCIS is the company manufacturing that chip, first as a custom
chip for the TO9, but later on added to their general catalog as the EF9369.
There is also a later revision, the TS9370, which can handle higher pixel clocks,
and removes the need for a separate video buffer/amplifier, further simplifying
the design. I don't know if there ever was any graphics hardware combining the
color palette chip with the graphics processors (or even the textmode one). These
products were manufactured by the same company, but for different projects, and
it's unclear if the fact that they would fit together quite well is intentional,
or just an happy incident.
There is also a TS68494 offering a 256 color palette (out of 4096 colors).
This is a placeholder article to remind me I should work on this. The mouse is not yet done. I will work on it later.
I have a VTech laptop based on a Motorola Dragonball processor. I want to do fun things with it. Maybe port EmuTOS, maybe try to run some version of Smalltalk on it.
And see how useful of a machine it could be.
There's just one problems: I own two of them actually, and one came without a mouse, and the other with a broken mouse.
These machine use quadrature-encoded mouse, similar to what is done on Amiga and Atari machines. Basically, the mouse sensors
are connected directly to the mouse cable, and the decoding logic is inside the computer.
At first I looked into fixing the mouse. There is a bit of broken plastic. Unfortunately, there is also a missing plastic bearing, and I didn't find an easily available replacement part (well, not at an acceptable price). Also, the mouse doesn't even use optical sensors for detecting mouse movements (yes, decent ball mouses do this, the ball rolls a wheel with a hole pattern in it, and the sensor detects light through the holes). Instead, it uses a wheel with copper rays, and a copper finger sliding on it. This is not durable and makes for a pretty terrible mouse.
So, why not use another mouse? I looked for the available offers for Amiga and Atari mouses, but I did not find something that's currently in stock. However, I noticed there are some optical mouses (out of stock, but they did exist at some point), and I got curious how they did it.
I didn't find info about that specific mouse, but I found someone else who did something interesting. It turns out some optical mouse sensors provide quadrature outputs simulating what you would get from a ball mouse sensor. So I started disassembling all mice I could get my hands on. This caused a few weird looks from my colleagues at work, and it turns out my workplace didn't have old enough mouses (this feature is of course gone from modern sensors for cost reduction).
Eventually I found what I was looking for. The winner is a wireless HP 5187TURF mouse. Since it is wireless, they didn't need to use a sensor that outputs PS/2 or USB. So they use one that outputs quadrature encoding and couple it with a microcontroller. This has another benefit: the mouse has a battery compartment. This will come in useful because VTech has a long experience designing laptops that will be used by children. This means all kind of weird things (solid or even liquid) could make their way into all the connectors. As a result, all connectors are protected with diodes and resistors to make sure there is no way to cause a short circuit that would damage anything. In the case of the mouse port, this means there is no +5V power supply connection. Instead, the mouse just connects the quadrature outputs to the ground or to nothing. There are pull-ups inside the computer that allow this to work. So, the batteries in the mouse will allow to power the mouse sensor, and that couldn't be done from the computer.
Now, all I need to do is solder some wires and add a cable to this mouse. To be continued...
Hello! Well it's been... almost a year already!? since the last article
about Smalltalk on Amstrad CPC here.
I did make quite some progress on it since then. First, I updated to the
latest version of SDCC and optimized the code in several passes so I could free
up enough space (a few hundred bytes) to work comfortably in my ROM.
I then wrote an incremental garbage collector. The idea is to allocate things
until I fill up one 16K bank, and then immediately garbage collect it. In the
current naive Smalltalk implementation, there are a lot of temporary objects
allocated just to run message sending (creating contexts and so on). That means
a lot of the allocations are just very short lived temporary objects, and by the
time we get to one filled 16K bank, about half of it, possibly more, can already
be freed.
With this allocator, I can reclaim some memory, and that's enough to get the
interpreter running! There's a problem however: all of it is very slow. The time
to compute a simple expression like "3 + 4" is counted in minutes. That's not
exactly great.
Initially that time was about half the garbage collector, and half the interpreter.
I have then optimized the garbage collector quite a bit, but overall things are still quite
slow.
I have now concluded that maybe the idea of an incremental garbage collector
wasn't that great. In fact, the GC doesn't preserve any data between GC cycles.
This means every time it runs, it has to scan the entire memory again to find
what can be reclaimed. Likely the scanning takes more time than the reclaiming.
After some interesting discussions (in particular with Millihertz, thanks!)
One thing that should have been obvious to me is that non-incremental, stop the
world garbage collectors will be simpler, and therefore faster than incremental
ones. Reading my old posts, I realize that I dismissed the baker two space GC
because I didn't like the idea of wasting half the RAM for heap space used only
during GC cycles. But, doing so, I immediately started looking at incremental
solutions. Thinking about this more, I realized that it doesn't have to be the
case. There can be other non-incremental algorithms!
My GC is doing a full scan of all objects in the heap. It's a bit silly to
do that and then use the acquired data to garbage collect only one bank, and
then let the interpreter run again, allocate some memory, change some references,
and when the next bank is full, the GC will have to start all over again, because
whatever information it could have gathered, is invalidated by changes made by
the interpreter.
So, here is a new strategy: I can let the interpreter run until the whole RAM
is filled with objects, but one last bank. Then, I can start a GC cycle. I can
then try to scan the heap only once to move objects from multiple banks at the
same time.
Scan the heap to find all objects currently in use
Take one bank, move all its marked objects to the last free bank and update all references to it
Now that bank is empty, add it to the list of free banks
Take the next bank, move its used objects to the banks in the free list and update all references
Repeat until all banks that have some freeable objects have been repacked
Basically, this would allow to do something similar to the simple two space
GC algorithm, but do it without having half the memory unused most of the time.
The cost is probably some extra complexity to manage tracking the live objects.
I'll have to think through it.
There are other ways to trade memory waste for speed. For example, having
indirection tables in each bank (or for each bank) would allow to track objects
in each bank separately. Sorting objects by classes (or by size buckets) could
also simplify the garbage collection (it would be doable without having to move
objects around).
So, is this going to be faster? Well not if we do just this change. The slow
part in my current implementation is scanning the whole heap to find references
to a bank. Without changing anything else, I'll still need to do that over and
over for each bank that's collected. So, I wouldn't win much.
I hope that there is some data locality. That is, not all banks have references
to all other banks. Then, things can become more efficient if we track the backreferences,
something like this:
In each the bank header, have a bitset to indicate which other banks it refers to. This needs 32 bits as I have 32 banks.
At the start of the GC cycle, scan over each bank and set bits for each other banks it has references to.
When updati ng references to a bank, we can now scan only the other banks that have references to it.
Hopefully, there will be relatively few pointers that go between banks. And
so when moving one bank, we need to update references only in one or two other banks.
Since the computation of that bit map was relatively expensive (iterating over
all of the heap), the GC should try to make the best use of that effort, and so
it should run a GC cycle on all the banks, not just one.
Alternatively, it could be interesting to keep this bitfield updated at all
times, setting the bits whenever a new reference is written into the bank. In
that case, the GC wouldn't need to recompute it, and could assume it is always
up to date. But the interpreter would have to be careful to keep it up to date.
I think I don't want to make the interpreter more complicated than it already
is, for now.
This solution is a bit imperfect: in Baker's GC, the exploration of the heap
is done breadth-firth by following pointers down the object tree starting from
the root. But this means objects are moved to the newspace from all over the
place, and their oldspace memory must be kept for the entire duration of the
scavenge to store redirection pointers. This allows to make sure that all live
objects and nothing more have moved to the newspace.
Since I want to try to use more than half of the memory, I will be reusing my
freshly freed banks before the complete GC cycle is done. So, keeping them to
store "redirections" during the whole GC run is not possible. That means I have to be sure that all references
to a bank are updated before I start collecting the next one. I can't move objects
coming from all over the place, otherwise I could fill my last free memory bank
before I can free another one to continue the garbage collection. So, I have to
traverse objects in memory order, and that means I'll probably end up needlessly
moving objects that are in fact unreachable. It's not so bad, this doesn't break
any invariants. But it means the GC is not as efficient as it could be. Such "zombie" objects
will only be deleted if all references to them are from inside the same bank.
To help with that, maybe I can use the info I have about which bank refer to
which other one. After moving a bank, the new one will hopefully have some empty
space. I should fill that in priority with objects that reference that same bank,
or at least objects which come from a bank that references the one being filled.
This way, there's a chance to group related references together, and have them
removed at the next GC cycle. We'll see how that goes...
Breaking news!
I mentioned at the start of the post that I had updated SDCC to the latest
version. It was a bit more ocmplex than that: for a while I had been running a
source snapshot, because SDCC 4.4.0 had been generating broken code.
SDCC 4.5.0 was recently released, and I updated the Haikuports recipe so I
could use that. In the meantime, Haikuporter (the tool used to build the packages)
was updated to do some extra checks, and it refused to build my new package
because SDCC was being configured without optimizations. Waddlesplash took a look
at that, fixed the problem, and tested it, and found that my issues with the build
of the interpreter taking 45 minutes to an hour are now resolved. This is maybe
helped not only by the optimizations, but also by the replacement of the memory
allocator in Haiku.
In any case, the whole Smalltalk ROM can now be built with a released compiler,
and in just 6 or 7 minutes instead of an hour. That will surely make it more fun
to experiment with in the future!
Four years ago, I wrote an article starting with a note on how I had not made
any progress on VTech hacking projects since 2017. So it seems it's about time for
a new update.
That old article has a few wrong things. First of all, the CQFD Scientus is
not a z80 computer as I expected, instead it is a 68HC05. Which is a bit
ridiculous given it has quite a lot of ROM and RAM, and so, that poor CPU with a
tiny address space is constantly bankswitching, making the ROM not very
fun to disassemble. I understood the banking scheme and a few other things, but
didn't go much further, because that doesn't seem like a very fun platform to
write code for.
The other wrong thing is about the mysterious audio chip. I wrote that it
could be just a volume controller, but I think that's not right and I have
since identified a possible TI microcontroller with matching pinout. But I
forgot what it was.
Anyway, a lot has happened as I got into the V.Smile console. This also has
a weird CPU but at least no bank switching. I ended up porting an entire compiler
to it that is now working acceptably (the code is not optimized, but it works).
The next step is figuring out some issues with controller input and writing drivers
for the sound system, and then probably trying to make some games for it.
But that's not what I'm writing about today. I also have some news about the
IQ Laptop. I had bought a german version of it, but since then I also found a French
one, called VTech Le Manager. The packaging and user manual seems a bit confused,
not sure if it is just a kid's toy or a serious machine. I'd say it fails to
deliver on both fronts: not really usable as a serious machine, but also not
fun enough to be a good toy. The hardware seems pretty good, anyway, so, as long
as the software can be replaced, that should be fine.
I mentionned a Linux port in the previous post, well, that is now working.
It runs from an external cartridge for now. I would like to replace the internal
ROM instead, and I have learnt that the French version of the laptop may have
shipped with flash chips internally instead of ROM. Either they needed to update
it until the last minute, or the production volume was so low that they didn't
bother making proper ROMs for it. I will try to dump that memory for future emulation.
Here are some random notes about the machine:
2MB of RAM and 2MB of ROM (or Flash internally)
Grounding one of the CPU pins can switch it into an internal debugger, allowing to load and execute code from the serial port. So it's easy to prototype things on.
The cartridge port uses a 45 pin, single row, 1mm spacing connector. Connector references are either JST ICM-A45H-SD14 or DDK MCB-545A, but both of these are out of stock and not manufactured. Sullins SFM210 could be somewhat compatible (right number of pins and spacing at least). To be confirmed when I get to make expansion RAM cartridges.
0000 0000 - 001F FFFF: 2MB internal RAM
0020 0000 - 002F FFFF: Internal ROM or Flash
0300 0000 - 0307 FFFF: Internal data flash
0300 0000 - 03FF FFFF: Internal IO space
0400 0000 - 04FF FFFF: Cartridge port 1
0500 0000 - 05FF FFFF: Cartridge port 2
Each cartridge port gets 22 address bytes for up to 16MB of address space. Extra RAM cartridges could push this thing all the way to 34MB of RAM! I received GERBER files from Alexandre for his cartridge and adapter, I can use that to document the cartridge port pinout if I need to make my own cartridges. And I also wonder how much the DragonBall EZ CPU could be overclocked safely?
David Given has ported EmuTOS to the AlphaSmart Dana, which uses the same CPU. With that and Alexandre's Linux work as sample (for the keyboard driver mainly), an EmuTOS port to this machine should be within reach?
Ce texte a été rédigé pour une dépêche sur Linuxfr.
L'équipe de modération du site a préféré en publier une version découpée en plusieurs parties
car c'est beaucoup trop long pour un article sur le site. (ils ont probablement raison:
lisez à votre rythme, faites des pauses, n'hésitez pas à revenir plus tard!)
Vous trouverez ici la version intégrale rédigée avant découpage. Merci à orfenor, BAud,
palm123, WrathOfThePixel, bobble bubble et TsyMiroro MDG qui ont participé à la rédaction
ou à la relecture de ce texte.
Haiku est un système d’exploitation libre destiné aux ordinateurs
personnels ou de bureau (pas de serveurs, pas de systèmes embarqués, pas
de tablettes ni de téléphones). Il s’agit au départ d’une réécriture
libre de BeOS, préservant la compatibilité binaire avec ce dernier (les
applications BeOS peuvent tourner sur certaines versions de Haiku).
Le projet Haiku (au départ nommé OpenBeOS) a démarré officiellement
le 18 août 2001 avec le premier message sur la liste de diffusion : Ok, let’s
start (OK, allons-y). Cet anniversaire est l’occasion de faire un
point sur les développements de l’année dans Haiku et ce qui est en
préparation.
La dépêche a été un peu retardée cette année, pour être synchronisée
avec la version R1 beta 5 de Haiku, publiée le vendredi 13 septembre
2024.
Le projet emploie un développeur presque à plein temps depuis 2021 et
le reste de l’équipe contribue bénévolement. La dernière version bêta a
été publiée fin 2023 et la prochaine (Bêta 5) ne devrait pas tarder.
Près
de 350 tickets ont été clos dans le cadre du travail sur la version
R1 bêta 5. Il y a bien sûr de très nombreuses corrections de bugs, qui
ne seront pas listées dans cet article. On se concentrera plutôt sur les
nouveautés, sauf dans les cas où la correction est vraiment importante
ou permet d’ouvrir de nouvelles possibilités d’utilisation.
Applications
Haiku est un système d’exploitation complet, fourni avec un certain
nombre d’applications permettant d’accomplir les tâches les plus
courantes. En plus de ces applications de base, le gestionnaire de
paquets HaikuDepot,
alimenté principalement par le travail du projet HaikuPorts,
apporte à la fois des applications portées depuis d’autres systèmes et
des applications développées spécifiquement pour Haiku.
De façon générale, on trouve cette année dans les applications de
Haiku des améliorations sur le rendu des nombres, l’utilisation d’un
symbole de multiplication à la place d’une lettre x là où c’est
pertinent, et de nombreuses petites corrections et améliorations sur la
mise en page des fenêtres, des corrections de problèmes de traduction et
ainsi de suite.
AboutSystem
AboutSystem est la fenêtre d’information sur le système Haiku. Elle
fournit quelques informations sur la machine sur laquelle le système
fonctionne (quantité de RAM, marque et modèle du CPU, uptime) ainsi que
les noms des développeurs et autres logiciels libres ayant participé au
développement de Haiku.
Cette application reçoit tout d’abord une mise à jour cosmétique : si
le système est configuré en “mode sombre”, le logo Haiku correspondant
(avec un lettrage blanc et des dégradés de couleurs un peu différents)
sera utilisé. Sinon, ce sera le logo avec lettrage noir.
Elle reçoit également quelques mises à jour de contenu : en plus de
l’ajout de quelques nouveaux contributeurs qui ont rejoint le projet, on
trouvera maintenant un lien vers la page web permettant de faire un don
à Haiku. Plusieurs liens vers des bibliothèques tierces utilisées dans
Haiku, qui ne fonctionnaient plus, ont été soit supprimés, soit
remplacés par des liens mis à jour.
Enfin, il est désormais possible d’utiliser AboutSystem comme un
“réplicant”, c’est-à-dire de l’installer directement sur le bureau pour
avoir en permanence sous les yeux les statistiques sur l’utilisation
mémoire et l’uptime ainsi que le numéro de build de Haiku en cours
d’exécution (ce qui peut être utile par exemple lorsqu’on lance beaucoup
de machines virtuelles avec des versions différentes de Haiku pour
comparer un comportement, ou si on veut stocker des captures d’écran de
plusieurs versions et s’y retrouver facilement).
CharacterMap
L’application “tables de caractères” permet d’étudier de près les
différents glyphes et symboles présents dans une police de caractères.
En principe, elle permet de choisir une police spécifique, mais le
serveur graphique de Haiku substitue automatiquement une autre police si
on lui demande d’afficher un caractère qui n’est pas disponible dans la
police demandée.
Cela peut être gênant dans certains contextes, par exemple si on
envisage d’embarquer une police dans un fichier PDF, il est difficile de
savoir quelle police contient vraiment les caractères qu’on veut
utiliser.
L’application a été améliorée pour traiter ce cas et affiche
maintenant ces caractères en grisé.
CharacterMap affichant des caractères
manquants
CodyCam
CodyCam est une application permettant de tester une webcam et de
l’utiliser pour envoyer périodiquement des images vers un serveur
HTTP.
L’évolution principale a été la mise à jour de l’icône de
l’application. L’utilité de CodyCam est limitée par le manque de
pilotes : il faudra soit trouver une webcam Sonix du début des années
90, seul modèle USB à disposer d’un pilote fonctionnel, soit utiliser un
ordiphone Android équipé d’un logiciel permettant de le transformer en
caméra IP (ou encore une vraie caméra IP).
Le pilote pour les WebCams UVC –
standard utilisé pour les caméras USB modernes – n’est pas encore au
point et n’est pas inclus dans les versions publiées de Haiku.
Debugger
Debugger est, comme son nom l’indique, le debugger de Haiku.
Il est développé spécifiquement pour le projet sans s’appuyer sur les
outils existants (gdb ou lldb). Il dispose à la fois d’une interface
graphique et d’une interface en ligne de commande, plus limitée. Cette
dernière est surtout utilisée pour investiguer des problèmes dans les
composants de Haiku qui sont nécessaires pour l’utilisation d’une
application graphique : app_server, input_server ou
encore registrar.
La version en ligne de commande a reçu quelques petites
améliorations, mais la principale nouveauté est la prise en charge des
formats DWARF-4 et DWARF-5 pour les informations de debug. Cela permet
de charger les informations générées par les versions modernes de
GCC, sans avoir besoin de forcer la génération d’une
version plus ancienne du format DWARF.
Le désassembleur udis86, qui n’est plus maintenu et ne reconnaît pas
certaines instructions ajoutées récemment dans les processeurs x86, a
été remplacé par Zydis.
Enfin, un bug assez génant a été corrigé : si une instance de
Debugger était déjà en train de débugger une application et qu’une
deuxième application venait à planter, il n’était pas possible
d’attacher une deuxième instance de Debugger à cette application. Les
problèmes impliquant plusieurs processus pouvaient donc être un peu
compliqués à investiguer. C’est maintenant résolu.
Deskbar
L’application DeskBar fournit la “barre des tâches” de
Haiku. Elle permet de naviguer entre les fenÊtres et applications
ouvertes, de lancer de nouvelles applications via un menu (similaire au
“menu démarrer” de Windows), ou encore d’afficher une horloge et des
icônes fournis par d’autres applications (sous forme de réplicants).
Tous les menus utilisent maintenant la police “menu” choisie dans
les préférences d’apparence du système. Auparavant, la police “menu” et
la police “plain” étaient mélangées. Ces deux polices étant identiques
dans la configuration par défaut, le problème n’avait pas été
repéré.
Si un nom de fenêtre est tronqué dans la liste des fenêtres, le nom
complet peut être affiché dans une infobulle.
L’icône pour les fenêtres dans la DeskBar a été remplacée. La
nouvelle icône indique plus clairement si une fenêtre se trouve dans un
autre bureau virtuel (dans ce cas, activer cette fenêtre provoquera un
changement de bureau).
GLTeapot
GLTeapot est une application permettant de tester le rendu OpenGL, en affichant un
modèle 3D de la théière
de l’Utah.
En plus de la théière, cette application affiche un compteur du
nombre d’images affichées par secondes. Bien que les chiffres affichés
ne soient pas du tout représentatifs des performances d’un rendu 3D
réaliste, certains utilisateurs insistent lourdement pour pouvoir faire
le concours de gros chiffres de nombre d’images par seconde.
Il est donc à nouveau possible de désactiver la synchronisation sur
le rafraîchissement de l’écran, et demander au processeur de réafficher
la théière plusieurs centaines de fois par seconde, bien que l’écran
soit incapable de suivre le rythme. Par défaut, la synchronisation est
activée et le rafraîchissement ne dépassera jamais 60 FPS, si toutefois
le pilote graphique implémente les fonctions de synchronisation
nécessaires.
HaikuDepot
HaikuDepot est un hybride entre un gestionnaire de paquets et un
magasin d’applications.
Il se compose d’un serveur (développé en Java) fournissant une API
REST et permettant de collecter les informations sur les applications
(icônes, captures d’écrans, catégories, votes et revues des
utilisateurs, choix de la rédaction pour les applications mises en
avant, …), d’un frontend web minimaliste et d’une application native C++
permettant d’afficher ces données.
La principale nouveauté est l’intégration du système de
single-sign-on (SSO) permettant d’utiliser un compte
utilisateur commun avec d’autres services en ligne de Haiku.
Actuellement, l’outil de revue de code Gerrit utilise ce même
compte, mais ce n’est pas encore le cas pour Trac (outil de suivi des
bugs) ni pour le forum. Ce sera mis en place plus tard.
De façon peut-être moins visible, mais pas moins importante, le code
C++ de l’application a reçu de nombreuses améliorations et optimisations
“sous le capot”, rendant l’application plus rapide et plus fiable, mais
qui sont un peu difficiles à résumer dans le cadre de cette dépêche.
Enfin, l’apparence de l’application a été légèrement retravaillée
pour mieux s’adapter aux écrans à haute définition (ce qui nécessite
d’avoir des marges et espacements de taille dynamique en fonction de la
taille de texte choisie par l’utilisateur).
Icon-O-Matic
Capture d’écran de l’éditeur
d’icônes
Icon-O-Matic est un éditeur d’icônes. Il permet d’exporter les
fichiers au format HVIF, un format vectoriel compact qui permet de
stocker les icônes dans l’inode d’en-tête des fichiers pour un
chargement et un affichage rapide.
Cette application a bénéficié du travail de Zardshard pendant le
Google Summer of Code 2023, elle a donc reçu plusieurs évolutions et
corrections importantes (dont certaines sont mentionnées dans la dépêche
anniversaire de l’année dernière).
Citons en particulier l’ajout d’un nouveau type de transformation,
“perspective”, qui permet de facilement déformer un ensemble de chemins
vectoriels selon une projection de perspective, ce qui est assez utile
pour concevoir plus facilement une icône avec un aspect tridimensionnel
(bien qu’en pratique l’apparence habituelle des icônes de Haiku soit un
intermédiaire entre une projection perspective et une vue isométrique,
ne se prêtant pas forcément à l’utilisation de cette opération de
transformation purement mathématique).
Une autre petite amélioration est l’ajout d’une vérification pour
empêcher la fenêtre de Icon-O-Matic de se positionner en dehors de
l’écran, par exemple si on a déplacé la fenêtre vers le bas de l’écran,
enregistré cette position, puis relancé l’application dans une
résolution d’écran plus réduite. Dans ce cas, la fenêtre sera
automatiquement ramenée dans la zone visible de l’affichage.
Magnify
L’application Magnify permet d’afficher une vue zoomée d’une partie
de l’écran. Elle peut servir pour améliorer l’accessibilité (mais n’est
pas idéale pour cet usage), mais aussi pour les développeurs
d’interfaces graphiques qui ont parfois besoin de compter les pixels
pour s’assurer que leurs fenêtres sont bien construites.
En plus de l’affichage zoomé, l’application permet d’afficher
l’encodage RGB de la couleur d’un pixel, ou encore de placer des
“règles” permettant de vérifier l’alignement des objets. Ces dernières
ont reçues une petite mise à jour, avec une amélioration de l’affichage
de leur largeur et hauteur pour les rendre plus lisibles.
Magnify avec une ‘règle’
MediaPlayer
L’affichage des sous-titres ne fonctionnait pas correctement, il
manquait une partie du texte. C’est maintenant corrigé.
PowerStatus
Capture d’écran de PowerStatus: fenêtre
principale et icône de la DeskBar avec son menu
L’application PowerStatus permet de surveiller l’état de la batterie
pour les ordinateurs qui en disposent.
Elle a reçu plusieurs améliorations importantes:
Une notification a été ajoutée pour un niveau de décharge très faible
(en plus du niveau faible déjà présent). Ces deux niveaux peuvent être
paramétrés à un pourcentage choisi de décharge de la batterie, et
associé à des sons d’alerte spécifiques. Avant ces changements, il était
facile de ne pas voir le message d’alerte (affiché seulement pendant
quelques secondes) ou de se dire qu’avec 15% de batterie on a encore le
temps de faire plein de trucs, puis se retrouver un peu plus tard avec
une batterie vide sans autre avertissement.
L’état “not charging” est maintenant détecté et correctement affiché,
pour une batterie au repos : ni en train de se charger, ni en train
d’alimenter la machine. C’est en particulier le cas d’une batterie déjà
chargée à 100%, si la machine reste connectée au réseau électrique.
L’icône de statut de la batterie s’installe automatiquement dans la
DeskBar au premier démarrage de Haiku sur les machines disposant d’une
batterie.
Le réglage du mode “performance” ou “économie d’énergie” est
enregistré et ré-appliqué lors des prochains démarrages (ces modes
configurent l’ordonnanceur du noyau pour exécuter un maximum de tâches
sur tous les coeurs du processeur, ou bien au contraire pour essayer de
garder ces coeurs en veille autant que possible s’ils ne sont pas
nécessaires).
SerialConnect
SerialConnect est une application de terminal série, utile
principalement aux développeurs de systèmes embarqués et autres gadgets
électroniques.
Elle est encore en cours de développement et propose pour l’instant
les fonctions les plus basiques. Il est maintenant possible de coller du
texte depuis le presse-papier pour l’envoyer sur un port série, ce qui
est pratique si on veut envoyer plusieurs fois la même séquence de
commandes.
ShowImage
ShowImage est la visionneuse d’images de Haiku. Elle utilise les
traducteurs, des greffons avec une API standardisée qui
permettent de convertir différents formats de fichiers entre eux.
L’interface graphique de ShowImage a été mise à jour pour utiliser le
“layout system”. Historiquement, dans BeOS, tous les éléments des
interfaces graphiques devaient être positionnés manuellement avec des
coordonnées en pixels, ce qui est pénible à faire, surtout si on doit
traiter tous les cas (polices de caractères de différentes tailles,
remplacement des textes lors de traductions, utilisation de thème
d’interfaces différents), et aussi lors d’évolution de l’application (si
on veut insérer un élément en plein milieu, il faut souvent décaler tout
ce qui se trouve autour).
D’autre part, ShowImage dispose maintenant d’un menu permettant
d’ouvrir l’image affichée dans un éditeur d’images.
Terminal
Le Terminal de Haiku permet d’exécuter un shell (c’est
bash par défaut) et toutes les applications conçues pour un
affichage en console.
Les principaux changements cette année sont la correction d’un
problème sur la configuration des couleurs utilisées par le Terminal (il
y avait un mélange entre le nom anglais et le nom traduit des couleurs,
empêchant d’enregistrer et de relire correctement le fichier de
configuration), ainsi que des modifications sur les raccourcis clavier
utilisés par le Terminal lui-même (en particulier pour naviguer entre
plusieurs onglets) qui entraient en conflit avec ceux utilisés par les
applications lancées dans le terminal.
Le terminal est également capable de traiter les “bracketed paste”,
c’est-à-dire que les applications en console sont informées que des
caractères en entrée proviennent du presse-papier. Cela permet par
exemple à bash de ne pas exécuter directement des commandes qui sont
collées, mais de les mettre en surbrillance et d’attendre que
l’utilisateur valide cette saisie.
D’un point de vue plus bas niveau, les pilotes TTY
utilisés pour les ports série et pour le Terminal, qui étaient
indépendants, ont été unifiés afin d’éviter de devoir corriger tous les
bugs deux fois dans deux versions du code presque identiques.
Tracker
Tracker est le navigateur de fichiers de Haiku. Tout comme la
DeskBar, il fait partie des quelques rares morceaux de BeOS qui ont été
publiés sous licence libre par Be et ont donc pu être repris directement
par Haiku. Contrairement à la DeskBar, la publication du code du Tracker
avait conduit à l’apparition de nombreux forks, chacun
améliorant à sa façon le logiciel. La version utilisée par Haiku
provient principalement du projet OpenTracker,
mais a réintégré ou réimplémenté au fil du temps les modifications
faites dans d’autres variantes.
Le Tracker est un composant central de l’interface de Haiku et a donc
reçu un nombre assez important d’évolutions :
La taille des fichiers est maintenant affichée à l’aide de la
fonction string_for_size qui s’adapte aux conventions de la
langue et du pays choisi par l’utilisateur.
Les brouillons de courrier électronique disposent maintenant de leur
propre type MIME et de l’icône associé. Ils s’ouvriront dans un éditeur
de mail plutôt que dans une fenêtre de lecture de message.
Pour les fichiers qui disposent d’un attribut “rating” (évaluation),
ce dernier est affiché avec des étoiles pleines ou vides selon la note
attribuée. La note va de 0 à 10 mais il n’y a que 5 étoiles. Le caractère
demi-étoile permet d’afficher la note exacte avec les versions
récentes d’UNICODE (depuis 2018 en fait, mais il a fallu attendre la
disponibilité dans une police de caractères).
Une fenêtre du Tracker, montrant la
colonne taille et la colonne rating
La gestion des dossiers en lecture seule a été améliorée. Ils sont
affichés sur fond gris (au lieu d’un fond blanc pour les dossiers
modifiables) et tous les menus correspondant à des opérations non
autorisées sont désactivés (au lieu d’être activés, mais d’aboutir sur
une erreur car le dossier est en lecture seule).
Dans le même esprit, le bouton “ouvrir” des boîtes de dialogues
d’ouverture de fichier est désactivé si le fichier sélectionné ne peut
pas être ouvert (c’était déjà le cas, mais tous les cas possibles
n’étaient pas bien pris en compte).
Un problème d’affichage sur le système de fichier
packagefs a été corrigé : les dossiers n’ont pas de taille
et affichent donc - au lieu d’une taille fixe de 4 Kio qui
n’a aucune signification.
La fenêtre de recherche a reçu quelques évolutions, voir plus bas
dans la section dédiée au Google Summer of Code, qui détaille le travail
réalisé à ce sujet.
Le menu “templates”, utilisé quand on fait un ‘clic droit ->
Nouveau…’ et qui permet de créer différents types de fichiers et de
dossiers à partir de fichiers de référence, peut maintenant contenir des
sous-dossiers, pour les personnes qui utiliset beaucoup cette
possibilité, avec par exemple des modèles de documents pré-remplis pour
différents usages.
Enfin, un peu de nettoyage interne : les classes NavMenu et
SlowContextPopup, qui permettent la navigation dans les sous-dossiers
via des menus popup, ont été fusionnées en une seule classe car elles
sont toujours utilisées ensemble. Cela simplifie le code pour
l’affichage de ces menus, qui a quelques particularités pour permettre
une navigation confortable même sur un disque dur un peu lent.
TV
L’application TV utilisée pour recevoir la TNT à l’aide d’un tuner
approprié a été déplacée dans le paquet haiku_extras et
n’est donc plus installée par défaut. La plupart des gens ne disposant
pas d’un tuner compatible sur leur ordinateur, cette application était
source de confusion et de nombreuses questions sur le forum.
WebPositive
WebPositive est le navigateur web de Haiku. Il utilise le moteur
WebKit développé conjointement par Apple, Igalia et Sony
principalement.
En dehors de la mise à jour du moteur vers une version plus récente,
WebPositive reçoit actuellement peu d’évolutions, les développeurs étant
malheureusement trop occupés par ailleurs. On peut toutefois mentionner
une correction sur la barre de signets : celle ci ne s’affichait jamais
lorsque la langue du système était autre chose que l’anglais.
Zip-O-Matic
Zip-O-Matic est un outil permettant de créer une archive zip
facilement depuis le Tracker. Il a reçu une amélioration qui est en fait
une correction d’un problème qui existait depuis longtemps : l’archive
créée est maintenant automatiquement sélectionnée dans le navigateur de
fichier à la fin du processus, ce qui permet de facilement la retrouver
pour la renommer, la déplacer ou l’envoyer à son destinataire.
Haikuports
Haikuports est un projet indépendant de Haiku, il se charge
d’alimenter le dépôt de paquets. Au départ il s’agissait principalement
d’un projet pour le portage de bibliothèque et de programmes venant
d’autres systèmes (d’où son nom), mais il est également utilisé pour la
plupart des applications natives développées pour Haiku.
Le dépôt de paquets contient actuellement 4193 paquets, il est mis à
jour en continu par une petite équipe de bénévoles qui ne sont pas
forcément tous développeurs, mais tout de même capables de faire les
tâches de maintenance ainsi que le développement de quelques patchs
simples.
Il est impossible de lister toutes les mises à jour et ajouts, le
projet HaikuPorts étant très actif. Donc voici une sélection arbitraire
de quelques nouveautés et mises à jour.
Applications natives
Mises à jour de Renga (client XMPP), PonpokoDiff (outil de diff),
2pow (clone de 2048), StreamRadio (lecteur de podcasts), NetSurf
(navigateur web léger), …
Genio, un IDE pour Haiku avec gestion de la complétion de code via
le protocole LSP (compatible avec les outils développés pour VS Code par
exemple).
Ajout de HaikuUtils, un ensemble d’outils de développement et de
debug divers.
WorkspaceNumber, un replicant pour afficher le numéro du bureau
actif dans la DeskBar.
KeyCursor, un outil pour contrôler le curseur de souris à l’aide du
clavier.
BatchRename, un outil pour renommer automatiquement des fichiers par
lot.
Applications portées
Un gros travail a été fait sur le portage de KDE Frameworks et des
applications l’utilisant. De très nombreuses applications Qt et KDE sont
donc disponibles.
Du côté de GTK, il n’existait pas de version de GTK pour Haiku, le
problème a été résolu à l’aide d’une couche de compatibilité avec
Wayland qui n’implémente pas le protocole Wayland mais réimplémente
l’API de la libwayland. Les applications GTK arrivent petit à petit,
mais l’intégration est pour l’instant beaucoup moins bonne qu’avec Qt,
qui dispose lui d’un vrai port utilisant les APIs natives directement.
L’apparence des applications est très visiblement différente, certaines
touches du clavier ne fonctionnent pas. C’est donc encore un peu
expérimental.
Enfin, pour compléter les possibilités d’outils graphiques, la
bibliothèque Xlibe implémente les APIs de la libx11 (mais pas le
protocole de communication de X) et permet de porter les applications
FLTK par exemple, ainsi que celles utilisant directement la libx11. Il
reste encore des problèmes avec les applications utilisant Tk (si vous
connaissez Tk ou X, les développeurs de Xlibe aimeraient bien un petit
coup de main). En attendant, les applications Tk sont utilisables à
travers un portage de undroidwish, mais ça reste peu confortable.
Du côté des compilateurs et des langages de programmation : LLVM a
été mis à jour en version 17. GCC est disponible en version 13 et peut
maintenant être utilisé pour compiler du FORTRAN et de l’Objective-C.
Les dernières versions de Python sont disponibles, et en plus avec des
performances améliorées. Node.JS est également mis à jour, ou si vous
préférez le langage R, vous le trouverez également, avec son IDE associé
rkward.
Bien sûr, la plupart des bibliothèques et outils disponibles sur
d’autres systèmes sont aussi disponibles : ffmpeg (en version 6), Git,
libreoffice, Wireshark, …
Mentionnons enfin un pilote FUSE pour monter des volumes réseau NFS,
qui vient compléter les deux implémentations de NFS présentes dans le
noyau (une obsolète qui implémente NFS2, et une plus récente
implémentant NFS4, mais qui est instable et pas activement maintenue
actuellement).
Panneaux de préférences
Appearance
Les préférences “Appearance” permettent de configurer l’apparence du
système et des applications : principalement les polices de caractères
et les choix de couleurs.
C’est ce dernier qui reçoit une mise à jour en profondeur, avec
l’ajout d’un mode automatique. Auparavant, chaque couleur utilisée par
le système devait être configurée manuellement, ce qui permet un
contrôle très fin, mais demande de passer un certain temps à faire des
ajustements. Le mode automatique permet de configurer seulement 3
couleurs : le fond des fenêtres, les barres de titres, et une couleur
d’“accentuation”. Les autres couleurs et nuances sont calculées
automatiquement à partir de cette palette de base.
En particulier, il devient beaucoup plus facile de choisir un fond
sombre pour se retrouver avec un système en mode sombre, très à la mode
chez certain.es utilisateurices de Haiku.
Il est toujours possible d’activer le mode avancé pour affiner les
réglages si nécessaire (ou si vous aimez les interfaces graphiques
bariolées et multicolores).
Le mode automatique dans l’application
AppearanceLa même capture d’écran avec une
configuration “mode sombre”
Keymap (disposition clavier)
L’application Keymap permet de configurer la disposition de touches
du clavier. Le point qui a reçu un peu d’attention est la gestion de la
configuration des touches modificatrices.
Haiku est un dérivé de BeOS qui lui-même a été au départ inspiré de
Mac OS. On conserve de cet héritage l’utilisation des touches
commande et option au lieu de meta et
alt sur les claviers de PC. Mais BeOS et Haiku sont conçus pour
être utilisés avec des claviers de PC. La touche commande qui
prend la place de la touche ALT est donc celle utilisée pour la
plupart des raccourcis claviers. Cela se complique si on essaie
d’utiliser un clavier fabriqué par Apple (les codes de touches renvoyés
par le clavier pour des touches situées au même endroit ne sont pas les
mêmes), ou encore si on a besoin d’une touche AltGr (historiquement
utilisée comme touche option par BeOS, mais aujourd’hui ce rôle
est plutôt attribué à la touche windows apparue un peu plus
tard). Une page sur le
wiki de développement de Haiku tente de résumer l’historique et la
situation actuelle.
La configuration des touches modificatrices est donc un sujet
complexe, et il est probable que le comportement sera a nouveau modifié
plus tard. Quoi qu’il en soit, en attendant, l’application Keymap permet
toutes les permutations possibles de configuration de ces touches.
Screen (Affichage)
Les préférences d’affichage, en plus de permettre de changer la
résolution d’écran, affichent quelques informations essentielles sur la
carte graphique et l’écran en cours d’utilisation. Pour les écrans, ces
informations sont généralement extraites des données EDID, mais il y a
une exception : les dalles d’affichage des PC portables n’implémentent
en général pas ce protocole. Les informations sont donc récupérées par
d’autres moyens parfois moins bien normalisés. Par exemple,
l’identifiant du fabricant est un code à 3 lettres. En principe, les
fabricants doivent s’enregistrer auprès d’un organisme qui attribue ces
codes, afin d’en garantir l’unicité.
Cependant, certains fabricants ne l’ont pas fait, et se sont choisi
eux-même un code qui semblait inutilisé. La base de données officielle
réserve donc ces codes et en interdit l’utilisation, afin d’éviter des
conflits. Il arrivait donc que le fabriquant d’un écran soit affiché
comme étant “DO NOT USE”, ce qui a inquiété quelques utilisateurs de
Haiku se demandant s’ils risquaient d’endommager leur matériel.
Ces entrées de la liste sont maintenant filtrées et remplacées par le
noms des fabriquants de panneaux d’affichages concernés (lorsqu’on sait
de qui il s’agit).
Outils en ligne de commande
Haiku est fourni avec un terminal et un shell bash (par défaut,
d’autres shells peuvent également être utilisés). Les outils définis
dans la spécification POSIX sont fournis, ainsi que des compléments
permettant d’utiliser les fonctionnalités supplémentaires de Haiku.
df
La commande df affiche l’espace disque disponible sur
chaque volume de stockage actuellement monté.
Les colonnes de l’affichage ont été réorganisées, pour être plus
lisibles, et se rapprocher un peu du format spécifié par POSIX (mais pas
complètement lorsqu’on lance la commande sans options particulières :
des informations supplémentaires sont alors affichées).
filteredquery
L’outil filteredquery permet d’effectuer une requête sur
les attributs étendus du système de fichiers (permettant de requêter le
système de fichiers comme une base de données, plutôt que de naviguer de
façon hiérarchique dans les dossiers), puis de filtrer le résultat pour
ne conserver que les réponses contenues dans un sous-dossier spécifique.
En effet, les requêtes étant indépendantes de l’organisation des
dossiers, il est nécessaire de faire ce filtrage par post-traitement des
résultats (ce qui reste tout de même généralement plus rapide que de
faire l’inverse : parcourir tous les fichiers d’un dossier pour trouver
ceux correspondant à un critère particulier).
Cet outil n’a pas rçu de nouvelles fonctionnalités, mais de
nombreuses corrections et nettoyages qui le rendent véritablement
utilisable.
ping, traceroute, telnet, ftpd
Ces commandes liées à des opérations sur le réseau ont été remplacées
par les dernières versions développées par FreeBSD, permettant de
bénéficier d’une version moderne, avec plus de fonctionnalités et moins
de bugs.
La commande ping6 est supprimée, car ping
peut maintenant utiliser l’IPv6 aussi bien que l’IPv4.
pkgman
L’outil pkgman permet de télécharger et d’installer des
logiciels et des mises à jour.
Il a peu évolué, mais on peut tout de même noter l’utilisation d’un
algorithme de tri “naturel” pour l’affichage des résultats dans l’ordre
alphabétique (par exemple, llvm12 sera affiché après
llvm9).
Une fonction qui n’est toujours pas disponible dans
pkgman est le nettoyage des dépendances non utilisées. Un
script fourni dans le dépôt Git de Haiku permet de réaliser manuellement
une analyse des paquets installés sur le système pour détecter ceux qui
n’ont pas de dépendances, il faudra pour l’instant se contenter de cette
solution.
strace
L’outil strace permet d’afficher les appels systèmes
effectués par une application, pour comprendre son interfaçage avec le
noyau et investiguer certains problèmes de performances ou de mauvais
comportements.
L’interfaçage avec le noyau pour extraire ces informations étant
assez spécifique, l’implémentation de strace est faite à partir de zéro,
et ne partage pas de code avec la commande du même nom disponible par
exemple sous Linux.
strace est mis à jour régulièrement et en fonction des
besoins des développeurs de Haiku pour décoder et afficher de plus en
plus d’informations. Par exemple, elle peut maintenant afficher le
contenu des iovec
(par exemple pour les fonctions readv ou
writev), ainsi que les objets manipulés par
wait_for_object et event_queue.
Un exemple de sortie de strace (traçant l’ouverture d’un fichier et
le chargement d’une bibliothèque partagée) avant ces changements:
La commande whence permettait de trouver dans le PATH un
exécutable à partir de son nom. Elle était implémentée sous forme d’une
fonction bash dans le fichier profile par défaut. Cependant, cette
implémentation posait problème pour charger le fichier profile avec
d’autres shells, elle a donc été supprimée. La commande
which peut être utilisée à la place, puisqu’elle remplit un
rôle équivalent.
Serveurs
Les serveurs sont l’équivalent des daemons pour UNIX ou des
services sous Windows : il s’agit d’applications lancées par le
système pour rendre différents services et coordonner l’ensemble des
applications.
app_server
app_server est le serveur graphique de Haiku, équivalent de X ou de
Wayland. Il se distingue par un rendu graphique fait principalement côté
serveur (pour les applications natives), ce qui permet de l’utiliser de
façon fluide à travers une connexion réseau.
Bien que ce soit le serveur graphique, et qu’il ait reçu plusieurs
améliorations importantes, les différences sont subtiles. Elles sont
toutefois importantes pour proposer un système qui semble réactif et
confortable à utiliser.
Un premier changement est une réarchitecture du code qui traite le
rafraîchissement de l’écran. Ce rafraîchissement se fait en général en
plusieurs étapes, par exemple, si on déplace une fenêtre:
Le contenu de la fenêtre déplacée peut être directement recopié de
l’ancienne position vers la nouvelle,
La zone où se trouvait la fenêtre auparavant doit être re-remplie
avec ce qui se trouvait en dessous de la fenêtre déplacée. Cela peut
être plusieurs morceaux de fenêtres d’autres applications, qui vont
devoir chacune ré-afficher une partie de cette zone.
Le problème étant que certaines applications peuvent mettre un peu de
temps à répondre à cette demande de ré-affichage (par exemple parce
qu’elles sont occupées ailleurs, ou alors parce que la zone à redessiner
est relativement complexe).
Différentes stratégies peuvent être mises en place dans ce cas :
laisser à l’écran le contenu obsolète, ou remplir la zone en blanc en
attendant que les données deviennent disponibles, par exemple. Ou
encore, tout simplement ne rien mettre à jour du tout tant que tout
l’écran n’est pas prêt à être affiché. Il faut faire un compromis entre
la réactivité (déplacer la fenêtre tout de suite), la fluidité (éviter
les clignotements de zones blanches) et la précision (affichage
d’information cohérente et à jour).
Plusieurs modifications ont permis d’obtenir un meilleur
compromis.
Dans un autre domaine, la police de caractères par défaut “Noto Sans
Display” a été remplacée par “Noto Sans”, ce qui donne un affichage du
texte légèrement différent. La police “display” avait été choisie suite
à une mauvaise compréhension de la signification de ce mot en
typographie : il signifie que c’est une police de caractères à utiliser
pour des gros titres et autres textes courts. Il ne signifie pas que
c’est une police à utiliser sur un écran d’ordinateur. De toutes façons
la police Noto Display n’est plus maintenue par Google et a disparu des
dernières versions du jeu de polices Noto.
Toujours dans le domaine des polices de caractères, app_server sait
maintenant charger les fichiers “variable fonts”. Ces fichiers
contiennent plusieurs polices de caractères définies à partir de glyphes
de base, et d’algorithmes de transformation et de déformation (pour
rendre une police plus ou moins grasse, plus ou moins italique, …). Pour
l’instant, app_server sait charger les valeurs de ces
paramètres qui sont préconfigurées dans le fichier. Cela permet de
réduire la place utilisée par les polices de caractères sur le media
d’installation de Haiku (c’est l’un des plus gros consommateurs d’espace
disque, qui nous empêche de faire tenir une version complète de Haiku
sur un CD de démonstration par exemple).
Plus tard, il sera également possible de configurer plus finement ces
paramètres pour générer des variantes intermédiaires des polices de
caractères, ainsi que d’exploiter certaines polices qui
offrent des paramètres configurables supplémentaires.
input_server
L’input_server se charge de lire les données venant des périphériques
d’entrée (clavier et souris) et de les convertir en évènements
distribués aux applications. Il est extensible par des add-ons qui
peuvent générer ou filtrer des évènements, ce qui peut être utilisé pour
de l’accessibilité (émuler une souris à partir de touches du clavier),
de l’automatisation (envoi de commandes pré-enregistrées), du confort
d’utilisation (bloquer le touchpad d’un ordinateur portable lorsque le
clavier est en cours d’utilisation) et bien d’autres choses.
L’input_server a reçu des corrections de problèmes sur la gestion des
réglages de souris, permettant en particulier d’utiliser des réglages
différents pour plusieurs périphériques (souris, touchpad), et que
ceux-ci soient bien enregistrés.
registrar
Le serveur registrar suit les applications en cours de
fonctionnement, et leur permet de communiquer entre elles au travers de
l’envoi de messages. Il assure également le suivi de la base de données
des types MIME et des associations de types de fichiers avec les
applications correspondantes.
L’implémentation de BMessageRunner, qui permet d’envoyer
des messages périodiques (par exemple pour faire clignoter le curseur
des zones de texte à la bonne vitesse), autorise maintenant des
intervalles de répétition en dessous de 50 millisecondes. Cela permet
d’utiliser ce système pour des animations fluides de l’interface
graphique, par exemple.
D’autre part, la liste des applications et documents récemment lancés
est maintenant limitée à 100 entrées. Cela évite un fichier qui grossit
indéfiniment et finit par contenir surtout des vieilles informations
sans intérêt.
Kits
Le système Haiku fournit les mêmes APIs que BeOS. Elles couvrent les
usages basiques d’une application, et sont découpées (dans la
documentation de BeOS et de Haiku, au moins) en “kits” qui prennent
chacun en charge une partie spécifique (interface graphique, multimédia,
jeux vidéos, accès au matériel, etc).
Interface
L’interface kit est la partie de la bibliothèque standard
qui se charge des interfaces graphiques.
### BColumnListView
BColumnListView est un ajout de Haiku par rapport à BeOS. Il s’agit
d’un élément d’interface permettant de présenter une liste avec
plusieurs colonnes, de trier les lignes selon le contenu de ces
colonnes, et aussi d’avoir des items hiérarchisés avec la possibilité de
plier et déplier une partie de l’arborescence.
Cette classe remplace avantageusement BListView et surtout
BColumnListView, les classes historiques de BeOS, qui sont beaucoup plus
limitées.
Un certain nombre de type de colonnes prédéfinis sont également
disponible, ce qui facilite la construction d’interfaces présentant les
données de différentes applications avec le même formatage.
La classe BColumnListView elle-même n’a pas changé. Par contre, les
colonnes de type “taille” (oour afficher une taille en Kio, Mio, Gio, …)
et “date” utilisent la langue choisie dans les préférences système au
lieu d’un format anglais par défaut.
BTextView
BTextView est une classe permettant d’afficher une zone de texte
éditable. Elle implémente les fonctionalités de base (curseur,
sélection, retour à la ligne automatique) ainsi que quelques
possibilités de mise en forme (couleurs, polices de caractères).
BTextView peut également être utilisée pour des zones de textes non
éditables, souvent plus courtes. Cela permet de réutiliser une partie
des algorithmes de mise en page et de formatage du texte dans différents
contextes. Dans le cadre de l’utilisation du “layout system”, une vue
doit pouvoir indiquer sa taille minimale, maximale et optimale. Le
“layout system” va ensuite calculer la meilleure disposition de fenêtre
possible pour satisfaire ces contraintes.
Le cas des zones de texte est particulier, car la hauteur optimale
dépend du nombre de lignes de texte, qui lui-même peut être plus ou
moins grand si la largeur de la vue oblige à ajouter des retours à la
ligne. Le “layout kit” prend en compte ce cas particulier, mais les
algorithmes ne sont pas encore tout à fait au point et peuvent conduire
à des résultats inattendus dans certains cas. Un de ces cas particuliers
sur les zones de texte non éditables a été corrigé.
BMenu
La classe BMenu permet d’afficher un menu. Elle est utilisée de
plusieurs façons, puisqu’on trouve des menus dans des barres de menu,
dans des contrôles de type “popup”, ou encore en faisant un clic droit
sur certains éléments de l’interface.
Les menus sont également particuliers parce qu’ils peuvent d’étendre
en-dehors de la fenêtre dont ils sont originaires. Ils sont donc
implémentés sous forme de fenêtres indépendantes. Mais cela pose un
autre problème : dans Haiku, chaque fenêtre exécute son propre thread et
sa propre boucle d’évènements. Si on navigue dans un grand nombre de
menus et de sous-menus, cela peut causer quelques problèmes de
synchronisation et de performances.
Le code contient également un grand nombre de cas particuliers pour,
par exemple, aligner les raccourcis claviers et les flèches indiquant la
présence de sous-menus ente les différents items d’un menu, ou encore
détecter si un déplacement de souris à pour but de sélectionner un autre
menu (en dessous ou au-dessus de celui actif), ou bien plutôt de
naviguer vers un sous-menu.
Les nouveautés suivantes sont apparues cette année:
Correction de problèmes de race condition lors de l’ajout
d’items dans un menu pendant qu’il est affiché à l’écran. Ce problème se
manifestait par exemple dans les menus affichant la liste des réseaux
Wifi, qui sont mis à jour en temps réel.
Finalisation de l’implémentation de la navigation au clavier (avec
les flèches directionnelles) dans les menus.
Affichage des symboles graphiques UNICODE pour “backspace” (⌫) et
“delete” (⌦) si ces touches sont utilisées comme raccourcis clavier pour
un item de menu.
Utilisation d’un algorithme
de tri stable pour la fonction SortItems. Ce type
d’algorithme préserve l’ordre relatif des items qui sont égaux d’après
la fonction de comparaison. Ce n’est pas le cas de certains algorithmes
de tri classiques, notamment le quicksort. La conséquence était
que trier un menu déjà trié pouvait changer l’ordre des items. C’était
visible encore une fois sur le menu listant les réseaux Wifi, qui est
trié par puissance du signal reçu.
### BSpinner
BSpinner est un contrôle permettant de choisir une valeur numérique,
soit à l’aide de boutons +/- pour modifier la valeur par incréments,
soit en entrant directement la valeur dans une zone de texte.
Il s’agit d’une extension de Haiku par rapport à BeOS qui ne
proposait pas cette fonctionalité.
Cette classe est encore en cours de développement. Elle a reçu des
améliorations pour désactiver correctement les boutons +/- lorsque la
valeur atteint le minimum ou le maximum autorisé, et aussi une
correction sur le message de notification envoyé lors des changements de
valeurs du spinner, qui ne contenaient pas la bonne valeur.
rgb_color
La structure rgb_color permet de représenter une couleur par la
valeur de ses composantes rouge, vert, bleu (comme son nom l’indique) et
alpha (comme son nom ne l’indique pas). Elle fournit également un
certain nombre de fonctions pour mélanger des couleurs, les éclaircir ou
les assombrir.
La méthode Brightness() dans la classe rgb_color
implémentante maintenant l’algorithme perceptual brightness
documenté par Darel Rex Finley, qui donne des meilleurs résultats que
l’algorithme utilisé précédement (qui était celui de la luminosité dans
l’espace de couleurs
Y’IQ. La fonction perceptual_brightness devenue
redondante est supprimée.
Cette méthode permet en particulier de déterminer si une couleur est
“sombre” ou “claire”, et ainsi de décider si du texte affiché par-dessus
doit être blanc ou noir (comme démontré ici
par exemple).
Locale
Le locale kit se charge de tous les aspects liés à la
locatisation : traductions des applications, formatage des messages en
utilisant les règles de pluralisation de chaque langue, formatage de
dates, de nombres avec et sans unités, de pourcentages, nom des fuseaux
horaires, …
Il utilise ICU pour implémenter la plupart de ces fonctionnalités,
mais fournit une surcouche avec une API s’intégrant mieux avec les
autres kits.
La principale évolution cette année est l’implémentation de
BNumberFormat, qui permet de formater des nombres. Elle
permet de choisir une précision (nombre de décimales - pour les langues
qui utilisent un système décimal), d’afficher ou non des séparateurs de
groupes (de milliers en Français, mais par exemple en Inde la séparation
se fait traditionnellement par multiples de 10 000).
Media
Le media kit se charge de tous les aspects multimedia.
Il se compose de deux parties. D’une part, un système de gestion de
flux média temps réel, permettant de transférer des données multimédia
(son ou flux vidéo par exemple) entre différentes applications qui vont
les manipuler, le tout avec un certain contrôle du temps de traitement
ajouté par chaque opération, pour tenter de minimiser la latence tout en
évitant les vidages de tampons qui produiraient une interruption dans le
flux. D’autre part, des classes permettant d’encoder et de décoder des
fichiers média et d’en extraire des flux de données (encodées ou
décodées).
C’est surtout cette deuxième partie qui a reçu quelques évolutions.
La version de ffmpeg utilisée pour le décodage de presque tous les
formats audio et video est maintenant la dernière version ffmpeg 6.
Quelques autres problèmes (erreurs d’arrondis, gestion des tampons
partiels en fin de fichier) ont également été corrigés, ce qui permet de
faire fonctionner à nouveau le jeu BePac Deluxe
qui est extrêmement intolérant au moindre écart de comportement par
rapport à l’implémentation du Media Kit dans BeOS.
Support
Le support kit contient un ensemble de classes basiques mais
indispensables : gestion des chaînes de caractères, des tampons en
mémoire, etc. Il fournit les briques de bases utilisées par les autres
kits.
BDataIO
BDataIO est une classe abstraite avec des fonctions de lecture et
d’écriture. Plusieurs autres classes sont des instances de BDataIO, par
exemple BFile (représentant un fichier), mais aussi BMemoryIO
(permettant d’accéder à une zone mémoire).
Plusieurs autres classes acceptent BDataIO (ou sa sous-classe
BPositionIO, qui ajoute la possibilité de se déplacer à une position
donnée dans le flux) comme entrée ou comme sortie. Il est donc
facilement possible de réaliser les mêmes opérations sur un fichier, une
zone de données en mémoire, un socket réseau, ou tout autre objet
susceptible de fournir une interface similaire.
BDataIO elle-même n’a pas évolué, mais deux de ses implémentations,
BBufferedDataIO et BAdapterIO, ont été améliorées. Ces deux classes
permettent de construire un objet BDataIO à partir d’un autre, en
ajoutant un cache en mémoire pour accélérer les opérations ou encore
pour rendre compatible avec BPositionIO un objet qui ne l’est pas.
Ces classes sont en particulier utilisées par l’application
StreamRadio, qui implémente la lecture de podcasts en connectant
directement le résultat d’une requête HTTP (effectuée grace au
network kit) dans un décodeur audio (via la classe BMediaFile
du media kit). La mise en tampon permet de revenir en arrière
dans la lecture d’un épisode, de télécharger en avance les données qui
vont être lues, et d’éviter de conserver inutilement en mémoire les
données qui sont déjà lues par l’application.
Bibliothèques C
Les “kits” mentionnés ci-dessus sont l’API en C++ utilisée par les
applications Haiku.
Il existe aussi des APIs en C, en grande partie implémentant la
bibliothèque C standard et les fonctions décrites dans la spécification
POSIX.
Libroot
Libroot implémente la bibliothèque standard C. Elle regroupe entre
autres la libc, la libm, et la libpthread, qui sont parfois implémentées
comme 3 bibliothèques différentes pour d’autres systèmes. Les évolutions
consistent à compléter l’implémentation de la spécification POSIX, et à
suivre les évolutions de cette dernière ainsi que des nouvelles versions
du langage C. On trouve également des corrections de bugs découverts en
essayant de faire fonctionner de plus en plus d’applications sur Haiku,
ce qui permet de mettre en évidence des différences de comportement avec
d’autres systèmes.
Ajout de getentropy pour initialiser les générateurs de
nombres aléatoires
Correction de problèmes de locks au niveau de l’allocateur mémoire
lors d’un fork
Plusieurs corrections sur l’implémentation de locale_t,
remplacement de code écrit pour Haiku ou provenant de FreeBSD par une
implémentation simplifiée mais suffisante, provenant de la bibliothèque
C musl.
Ajout de static_assert en C11
Correction d’un crash lors de l’utilisation de certaines fonctions
XSI
Ajout de stpncpy
La fonction open utilisée sur un lien symbolique
pointant vers un fichier non existant peut maintenant créer le fichier
cible.
Il est possible d’utiliser mmap sur un fichier plus
grand que la mémoire disponible sans avoir besoin de spécifier le flag
MAP_NORESERVE
Utiliser rename pour renommer un fichier vers lui-même
ne retourne plus d’erreur (conformément à la spécification POSIX).
Ajout de pthread_sigqueue
Libnetwork
La libnetwork implémente les APIs nécessaire pour se connecter au
réseau (sockets, résolution DNS, …). Elle est séparée de la bibliothèque
C pour des raisons historiques : l’implémentation de TCP/IP pour BeOS
avait été réalisée entièrement en espace utilisateur (le noyau n’offrant
qu’une interface pour envoyer et recevoir des paquets ethernet sur la
carte réseau). Cela a posé des problèmes de compatibilité avec d’autres
systèmes, et des problèmes de performance. Haiku est donc compatible
avec la version “BONE” de BeOS, qui implémente la pile réseau dans le
noyau.
Mise à jour du résolveur DNS à partir du code de NetBSD 9.3.
Précédement le code utilisé était celui du projet netresolv de NetBSD,
mais ce projet n’a pas connu de nouvelles publications et le code est à
nouveau maintenu directement dans NetBSD sans publication séparée.
Correction d’un crash lors de l’utilisation de multicast IPv4
LibBSD
La libbsd implémente plusieurs extensions fournies par la libc de
certains systèmes BSD. Elle est séparée de la bibliothèque C principale
pour limiter les problèmes de compatibilité: certaines applications
préfèrent fournir leur propre version de ces fonctions, ou d’autres
fonctions avec le même nom mais un comportement différent. Elles peuvent
alors s’isoler en n’utilisant pas la libbsd pour éviter toute
interférence.
De façon similaire à la libbsd, la libgnu fournit des fonctions qui
sont disponibles dans la glibc (la bibliothèque C du projet GNU) mais ne
font pas partie d’un standard (C ou POSIX).
Ajout de sched_getcpu pour savoir sur quel coeur de CPU
le thread appelant est en train de s’exécuter.
Ajout de pthread_timedjoin_np, pour attendre la fin de
l’exécution d’un thread (comme pthread_join mais avec un
timeout.
Noyau
Le noyau de Haiku est similaire à celui de BeOS: il s’agit d’un noyau
monolithique, avec du multitâche préemptif et protection mémoire. Rien
de très inhabituel. Il est développé en C++ (comme le reste du système),
ce qui permet de rendre le code plus lisible que du C tout en conservant
des bonnes performances pour ce code bas niveau.
Un point intéressant, le noyau offre une API et une ABI stable pour
les pilotes, ce qui fait qu’il est en théorie possible de développer un
pilote hors du projet Haiku et de le faire fonctionner avec plusieurs
versions du noyau. En pratique, peu de personnes se lancent dans ce
genre de chose, il est plus simple d’intégrer les pilotes dans le dépôt
de sources de Haiku pour l’instant.
Pilotes
Commençons justement par regarder les nouveautés du côté des pilotes
matériels. Il s’agit pour tout système d’exploitation d’un point de
difficulté, indispensable pour fonctionner sur une large variété de
systèmes.
Processeurs
En principe, un processeur est un matériel assez bien standardisé,
qui implémente un jeu d’instruciton bien défini et ne devrait pas
nécessiter de pilote spécifique. Cependant, le matériel moderne de plus
en plus complexe, offrant de plus en plus de fonctionnalités dans une
seule puce électronique, fait qu’il faut tout de même prendre en compte
quelques cas particuliers.
Ajout de nouvelles générations de machines Intel dans le driver
PCH thermal (récupération de la température du CPU au travers
du platform control hub).
Implémentation du contournement pour la faille Zenbleed
dans les processeurs AMD.
La mise à jour du microcode pour les processeurs Intel n’est pas
faite si le CPU est déjà à jour (pour gagner un peu de temps au
redémarrage du système).
Réseau
Les cartes réseau restent aujourd’hui le composant le moins bien
standardisé sur les ordinateurs. Il n’existe pas d’interface
standardisée, et chaque fabricant propose sa propre façon de faire.
Aujourd’hui, la plupart des autres périphériques suivent des
spécifications (xHCI pour les contrôleurs USB3, AHCI pour le SATA, Intel
HDA pour les cartes son, …) ou bien il ne reste que peu de concepteurs
de composants (par exemple pour les cartes graphiques où on ne trouve
que Intel, AMD et NVidia).
Écrire des pilotes pour toutes ces cartes réseau demanderait beaucoup
trop de travail. C’est pourquoi, depuis 2007, Haiku s’est doté d’une
couche de compatibilité avec FreeBSD, permettant de réutiliser les
pilotes écrits pour ce dernier (une approche également
utilisée par le système d’exploitation temps réel RTEMS).
Cependant, les développeurs de FreeBSD font face au même problème, et
ont décidé d’adopter la même solution : une couche de compatibilité
permettant d’utiliser les pilotes de Linux. Cela pose deux problèmes
pour Haiku : il ne semble pas souhaitable d’empiler les couches de
compatibilité, et il ne semble pas raisonable d’écrire une couche de
compatibilité avec Linux, dont les APIs internes évoluent beaucoup trop
vite, ce qui nécessiterait une réécriture permanente de la couche de
compatibilité pour suivre le rythme.
Finalement, la solution retenue pour Haiku est d’utiliser les pilotes
activement développés par OpenBSD et en particulier par Stefan Sperling.
La couche de compatibilité avec FreeBSD est également maintenue, et
Haiku bénéficie donc des pilotes développés pour ces deux systèmes, en
plus des siens propres.
Par exemple, les pilotes wifi iaxwifi200 et
idualwifi7260 proviennent de OpenBSD, tandis que
ipro1000 et intel22x sont ceux de FreeBSD 14.
Les couches de compatibilité reçoivent régulièrement des corrections et
améliorations.
En dehors des cartes réseaux physiques, Haiku dispose d’un nouveau
pilote tun permettant de créeer des tunnels réseau. Celui-ci a
été développé dans le cadre du Google Summer of Code 2023, et permet par
exemple d’utiliser un client OpenVPN sous Haiku.
Enfin, une évolution qui concerne tous les pilotes réseaux : le
nombre de paquets et d’octets reçus et envoyés pour une interface réseau
est maintenant décompté par la pile réseau, plutôt que par chaque pilote
d’interface réseau. Les pilotes doivent toujours tenir à jour les
compteurs d’erreurs. Ce changement permet de regrouper le code de
comptage à un seul endroit, et d’éviter des comportements différents
entre pilotes. En particulier, le comptage des paquets pour l’interface
localhost n’était pas correct.
Périphériques d’entrée
Haiku permet d’utiliser les claviers et souris connectés en USB et en
PS/2 (encore utilisé dans certains ordinateurs portables, mais il semble
en voie de disparition). Les pilotes pour les touchpads et claviers i2c
sont encore en cours de développement, et le Bluetooth arrivera un peu
plus tard.
Commençons par le pilote PS/2. Il reçoit relativement peu
d’évolutions, cependant, les ordinateurs portables récents
n’implémentent plus forcément complètement le matériel nécessaire
(l’interface PS/2 étant simulée par l’embedded
controller). Le pilote PS/2 de Haiku qui essaie de détecter de
nombreux cas de configuration possibles est parfois un peu dérouté par
ces écarts. Cela pouvait provoquer un blocage empêchant d’utiliser le
clavier pendant plusieurs secondes après le lancement de la machine, le
temps que le pilote finisse d’énumérer les périphériques PS/2. Le
problème a été corrigé en réduisant le temps d’attente avant de décider
qu’il n’y a aucun périphérique connecté.
Du côté de l’USB, une première correction concerne la prise en compte
de l’attribut “minimum” dans les rapports HID. Le protocole HID permet
de définir toutes sortes de périphériques (claviers, souris, mais aussi
clubs de
golf, simulateurs de tanks, …). Les périphériques USB HID envoient à
l’ordinateur une description des contrôles dont ils disposent (groups de
boutons, axes, etc). Pour les boutons et touches de clavier, la valeur
“minimum” indique le code du premier bouton dans le groupe, les autres
étants déduits en incrémentant la valeur pour chaque bouton présent. Ce
cas n’était pas bien pris en compte par le pilote de clavier, ce qui
provoquait l’envoi de mauvais codes aux applications pour les claviers
concernés.
D’autre part, et de façon plus spécifique, le pilote de souris
bénéficie maintenant d’un quirk, c’est-à-dire d’une procédure
de contournement d’un problème, pour les souris et trackballs de la
marque Elecom. Ces dernières utilisent en effet toutes le même
descripteur HID, indiquant la présence de 5 boutons, alors que certains
modèles ont en fait un 6ème bouton non déclaré. Le descripteur est
corrigé à la volée pour les périphériques concernés.
Son et image
Haiku dispose d’un pilote pour les périphériques USB Audio. Ce pilote
est en développement depuis très longtemps (cela remonte avant
l’apparition de l’USB 2.0), mais il n’avait jamais pu être finalisé en
raison du manque de prise en charge des transferts
isochrones. Ces problèmes ont enfin été corrigés, mais le pilote
nécessite encore des travaux pour le rendre compatible avec plus de
matériel (en particulier les périphériques implémentant la version 2.0
de la spécification USB Audio) et probablement également quelques
corrections dans le serveur média pour le préparer à l’apparition et la
disparition de cartes son pendant que le système est en train de tourner
(actuellement, cela nécessitera un redémarrage du serveur).
Du côté des cartes son PCI, pas de grande nouveauté, mais un gros
nettoyage dans le cadre de travaux pour supprimer tous les
avertissements du compilateur. Ce travail se fait petit à petit, dossier
par dossier dans le code de Haiku. L’analyse du dossier contenant les
pilotes de cartes son a révélé l’existence de 3 pilotes ciblant le même
matériel, ainsi que de plusieurs fichiers qui avaient été dupliqués dans
plusieurs pilotes (développés avant leur rassemblement dans le dépôt de
sources de Haiku à partir du mème exemple de code), puis qui avaient
divergés au cours du développement de chaque pilote. Ce code a été
réunifié dans une version partagée qui inclut toutes les corrections et
améliorations de chaque version.
Du côté des cartes graphiques, des travaux sont en cours pour pouvoir
piloter correctement les cartes graphiques Intel de 12ème génération. Le
pilote existant fonctionne déjà dans certains cas, mais se repose
beaucoup sur le travail fait par le firmware (BIOS ou EFI) pour
initialiser l’affichage. Il est donc par exemple impossible d’utiliser
un écran qui n’a pas été configuré au démarrage de la machine (passer
d’une sortie HDMI à l’écran d’un PC portable ou inversement, par
exemple).
Machines virtuelles
Haiku est utilisé dans des machines virtuelles pour diverses raisons
: à des fins de test par les développeurs, pour l’infrastructure de
compilation des paquets, ou encore par des utilisateurs qui veulent le
tester sans l’installer sur une machine physique dédiée.
Des pilotes spécifiques et quelques adaptations sont nécessaires pour
un bon fonctionnement sur ces machines. En particulier, des pilotes sont
nécessaires pour certains périphériques virtio, qui permettent
aux machines virtuelles d’émuler un matériel simplifié, ne correspondant
pas à un matériel réel existant. Ceci permet de meilleures
performances.
Le pilote virtio de Haiku a été mis à jour pour implémenter la
version 1.0 de la spécification. Cela a permis de corriger des problèmes
dans le pilote virtio_block (support de stockage
virtualisé).
Un nouveau pilote virtio_gpu permet l’affichage de
l’écran sans avoir à passer par un pilote pour une carte graphique, ni
par les pilotes VESA ou framebuffer EFI qui montrent assez vite leurs
limitations (choix de résolutions d’écran limité, par exemple). Plus
tard, ce pilote pourrait permettre également d’expérimenter avec la
virtualisation de OpenGL, et donc d’expérimenter avec l’accélération du
rendu 3D sans avoir à développer un pilote graphique capable de le
faire.
Ces pilotes virtualisés facilitent également le travail de portage de
Haiku vers de nouvelles architectures : il est possible de lancer Haiku
dans QEMU avec n’importe quel processeur, et un ensemble de
périphériques virtio pour lesquels les pilotes ont pu d’abord être
testés sur une autre architecture déjà fonctionnelle.
Autres
La bibliothèque ACPICA a été mise à jour avec la dernière version
20230628, et les changements nécessaires pour son fonctionnement dans
Haiku ont été intégrées en amont,
ce qui facilitera les prochaines mises à jour. ACPICA est développée par
Intel et permet d’implémenter la spécification ACPI, pour la gestion
d’énergie, l’énumération du matériel présent sur une machine, et
diverses fonctionnalittés liées (détection de la fermeture d’un
ordinateur portable, récupération du nveau de charge des batteries, par
exemple).
Le pilote poke, qui permet aux applications de manipuler
directement la mémoire physique sans l’aide d’un pilote spécifique, a
été remis à jour et finalisé. Il est utile principalement pour
expérimenter avec le matériel avant de développer un pilote
spécifique.
La pile Bluetooth a reçu un premier coup de nettoyage. Pas de grosses
évolutions pour l’instant, seules les couches les plus basses sont
implémentées, on pourra au mieux énumérer les périphériques Bluetooth
présents à proximité. Le développement des fonctionnalités suivantes
attendra au moins la publication de la version Beta 5.
Systèmes de fichiers
Haiku implémente plusieurs systèmes de fichiers. Celui utilisé pour
le système est BFS, hérité de BeOS et qui fournit quelques fonctions
indispensables à Haiku (comme les requêtes qui permettent d’indexer des
attributs étendus de fichiers dans une base de données). Mais de
nombreux autres systèmes de fichiers peuvent être lus, et pour certains,
écrits. Cela permet de facilement partager des fichiers avec d’autres
systèmes d’exploitation.
Le système de fichier UFS2 est maintenant complètement implémenté (en
lecture seule), inter-opérable avec FreeBSD, et sera disponible dans
l’installation de base pour les prochaines versions de Haiku.
Du côté de Linux, l’interopérabilité est possible en lecture et en
écriture avec les systèmes de fichiers ext2, 3, et 4 (tous les 3
implémentés dans un seul pilote qui sait les reconnaître et les
différencier). Cette implémentation a reçu quelques corrections de bugs
ainsi qu’une implémentation de F_SETFL.
Enfin du côté de Windows, la prise en charge de NTFS avait déjà été
mise à jour et grandement améliorée (en réutilisant les sources de
NTFS-3g). Cette année c’est le tour des systèmes de fichiers FAT. Le
pilte utilisé jusqu’à maintenant avait été publié par Be il y a très
longtemps. Il avait été mis à jour pour Haiku mais comportait de
nombreux problèmes : mauvaise gestion des dates de modification des
fichiers, interopérabilité avec d’autres implémentations, voire crash du
système lors de tentative de lecture de partitions corrompues. Ce code a
été entièrement remplacé par un pilote utilisant l’implémentation du FAT
de FreeBSD.
Enfin, le système de fichier ramfs (pour stocker des fichiers dans la
RAM de l’ordinateur de façon non persistente) a reçu des corrections sur
la fonction preallocate. Cela corrige en particulier des
fuites de mémoire dans les navigateurs web basés sur QWebEngine, qui
utilisent ce système de fichiers pour partager de la mémoire entre
plusieurs processus.
Un changement un peu plus global, et pas lié à un système de fichier
spécifique, est la réunification du code pour parser les
requêtes. Il s’agit d’une méthode pour rechercher des fichiers
à partir de leurs attributs étendus (xattrs) qui sont indexés à la façon
d’une base de données. Au départ, cette fonctionnalité était propre au
système de fichier BFS, mais elle a été implémentée également pour ramfs
et packagefs (système de fichier permettant d’accéder au contenu des
paquets logiciels sans les décomprésser). Lors du développement de ces
deux nouveaux systèmes de fichiers, le code permettant de convertir une
chaîne de caractères exprimant une requête en opération exécutable avait
été extrait du pilote BFS pour en faire un module générique. Mais le
pilote BFS n’avait pas encore été mis à jour pour utiliser ce module.
C’est désormais chose faite, ce qui assure que le comportement entre les
3 systèmes de fichiers est le même, et que les corrections de bugs
bénéficieront à tous les trois.
Pour terminer sur les systèmes de fichiers, l’outil
fs_shell, qui permet d’exécuter le code d’un système de
fichier en espace utilisateur, a rçu deux nouvelles commandes :
truncate et touch. Cet outil permet de tester
les systèmes de fichiers en cours de développement dans un environnement
plus confortable et mieux contrôlé, et il est aussi utilisé lors de la
compilation de Haiku pour générer les images disques.
Réseau
La pile réseau proprement dite a principalement évolué avec de la
mise en commun de code. Par exemple, l’implémentation de l’ioctl FIONBIO
(non standardisé, mais largement implémenté) pour passer un descripteur
de fichier en mode non bloquant a été réécrite pour partager du code
avec le flag O_NONBLOCK configurable par fcntl
et F_SETFL. Également, le flag MSG_PEEK qui permet de
lire des données d’un socket sans les retirer de son buffer de
réception, est maintenant implépenté directement par la pile réseau au
lieu d’avoir une version spécifique à chaque type de socket.
Sockets UNIX
Les sockets de la famille AF_UNIX sont utilisés pour les
communications locales entre applications sur une même machine. Ils sont
en particulier utilisés par WebKit et de nombreux autres moteurs de
rendu web, mais assez peu par les applications natives pour Haiku, qui
disposent d’autres méthodes de communications (en particullier les BMessage
et les ports).
L’implémentation des sockets UNIX est maintenant complète et
suffisante pour faire fonctionner toutes les applications qui en ont
l’utilité.
### TCP
La pile TCP de Haiku est devenue au fil du temps un goulot
d’étranglement des performances. D’une part parce que toutes les autres
parties du système se sont améliorées, et d’autre part parce que les
interfaces réseaux sont de plus en plus rapides et de plus en plus
sollicitées.
Le travail sur la pile TCP cette année a commencé par la remise en
route de l’outil tcp_shell, qui permet de tester
l’implémentation de TCP en espace utilisateur et en isolation du reste
du système. Cet outil avait été utilisé au tout début du développement
de la pile TCP, mais n’avait pas été tenu à jour depuis. Il permet
maintenant de tester la pile TCP communiquant avec elle-même, et aussi
d’injecter des paquets à partir de fichier pcap. Pour l’instant, la
fonction permettant de communiquer avec l’extérieur n’a pas été remise
en place.
Cet outil a permis d’identifier et d’analyser certains des problèmes
rencontrés.
Le premier problème était un envoi d’acquittements TCP en double. À
première vue, cela ne devrait pas poser de gros problèmes, il y a
seulement un peu de redondance. Mais, en pratique, une implémentation de
TCP qui reçoit des acquittements en double suppose qu’il y a eu un
problème de congestion réseau lors de l’envoi de données dans l’autre
sens. Les algorithmes de contrôle de la congestion se mettent en jeu, et
le traffic ralentit pour éviter une congestion qui n’existe pas. Par
exemple, la taille de la fenêtre de transmission TCP (le nombre maximum
d’octets qui peuvent être envoyés sans attendre d’acquittement) peut
être réduite.
Et, malheureusement, cela déclenche un autre problème : la taille de
cette fenêtre peut atteindre 0 octets, et dans ce cas, HAiku ne
s’autorisqit plus à émettre aucun paquet. Cela pouvait se produire au
même moment dans les 2 directions sur une connexion TCP, ce qui fait
qu’aucune des deux machines connectées ne s’autorise à envoyer de
données à l’autre. Ce problème a été corrigé, les transmissions peuvent
maintenant continuer à débit réduit, puis reprendre une vitesse optimale
petit à petit.
Après ces corrections, une mesure des performances de TCP dans un
environnement de test montre que la pile TCP est capable de traiter
jusqu’à 5.4Gbit/s de traffic, alors que le débit plafonnait à 45Mbit/s
auparavant. C’est donc un centuplage des performances.
Autres
Plusieurs autres évolutions diverses dans le noyau:
L’implémentation de kqueue, ajoutée
l’année dernière, a reçu plusieurs corrections et améliorations. Elle
couvre déjà plusieurs usages et permet l’utilisation de plus de
logiciels portés depuis d’autres systèmes, mais les cas d’utilisation
les plus avancés ne sont pas encore tout à fait fonctionnels.
Pour rappel, kqueue est une fonction des systèmes BSD permettant à un
thread utilisateur de se mettre en attente de plusieurs types
d’évènements et de resources du noyau. L’usage est similaire à celui de
epoll
sous Linux mais l’API est différente.
La classe ConditionVariable, utilisée pour la synchronisation entre
threads et interruptions dans le noyau, a reçu plusieurs mises à jour.
Un article sur le site de Haiku détaille l’utilisation
et le fonctionnement de cette classe.
La boucle principale du débugger noyau (KDL), qui prend la main sur
tous les processeurs en cas de crash du système ou sur demande de
l’utilisateur pour investiguer des problèmes, inclus maintenant une
instruction PAUSE.
Cela permet d’informer le CPU qu’il n’est pas nécessaire d’exécuter
cette boucle à la vitesse maximale, évitant de faire surchauffer la
machine sans raison. Cette boucle est principalement en attente
d’instructions de l’utilisateur, via un clavier ou un port série.
Du refactoring sur les parties du code qui sont spécifiques à chaque
architecture : arch_debug_get_caller est maintenant
implémenté via un builtin gcc plutôt que du code assembleur à écrire à
la main pour chaque machine.
arch_debug_call_with_fault_handler appelait une fonction
avec un mauvais alignement de pile sur x8_64, pouvant conduire à un
crash si la fonction appelée utilisait des instructions SSE par exemple.
Correction également d’un problème qui pouvait causer la perte d’une
interruption inter-CPU (permetant à un coeur de processeur d’interrompre
l’exécution de code en cours sur un autre coeur) dans certains cas.
Une modification sur la gestion des descripteurs de fichiers: la
structure interne des descripteurs de fichiers était pourvue d’un champ
indiquant le type (fichier, socket, pipe, …). Ce champ et tout le code
qui en dépendait ont été supprimés. Ceci permet à des add-ons du kernel
de déclarer leurs propres types de fichiers sans avoir à modifier le
noyau. Cela pourrait par exemple être utile pour développer une couche
de compatibilité avec Linux, qui fait un usage généreux des descripteurs
de fichiers de tous types (eventfd, signalfd, timerfd, …).
Réécriture du code de debug activé par l’option
B_DEBUG_SPINLOCK_CONTENTION qui permet d’investiguer les
problèmes de performances liés à l’utilisation de spinlocks (attente
active sur une interruption matérielle).
Un petit changement d’algorithme sur l’allocateur de pages du noyau.
Cet allocateur alloue des pages mémoires par blocs multiples de 4Ko. Les
pages libérées étaient réinsérées une par une dans une liste chaînée.
Cela conduit à insérer les pages dans l’ordre inverse de leurs addresses
(la dernière page d’une zone mémoire se retrouve au début de la liste).
Lors des prochaines allocations, cette page se retrouve donc allouées en
premier, puis celle qui se trouve juste avant, et ainsi de suite. La
zone mémoire construite par toutes ses pages est donc considérée comme
discontinue. En inversant l’ordre d’insertion des pages dans la liste,
on préserve les pages dans un ordre globalement croissant d’adresse
mémoire, et on augmente les chances qu’une allocation de plusieurs pages
se trouve avec des pages contiguës et dans le bon ordre. Cela est utile
en particulier pour les allocations qui vont être utilisées pour des
transferts DMA: il sera possible de programmer un seul gros transfert
DMA au lieu de plusieurs petits.
L’état de la FPU
du processeur n’était pas complètement sauvegardé lors d’un changement
de contexte. Certains drapeaux de configuration pouvaient donc rester
positionnés avec les valeurs configurées par un thread, pendant
l’exécution d’un autre. Au mieux cela donnait des résultats inattendus,
au pire, un crash (par exemple si le FPU est configuré pour lever une
exception matérielle, dans un thread qui ne s’y attend pas). Le nouveau
code de sauvegarde utilise des instructions dédiées qui sauvegarder d’un
coup tout l’état du FPU, ce qui fait qu’en plus de fonctionner
correctement, il est plus rapide que ce qui était fait précédemment.
Une évolution sur les sémaphores:
la fonction release_sem_etc permet de donner une valeur
négative au paramètre “count”. Dans ce cas, le thread qui était en
attente d’un acquire_sem sera réveillé, mais la fonction
acquire_sem retournera une erreur indiquant que le
sémaphore n’a pas pu être obtenu. Cela permet de simplifier un peu le
code de certaines utilisations classiques des sémaphores.
Une correction de bug sur le code traitant les “doubles fautes”. Le
fonctionnement d’un système d’exploitation est en partie basé sur
l’interception des “fautes”, par exemple, un programme qui essaie
d’accéder à de la mémoire qui a été évacuée dans la swap. Cette mémoire
n’est pas immédiatement accessible, le programme est donc interrompu, le
noyau prend la main, va récupérer cette mémoire, puis rend la main au
programme qui n’y voit que du feu et continue son exécution comme si de
rien n’était. Les fautes peuvent également se produire dans le cas où un
programme essaie d’accéder à une zone mémoire non allouée, on aura alors
une erreur de segmentation.
Tout ça est très bien, mais que se passe-t-il si le code qui traite
ces problèmes déclenche lui-même une faute? C’est prévu : il existe un
deuxième morceau de code qui va intercepter ces problèmes et tout
arrêter pour lancer le debugger noyau, et permettre à un humain
d’examiner la situation.
Oui, mais que se passe-t-il si ce code déclenche lui même une faute?
C’est ce qu’on appelle une triple faute, dans ce cas, la solution de
dernier recours est d’immédiatement redémarrer la machine.
Des utilisateurs se sont plaints de redémarrages intempestifs, et une
étude attentive du code traitant les doubles fautes a révélé un problème
qui déclenchait systématiquement une triple faute (difficile à analyser,
car on n’a pas de journaux ou de moyen d’investiguer le problème).
Espérons que l’accès au debugger noyau lors des doubles fautes permettra
désormais de comprendre d’où elles proviennent.
Tout autre sujet, le noyau dispose maintenant d’APIs pour configurer
l’affinité des threads, par exemple pour interdire à un thread de
s’exécuter sur certains coeurs de processeurs. Cela peut être utile sur
des machines avec des processeurs hétérogènes (par exemple ARM
BIG.Little), ou encore si le développeur d’une application pense pouvoir
faire mieux que l’ordonnanceur par défaut pour répartir ses threads sur
différents coeurs.
Pour terminer sur les évolutions dans le noyau, la calibration du TSC
peut maintenant être faite à partir d’informations obtenues via
l’instruction CPUID. Le TSC est un compteur de cycles qui s’incrémente à
une vitesse plus ou moins liée à la fréquence du processeur. Il est
utile de connaître la durée en microsecondes ou nanosecondes d’un “tick”
du TSC pour différents usages. Historiquement, cette durée est calculée
en utilisant le Programmable
Interval Timer, un composant présent dans les ordinateurs
compatibles PC depuis le tout début. Ce composant n’a plus beaucoup
d’autres utilités aujourd’hui, et certains chipsets ne l’implémentent
plus, ou pas correctement. Ou encore, dans les machines virtuelles,
l’émulation du processeur (virtualisé) n’est pas forcément exécutée de
façon synchrone avec celle du timer, rendant cette mesure peu fiable.
L’instruction CPUID permet de récupérer l’information de façon plus
directe. Un changement
similaire dans FreeBSD donne un bon aperçu de la situation.
Portages ARM, RISC-V et
autres
Historiquement, Haiku est développé en premier pour les machines x86
32-bit. Une version 64 bit est apparue en 2012. D’autres versions pour
les processeurs PowerPC, ARM (32 et 64 bits), RISC-V, Sparc ou encore
Motorola 68000 sont dans des états d’avancement divers. Les versions ARM
et RISC-V sont actuellement celles qui reçoivent le plus d’attention des
développeurs. Il existe un fork de Haiku qui est entièrement fonctionnel
sur certaines machines RISC-V, les changements sont intégrés petit à
petit avec pas mal de nettoyage à faire.
Une des problématiques pour ces nouvelles architectures est la
procédure de “bootstrap”. Pour gagner du temps et simplifier la
procédure, la compilation de Haiku se base sur un certain nombre de
dépendances qui sont pré-compilées depuis une machine fonctionnant sous
Haiku. Cela permet de ne pas avoir à compiler des douzaines de
bibliothèques tierces, avec un environnement de compilation peu contrôlé
(on peut compiler Haiku depuis un système Haiku, depuis un grand nombre
de distributions Linux, depuis Mac OS, depuis un BSD, ou même depuis
Windows avec WSL).
Cependant, lors du développement de Haiku pour une nouvelle
architecture, ces paquets précompilés ne sont bien entendu pas encore
disponibles. Il est donc nécessaire d’utiliser une procédure de
“bootstrap”, qui va se baser sur un autre système et compiler ce qui est
nécessaire en compilation croisée, pour aboutir à un système Haiku
réduit au minimum de fonctionnalités, juste de quoi pouvoir lancer
l’outil haikuports, qui va lui-même ensuite compiler tous les autres
paquets.
Ce processus est assez complexe, et a été laissé un peu à l’abandon.
Il a été récemment remis en route, avec des corrections de bugs dans
l’outil haikuporter, des mises à jour dans les paquets cross-compilés
(par exemple pour passer de Python 2 à Python 3), et divers autres
petits problèmes. Il est maintenant à nouveau possible de construire une
image disque de bootstrap au moins pour la version PowerPC.
Le portage RISC-V a reçu une mise à jour vers gcc 13 (c’était déjà le
cas pour les autres architectures) et a pu être utilisé pour compiler
LLVM puis Mesa (l’intégration dans la ferme de compilation de Haikuports
n’est pas encore en place, donc ces compilations doivent être faites par
un développeur qui lance les commandes haikuports nécessaire et patiente
longtemps pendant la compilation de ces gros projets).
Les versions 68000 et PowerPC ont été un peu dépoussiérées, mais il
manque toujours un certain nombre de pilotes matériels de base pour
pouvoir les utiliser sur de vraies machines et même dans une certaine
mesure dans QEMU (ce dernier permettant d’émuler une machine utilisant
de nombreux périphériques VirtIO, ce qui pourrait simplifier un peu les
choses).
La bibliothèque libroot a reçu plusieurs mises à jour dans les
parties qui nécessitent du code spécifique à chaque architecture, pour
ajouter en particulier le RISC-V, et au passage plusieurs autres
familles de processeurs.
Une partie de Haiku qui nécessite de grosses évolutions est la
gestion des bus PCI. Le pilote existant supposait la présence d’un BIOS
pour effectuer la découverte du bus, ou pouvait également utiliser des
tables ACPI, mais d’une façon un peu limitée, qui repose tout de même
sur le BIOS ou un quelconque firmware pour assigner des adresses valides
à toutes les cartes PCI. Un problème identifié depuis longtemps
puisqu’il s’agit du bug
numéro 3 dans l’outil de suivi de bugs de Haiku. Ce bug fêtera ses
20 ans en mars prochain, espérons qu’il soit corrigé d’ici là. Les
choses avancent, puisque le pilote PCI va maintenant s’attacher
correctement aux noeuds ACPI correspondants dans le device tree, ce qui
permet ensuite d’interroger ACPI pour découvrir les plages d’adresses
mémoires disponibles pour l’allocation d’une adresse à chaque carte PCI
connectée. Du côté des nouveaux ports de Haiku, cela va également
permettre d’avoir plusieurs bus PCI “racine” indépendants. Et ces
développements pourraient également Être utiles pour une prise en charge
complète de Thunderbolt et USB 4.
Un autre pilote qui sera utile pour les versions ARM et RISC-V est le
pilote SDHCI,
qui permet de s’interfacer avec les lecteurs de cartes SD ainsi que les
modules eMMC. Initialement destiné uniquement aux modules connectés sur
un bus PCI, le pilote a été conçu pour être facilement extensible, et
permet maintenant d’utiliser également les contrôleurs SDHCI exposés via
ACPI. Cependant, le pilote a encore quelques problèmes de fiabilité, et
il manque une implémentation des commandes nécessaiers pour les modules
eMMC, qui partagent le même protocole de communication que les cartes
SD, mais utilisent un jeu de commandes différent (il y a une petite guerre
de standards, le format SD s’est imposé pour les cartes amovibles,
mais MMC qui n’a pas de royalties a pu prendre le marché des modules
soudés sur les cartes mères, où l’interopérabilité avec le matériel
existant ne pose pas autant problèmes).
Le portage sur ARM64 avance petit à petit, il parvient à démarrer une
partie de l’espace utilisateur et a reçu dernièrement des corrections
sur le code permettant les changements de contexte entre différents
threads. L’affichage du bureau complet pour la première fois sur une
machine ARM64 ne devrait donc plus être très loin.
Bootloader
Le démarrage de Haiku est pris en charge par un bootloader spécifique
nommé haiku_loader. Contrairement au noyau Linux, qui peut s’initialiser
tout seul quasiment dès le démarrage du matériel, le noyau de Haiku a
besoin que son bootloader prépare une grande partie de l’environnement
(activation de la mémoire virtuelle, initialisation de l’affichage et
mise en place du “splash screen”, par exemple). Le bootloader prend en
charge toutes ces tâches et permet en plus de configurer des options de
démarrage via un menu en mode texte, de démarrer via le réseau,
d’utiliser un snapshot plus ancien du système si une mise à jour s’est
mal passée.
Le bootloader a peu évolué cette année, le changement principal étant
la suppression de logs de warning lors du chagement de fichiers ELF,
pour les sections non traitées PT_EH_FRAME (généré par les versions
modernes de gcc) ainsi que d’autres sections spécifiques aux processeurs
RISC-V qui ne nécessitent pas de traitement spécifique dans ce cas.
Amélioration de performances
Beaucoup de travail a été fait sur l’amélioration des performances.
C’est un sujet qui a été un peu laissé de côté au début du développement
de Haiku. Le premier but était de faire fonctionner les choses, avant de
les rendre plus rapides. Maintenant que les développements sont assez
avancés, il est temps de commencer à étudier ce problème et à essayer de
se rapprocher des perfomances d’autres systèmes.
Implémentation
des IO vectorisées sur les périphériques de type bloc
Lorsqu’on veut lire ou écrire sur un disque, il faut envoyer une
commande pour accéder à des secteurs consécutifs. Dans le cas normal,
c’est le cache du système de fichiers qui se charge de regrouper les
différents accès et de les ordonnancer de façon optimale.
Mais il y a un cas particulier pour les accès direct au disque. Par
exemple, si on ouvre le disque directement (via son device dans
/dev/disk/) ou encore lorsqu’un système de fichier veut écrire son
journal (qui ne passe pas par le cache). Les écritures dans le journal
sont faite avec des accès vectorisées (via readv ou
writev) qui contiennent chacun une liste d’endroits où lire
ou écrire des données. Ces accès étaient implémntés sous forme d’une
boucle appelant plusieurs fois read ou write.
Maintenant, la liste est directement transmise au pilote de disque qui
peut ainsi mieux traiter ces accès.
Réparation du profiler
Haiku dispose d’un outil de profiling, mais celui-ci ne fonctionnait
plus et retournait des données incohérentes. Plusieurs problèmes ont été
corrigés pour faciliter les mesures de performances et vérifier que les
optimisations rendent réellement les choses plus rapides.
Réduction des
verrouillages du device manager
Le problème initial qui a conduit à ces améliorations était la
lenteur du lancement de nouveaux processus. Un goulet d’étranglement qui
a été identifié est le verrouillage du device_manager pour accéder au
périphérique /dev/random pour initialiser le stack protector (qui a
besoin d’écrire des valeurs aléatoires sur la pile). Toutes les
ouvertures de fichiers dans /dev nécessitent d’acquérir un verrou qui
empêche l’exécution en parallèle avec de nombreuses autres tâches liées
aux périphériques matériels.
Le problème a été corrigé de deux façons: d’abord, le stack protector
utilise une API permettant de générer des nombres aléatoires sans ouvrir
de fichier dans /dev. D’autre part, une analyse a montré que la pile USB
passait beaucoup de temps à exécuter du code en ayant verrouillé l’accès
au device manager. Ce code a été modifié pour libérer le verrou plus
souvent.
DT_GNU_HASH dans les fichiers
ELF
Un autre aspect assez lent du lancement de processus est le
chargement des bibliothèques et la recherche des symboles dans ces
bibliothèques. Pour identifier si une bibliothèque contient un symbole,
la recherche se fait par un hash du nom de la fonction recherchée.
Historiquement, c’est la section DT_HASH qui est utilisée, mais les
utils GNU implémentent également DT_GNU_HASH, qui utilise une meilleure
fonction de hash et ajoute également un bloom filter qui permet
de tester très rapidement, mais de façon imparfaite, la présence d’un
symbole dans une bibliothèque.
Le chargeur de bibliothèques de Haiku sait maintenant utiliser les
tables DT_GNU_HASH, mais ce n’est pas encore déployé car les gains de
performances ne justifient pas l’augmentation de taille des
bibliothèques (il faut stocker les tables dans l’ancien et dans le
nouveau format). Il sera toutefois possible de l’ajouter au cas par cas
sur les bibliothèques où le gain est important (par exemple s’il y a
beaucoup de symboles).
premapping de mmap
La fonction mmap permet de mapper un fichier directement
en mémoire. Les écritures en mémoire sont ensuite directement
enregistrées sur disque. Il n’est pas souahitable de charger tout le
fichier d’un coup au moment de l’appel à mmap, ce serait trop lent. Mais
il ne fait pas non plus attendre que le logiciel accède à cette mémoire
et remplir les données au goutte à goutte (ou plus précisément, une page
de 4Kio à la fois).
Un cas particulier est le traitement des bibliothèques partagées, qui
sont chargées en mémoire de cette façon. Dans ce cas, le fichier est
probablement déjà chargé quelque part en mémoire pour un autre
processus, et il devrait être possible de réutiliser les mêmes données.
Le code testant cette possibilité ne fonctionnait pas à tous les coups,
ce qui fait que des fichiers qui auraient pu être mappés tout de suite,
ne l’étaient pas.
Une autre amélioration est d’utiliser plusieurs allocateurs séparés
pour chaque processeurs, pour réduire les blocages entre différents
threads qui ont besoin de manipuler des pages de mémoire.
Suppression des zones
mémoire
Les applications Haiku peuvent créer des zones de mémoires (appelées
areas) qui disposent d’un identifiant unique et peuvent être
partagées avec d’autres processus.
Lorsqu’une application s’arrête, il faut supprimer toutes les areas
qui ont été créées. Cela était fait par une simple boucle supprimant ces
zones une par une. Mais cela pose un problème: chaque suppression doit
verrouiller la liste des areas puis la déverrouiller. Le code a été
modifié pour verrouiller la liste une seule fois et retirer de la liste
toutes les zones d’un seul coup, avant de faire les autres opérations de
suppression qui n’ont pas besoin d’accéder à la liste.
Au total, toutes ces améliorations conduisent à une amélioration des
performances de plus de 25% sur un test en condition réelles
(compilation d’une partie des sources de Haiku).
Calcul
des sommes de contrôles des paquets réseau par le matériel
Dans un autre domaine, une perte de temps conséquente est le calcul
des checksums pour les paquets réseau reçus et envoyés. En effet, ce
calcul était fait systématiquement par le logiciel, même si le matériel
est capable de s’en charger. Il est maintenant possible pour les pilotes
réseaux qu’ils sont capable de vérifier et de générer ces checksums par
eux-même, et ainsi la pile réseau peut s’en dispenser. Cela permet aussi
de se passer entièrement de checksums sur les interfaces localhost, qui
ne devraient pas subir de corruption de paquets, et ne gagnent rien à
cette vérification.
Cela a été également l’occasion de supprimer quelques copies des
données des paquets réseau.
user_mutex
La structure user_mutex joue un rôle similaire aux
futex de Linux. Elle est utilisée pour implémenter, par
exemple, pthread_mutex et pthread_rwlock.
L’implémentation avait plusieurs bugs (race conditions), et a été
remplacée par un nouveau système plus efficace.
Au total, toutes ces améliorations permettent des performances 25%
meilleures que la version beta 4 de Haiku. Il reste cependant de quoi
faire, puisque certains benchmarks (compilation d’une partie du code
source de Haiku) restent près de 2 fois plus lent que l’opération
équivalente sous Linux.
Chaîne de compilation
Haiku est compilé avec gcc, ld et les binutils. Ils nécessitent tout
trois un petit nombre de patchs maintenus dans un dépôt git dédié et
reversés dans les versions upstream autant que possible. Une version de
gcc 2.95.3 est également utilisée pour les parties du système assurant
encore la rétro compatibilité avec BeOS, les versions plus récentes
utilisent un mangling
différent et ne sont pas inter opérables.
L’outil de compilation utilisé est Jam, développé à l’origine par
Perforce et dont il existe plusieurs forks dont un maintenu par Boost et
un autre par Freetype. Haiku utilise sa propre version de Jam avec de
nombreuses évolutions.
Commençons la liste des changements dans cette section avec des mises
à jour de dépendances : Haiku est maintenant compilé avec GCC 13.2 (la
version 14 sera intégrée prochainement). La bibliothèque ICU utilisée
pour implémenter toutes les fonctions d’internationalisation (qui se
trouve donc avoir un rôle assez important dans la bibliothèque C
standard) a été mise à jour en version 74.
Le travail pour supprimer tous les avertissements du compilateur se
poursuit petit à petit, mais les problèmes restants sont de plus en plus
difficiles à corriger, soit parce qu’il s’agit de code tiers (qu’il est
plus facile de garder en l’état pour le synchroniser avec de nouvelles
versions), soit parce que l’avertissement ne peut pas être corrigé
proprement sans perte de performance, ou encore d’une façon qui contente
à la fois gcc 13 et gcc 2 pour les parties du code compilées avec ces
deux versions.
On peut toutefois mentionner que tous les trigraphes
présents dans le code (par accident, par exemple il est facile d’écrire
“??!” dans un commentaire) ont été supprimés. Ils ne sont plus
disponibles dans C++ à partir de la version 17 et génèrent des erreurs
de compilation.
D’autre part, l’option de compilation
-Wno-error=deprecated a pu être désactivée, car plus aucun
code ne déclenche cette erreur.
Puisqu’on parle d’options de compilation : l’optimisation
“autovectorisation” pour la compilation du noyau a été désactivée pour
l’instant. Cette option fait que le code utilise des instructions SSE,
et faire cela dans le noyau problématique pour la plupart des machines
virtuelles (QEMU, VMWare et Virtual Box). La plupart des autres noyaux
n’utilisent pas ces instructions, ce qui fait que des bugs dans les
hyperviseurs sont tout à fait possibles, par manque de tests. Mais le
problème pourrait aussi venir de Haiku. L’investigation est pour
l’instant remise à plus tard.
Un dernier changement dans le système de build consiste à permettre
l’utilisation de git worktree. Quelques commandes git sont
utilisées lors de la compilation pour calculer le numéro de version du
code en train d’être compilé, et ça ne fonctionnait pas correctement
dans ce cas de figure.
Ces documents sont complétés par de nombreuses pages et articles sur
le site internet, et deux livres pour apprendre à
programmer en C++ avec Haiku, ou encore un document de référence
pour la conception d’interfaces graphiques et un autre pour le style
graphique des icônes.
Documentation d’API
La
documentation d’API de BeOS était assez complète et de bonne
qualité. L’entreprise Access Co Ltd qui a hérité de la propriété
intellectuelle de BeOS a autorisé le projet Haiku à la réutiliser et à
la redistribuer. Malheureusement, cette autorisation est faite avec une
licence Creative Commons n’autorisant pas les modifications. Cette
documentation ne peut donc pas être mise à jour, ni pour corriger les
erreurs, ni pour ajouter des informations sur toutes les nouvelles
fonctions ajoutées par Haiku ou les différences entre les deux
systèmes.
Il est donc nécessaire de réécrire une nouvelle documentation à
partir de zéro. Ce travail est assez ingrat lorsqu’il s’agit de
re-décrire ce qui est déjà très bien expliqué dans la documentation
existante. La nouvelle documentation a donc tendance à se concentrer sur
les nouvelles fonctions, et il faut souvent jongler entre les deux
documentations, le contenu des fichiers .h, et des exemples de code
d’applications existantes pour découvrir toutes les possibilités
offertes.
Il ne semble pas utile de lister chaque fonction ou méthode qui a été
documentée. On peut mentionner une page d’explications sur la
bibliothèque C standard, comprenant des liens vers les spécifications
POSIX qui documentent déjà la plupart des choses, et quelques détails
sur les différences avec d’autres systèmes.
Une autre nouvelle page documente les primitives de synchronisation
qui sont disponibles pour le code s’exécutant dans le noyau.
Documentation interne
La documentation interne était à l’origine simplement une
accumulation de fichiers dans divers format dans un dossier “docs” du
dépôt Git de Haiku. Depuis 2021, ces fichiers ont été rassemblés et
organisés à l’aide de Sphinx, qui permet de mettre à disposition une
version navigable en HTML et de donner une meilleure visibilité à ces
documents.
D’autres pages sont petit à petit migrées depuis le site web
principal de Haiku, qui n’est pas un très bon support pour de la
documentation, et bénéficiera un jour d’une refonte pour être plus
tourné vers les utilisateurs que vers les développeurs.
Quelques nouvelles pages ajoutées cette année:
Une documentation sur l’utilisation de divers outils de complétion
de code automatique avec le code source de Haiku
Une page présentant l’organisation du code source et les principaux
dossiers et sous-dossiers
La documentation de l’outil rc utilisé pour compiler
les “resources” attachées aux exécutables a été intégrée
Le système de fichier FAT a reçu également une page de documentation
à l’occasion de sa réécriture
Un point sur le financement
L’association Haiku inc qui gère le compte en banque de Haiku publie
chaque année un rapport
financier.
Le financement provient principalement de dons des utilisateurs et
supporters de Haiku. Le projet reçoit également une compensation
financière de Google pour le temps passé à encadrer les participants du
Google Summer of Code (voir le paragraphe suivant). La contribution de
Google cette année est de 3300$.
Les plateformes de don les plus utilisées sont Paypal et Github
sponsor. Ce dernier est recommandé car, pour les dons reçus via Github,
c’est Microsoft qui paie les frais bancaires de la transaction. 100% de
l’argent donné arrive donc sur le compte de Haiku. Tous les autres
opérateurs ont un coût, soit fixe lors des retraits, soit un pourcentage
de chaque don, soit un mélange des deux.
En 2023, l’association a reçu 25 422$ de dons et a dépensé 24
750$. Elle dispose d'une réserve confortable de 100 000$
(accumulés avant 2021, alors qu’il n’y avait pas de développeur salarié)
ainsi que d’environ 150 000$ en cryptomonnaies.
Les dons en cryptomonnaies sont pour l’instant bloqués sur un compte
Coinbase suite à des problèmes administratifs (le compte n’est pas
correctement déclaré comme appartenant à une association, il faudrait
donc payer un impôt sur le revenu lors de la conversion en vraie
monnaie). Il semble difficile de contacter Coinbase pour régler ce
problème.
Du côté des dépenses, le poste le plus important est le paiement de
21000$ à Waddlesplash, développeur employé par Haiku inc pour faire
avancer le projet Haiku. Il travaille à temps partiel et avec un salaire
très bas par rapport au marché, comme cela a été fait pour les
précédents contrats entre Haiku inc et d’autres développeurs. Les
finances de l’association ne permettent pas encore d’assurer un emploi à
plein temps avec un salaire correct sur le long terme (c’est faisable
sur le court ou moyen terme à condition de puiser dans les réserves de
trésorerie).
Le reste des dépenses concerne principalement le paiement de
l’infrastructure (serveurs pour le site internet, l’intégration
continue, hébergement cloud pour les dépôts de paquets) pour environ
3000$.
Il faut enfin compter environ 500$ de frais Paypal, puis quelques
dépenses administratives (déclaration de changement d’adresse de
l’association, déclaration d’embauche) pour des montants négligeables
(moins de 10$ au total).
En 2024, l’objectif fixé en janvier était de récolter 20 000$ de dons
supplémentaires. Cet objectif a été atteint dès le mois de Juillet, et a
donc été révisé pour tenter d’atteindre les 30 000$. Cela permettra de
rémunérer Waddlesplash pour un plus grand nombre d’heures cette année,
ou bien d’envisager l’embauche d’une deuxième personne si unë candidatë
se présente parmi les personnes contribuant au projet (l’embauche d’une
personne extérieure ne se fera pas tant que l’association ne peut pas se
permettre de proposer une rémunération raisonnable).
Google Summer of Code
Haiku participe au Google Summer of Code depuis 2007. Il
s’agit d’un programme ou des étudiants (et d’autres participants pas
forcéments étudiants, ces dernières années) sont payés par Google
pendant 2 mois pour découvrir la contribution à des projets de logiciels
libres.
Ce programme a été monté par le “Open source program office” de
Google. Leur intérêt est de défendre leur image d’entreprise sympathique
(bien mise à mal ces dernières années, c’est devenu un géant de la
publicité en ligne et de l’aspiration des données personnelles), et de
contribuer à la richesse d’un écosystème de logiciels libres dont ils
bénéficient beaucoup. Cela permet aussi d’encourager des personnes à
s’essayer au développement logiciel, facilitant indirectement le
recrutement chez Google en augmentant le nombre de candidats. Ces
justifications peuvent sembler hypothétiques ou très indirectes, mais
elles ont convaincu Google d’attribuer un budget de quelque millions de
dollars à ce programme.
Une équipe de Google choisit les projets de logiciel libres
participants parmi de nombreuses candidatures. Chaque projet participant
propose une liste d’“idées” (un peu sous la forme d’un sujet de stage)
et a ensuite la responsabilité de choisir parmi les candidats qui ont
répondu à cette offre (en respectant les critères de non discrimination
imposées par Google ainsi que les embargos imposés par les USA), et
d’assurer l’encadrement des personnes sélectionnées. Google rémunère les
participants, et dédommage les projets participants pour le temps
investi.
Cette année les dévelopeurs de Haiku encadrent 5 participants :
Calisto
Mathias – Re-design de la fenêtre de recherche de fichiers
Le système de fichier BFS utilisé par Haiku permet l’exécution de
requêtes (comme une base de données) exploitant les attributs
étendus des fichiers, qui peuvent être indexés.
Ce système permet de faire beaucoup de choses, et la fenêtre de
recherche du navigateur de fichier essaie d’en tirer parti. Cependant,
l’interface résultante est trop complexe, et peu de personnes prennent
le temps de concevoir des requêtes améliorant leur façon de travailler,
se cantonnant aux quelques exemples fournis.
L’objectif de ce projet est de refondre l’interface de cette fenêtre
pour obtenir quelque chose de plus intuitif, et également d’afficher en
temps réel les résultats de la requête dès qu’elle est modifiée, pour
encourager les utilisateurs à expérimenter avec des requêtes plus
complexes.
Daniel
Martin – Virtualisation matérielle accélérée avec NVMM
Haiku n’est pas encore parfait, et certaines tâches nécessitent
encore l’utilisation d’autres systèmes d’exploitation. Une partie des
utilisateurs ont donc une configuration en double boot, ou bien lancent
Haiku dans une machine virtuelle.
L’objectif de ce projet est de permettre d’utiliser Haiku comme
système principal, et de lancer les autres systèmes dans des machines
virtuelles. Cela sera réalisé à l’aide d’un portage de NVMM, qui a été
développé à l’origine par NetBSD et Dragonfly BSD. Cette bibliothèque a
l’avantage d’être bien documentée et conçue pour faciliter son
adaptation vers d’autres systèmes.
NVMM sera complétée par l’utilisation de QEMU qui pourra fournir un
“front-end” à cette mécanique.
Diego
Roux – Pilote pour les cartes sons virtuelles VirtIO
Pour les personnes utilisant Haiku dans une machine virtuelle, il est
intéressant d’utiliser autant que possible la famille de périphériques
VirtIO.
Il s’agit de périphériques virtuels conçus sans s’inspirer de
matériel existant, et plutôt pour avoir l’interface la plus simple
possible entre la machine virtualisée et son hôte.
Haiku dispose déjà d’un jeu de pilote Virtio relativement complet
(réseau, stockage de masse, affichage graphique). Le but de ce projet
est de compléter cet ensemble avec un pilote pour les cartes son
VirtIO.
trungnt2910 – Portage de GDB
Haiku dispose de son propre débugger (appelé Debugger, de façon assez
peu originale). Ce dernier présente une interface graphique confortable,
mais une interface en ligne de commande beaucoup plus limitée. Il
souffre également de quelques problèmes de performances et d’un manque
de prise en charge des fichiers exécutables et bibliothèques compilés
avec autre chose que GCC. Il est également incapable de faire du
debug à distance ou de s’intégrer dans une interface graphique
existante (par exemple au sein d’un IDE).
L’objectif de ce projet est de ressusciter la version de GDB ciblant
Haiku. Cette version très ancienne était utilisée avant l’apparition du
Debugger natif. Le projet est en bonne voie, le code d’interfaçage a été
entièrement réécrit pour s’adapter aux versions modernes de GDB, et
plusieurs évolutions et corrections ont été intégrées dans le système de
debugging de Haiku (par exemple, pour mettre en pause tous les threads
nouvellement créés afin que le debugger puisse les intercepter).
Zardshard
– Migration du navigateur web WebPositive vers WebKit2
Le navigateur WebPositive utilise le moteur de rendu webKit.
Actuellement, il s’interface avec ce moteur via l’API WebKitLegacy.
Cette API exécute tout le moteur de rendu web dans un seul processus, et
ne fournit pas les garanties d’isolation nécessaires pour les
navigateurs web modernes (que ce soit en terme de sécurité, ou en terme
de fiabilité).
L’objectif de ce projet est de reprendre les travaux déjà entammés en
2019 pour migrer WebPositive vers la nouvelle API “WebKit2”, et
bénéficier d’une séparation entre l’interface graphique, la
communication réseau, et le rendu HTML/CSS/Javascript dans des
applications séparées. Ainsi, un crash d’un de ces composants peut être
récupéré de façon transparente sans faire disparaître toute
l’application (et les données non enregistrées de l’utilisateur
avec).
Le projet est également en bonne voie, un navigateur de test permet
déjà d’afficher quelques pages ce qui montre que les bases sont en
place. Il reste à régler de nombreux problèmes de rendu de texte, ainsi
qu’à implémenter la gestion des entrées (clavier et souris) pour avoir
un navigateur web utilisable. Il faudra ensuite migrer WebPositive vers
ces nouvelles APIs.
I had problems with my last batch of Nova boards.
For some reason they would not work. At first I expected a bad solder or other issue while assembling the
boards, but after the 3rd one not working in a row, I started to get suspicious.
Usually I would notice sooner, as I try to keep one working board in stock
for comparing with the new ones if something isn't working. But due to "high"
demand (I sold 30 of them), I didn't have a sample of the latest PCB revision to compare against.
Also, one of the boards had an IC with a pin I forgot to solder, and on another one
I put a chip backwards, which I had to unsolder and put back the right way, so I
wasn't sure if something could have been damaged by that. Which is why I kept
trying to assemble a "good" board to compare against.
Then, I noticed that he boards worked just fine, until I plugged the
snaphat.
This is a relatively simple thing: a component that clips onto the RTC chip,
containing a lithium cell and a crystal, packed in epoxy. It has just 4 pins,
two for the crystal and two for the battery.
The chip is manufactured this way to allow SMD soldering of the RTC chip, and
then adding the large package with the battery and crystal on top after soldering.
My current stock of snaphats comes from utsource. I ordered them in march
2023, I guess Farnell was out of stock at the moment and I decided to order them from
utsource instead. What I received was obviously not the real thing, if you
compare the chips side by side, the markings are very bad quality and out of focus, and
the yellow plastic is visibly thinner, almost transparent. I expected these
things would be at least marginally functional. Maybe the clock would drift, or maybe
the battery wouldn't last very long.
What I didn't expect is for some of them to have the battery polarity
reversed! This was the case for at least 7 of the 10 I bought. I say at least,
because I have not done enough testing and it's possible that I sent 2 broken
ones to a customer already (I test the boards without battery, if that works,
I snap the battery on when shipping them and don't always do a full test that
the battery works. Well, I will now.)
Interestingly, there are also one or two with the battery polarity correct.
But the markings on the hat are printed flipped 180º, so the pin 1 marker is at
the wrong place. At least the plastic pin forcing insertion in the right
direction is placed correctly.
So... the good news is I know what the problem was, and fortunately it looks
like nothing was damaged. The bad news is I have to order real chips and I lost
30 to 40€ in the process buying components that don't quite work. Unless
someone is also manufacturing fake RTC chips which also have a reversed
polarity for the battery terminals?
Hello there! In the previous article in this series, I was trying to add a new backend to binutils, and my attempt was almost
immediately stopped by a failure of expect (a tool needed for binutils test) when running on Haiku.
The bad news is the bgu with expect is not resolved. The good news is that I ported another assembler and linker, and then built a C compiler on top of that (with the previous article being published a bit more than a year ago, and me doing also a lot of other things in between, this is not bad at all).
I actually already talked about the vasm assembler in the previous post. At the time, vasm was not able to handle the pure 16-bit addressing, and its companion linker, vlink, had the same problem. When I discussed this with the developers, they initially weren't too interested about it, it would be a lot of work for a rather obscure CPU family. However, after I made this request, they got similar questions from at least two other people working on similarly strange architectures, and all landing on vasm as a possible candidate to get them out of that situation.
So, I received an email telling me that they finally decided to support it. Thanks a lot to the developers of vasm, vlink and vbcc, who were both very helpful with providing good documentation and also email support as I tried to get this working. Together we could identify a few problems and ask ourselves a few interesting questions, such as, what is the native endianness of a CPU that can only address 16 bit bytes and never consider them as an 8 bit or a 32bit quantity? Anyway, in the end, I could implement everything I needed in vasm, and Frank Wille made the needed changes in vlink so I could finally have an assembler that would build .o file and a linker that would link them into a cartridge image for the VSmile.
I believe this part of the work to be 100% complete now (in fact, it has been complete for a while and I did not discover any bugs). The next release (2.0) of vasm and vlink will thus include support for the unSP CPU.
Once that was solved, I turned my attention back to the compiler. Now it was possible to set it up correctly and use it to build a more serious C project. I used the Contiki operating system as my test project, because I had already ported it to the V.Smile using the compiler provided by the CPU manufacturer. So I know the C code was correct and all I had to do was translate it to assembler.
vbcc is very well documented, and also much simpler than more modern compilers. It works as most compilers do: the frontend parses the C code and convert it into a list of basic operations (load a variable into a register, add two values together, store something at a memory address, etc). The backend must then convert this list into actual assembler instructions. The main difficulty is that the internal representation uses arbitrary operations (such as operations using two operands in memory and storing the result also in memory), whereas the unSP CPU is a load-store architecture: operations will always store the result in a register (except the ST instruction), and at least one of the operands has to be a register as well. So, one single operation from the internal representation can turn into a few instructions. The other problem is that, with only 4 general purpose registers, there is not much space, and the frontend will usually decide to allocate all of them to variables used in the function. So, the generated code has a lot of PUSH and POP instructions to temporarily save registers on the stack and use them for something else. This could be improved by telling the frontend to always reserve one register for the internal use of the backend, or optimized in various other ways.
In a somewhat opposite way, the frontend does not know about some "advanced" operations that CPU instructions can do. For example, it has no notion of auto-increment instructions, that dereference a pointer and also change its value. The frontend will instead generate separate operations, to deference the pointer, then to increment it. A specific optimizer pass will be needed to handle this (vbcc already does it for other CPU architectures, but it is CPU specific so the code will have to be copied and adapted). Overall, the generated code is not the most elegant, but can be tweaked by writing the C sources in an unusual style, for example to better hint the compiler that some values are constant and can be used directly in the code instead of through a variable indirection. Modern C would solve this with the constexpr keyword, but vbcc doesn't have that yet. There are a few other problems such as relying a lot on the stack for variables, whereas on unSP it may make more sense to allocate them at fixed addresses (as static variables) for non-reentrant functions, in some cases.
Anyway, the most annoying thing was my own fault: the operands in the compiler internal representation can have various flags, such as REG to indicate that the value is stored in a register. I was initially a bit confused by what the flags meant exactly, and as a result, I wrote a lot of the backend code while ignoring some of them. Eventually (after a lot of time looking at dumps of the internal representation, comparing it with the generated assembler and using the MAME debugger), I started to understand what I had missed the first time, but a lot of code was already written, and it is very repetitive code but full of special cases. So, I spent quite some time fixing handling of the DREFOBJ flag (which means the variable or register value should not be used directly, but as the address of the operand, basically it is used to dereference pointers in C). There are probably some cases where it is still missing, and the pointer address value will be used instead of the pointed value.
Speaking of MAME: that is also a relatively recent addition to my toolset on Haiku. MAME itself had been available since 2019 (thanks to Extrowerk who did the initial port and recipe), but it was only since 2022 that I could build it with the Qt GUI for the debugger. Lately, Alex Brown has been taking care of updating it regularly to newer versions, which allows me to eventually get the few bugfixes to the VSmile emulation. MAME is actually the only application forcing me to have Qt (and a lot of dependencies) installed on my Haiku machine. Maybe someday I will look into building a native Haiku GUI for it instead...
I have more plans for this compiler, there is room for optimizations to generate much better code, and I should probably use it on some larger project on the VSmile console. But, for now, I will let it aside for a while, and maybe hope that someone else will help improve it (it's always nicer to not work alone on things, or just come back to a project after a while and discover that someone else decided to take care of it for a while). For now, I am already thinking about my next yearly talk at the Capitole du Libre conference, where I enjoy doing talks about weird and somewhat useless projects. I already did one about the VTech V.Smile 2 years ago, and I think it's too early for an update on that. But my attempts at porting Smalltalk to the Amstrad CPC should make a good talk. But it would be better if I show up with something at least somewhat working. So I guess that will be the goal for the next 3 months!
If you want to try the compiler for yourself, you will have to follow the manual setup instructions for now. I hope to make this simpler as the code get upstreamed in vbcc, and then vasm, vlink and vbcc get new releases including my work.
Liens en vrac vers les amis, projets auxquels je participe, et d'autres trucs...
My world domination projects
GrafX2, a multi-platform pixel art tool. I ressurected the project back in 2007, porting it from MS-DOS to many other platforms.
CPC SDK, an assortment Amstrad CPC cross-development tools. I got tired of gathering various tools from many places, most abandoned by their original authors, and decided to group them all in a single Git repository.
Haiku, an open source operating system for normal people.
ENSSAT Robotique, I was a member of my engineering school robotics team. We managed to get to the final phases of the French robotics competition.
Friends
Le blog d'ASCII, un collègue étudiant à l'ENSSAT. Jeux de rôles, sites web, et plein d'autres trucs super!
Le musée de Sylvestre. De l'Amstrad, des gros pixels qui cligotent, et plein d'autres trucs super!
My previous article on porting Little Smalltalk to the Amstrad CPC received
some attention and support messages from other people, who either find it
useful, or just interesting to read. I got a comment from someone who was happy
that I am doing retrocomputing and not just retrogaming. I'll take that as a
compliment and I think it is a somewhat common theme in the Amstrad CPC scene.
As you can probably guess from the previous post on garbage collectors, I
am still thinking about this project and trying to figure out a way to make
things work.
First, I noticed that I had made a mistake in my first attempt: by not looking
closely enough, I assumed that the image file would be somewhat specific to the
word size and endianness of the target machine, leading me into attempting to
build the image directly on the CPC. A closer look at the code made me realize
that this is not the case. The stored image format is generic and does not change
depending on endianness and word size.
So, that means I can throw away my work on porting the image builder, and
focus on the VM itself. But I was still curious why the built image was more
than 100 kilobytes. Surely there is not *that* much code in Little Smalltalk?
I started taking a closer look at the image file in an hex editor. And I
noticed something that I should have found much earlier, especially as it was
hinted at in the Little Smalltalk book. The image contains not only the bytecode,
but also the sourcecode to all methods. This allows Little Smalltalk to not
include a disassembler. You can open the sourcecode in an editor, edit it as
you want, and then recompile it to create a new bytecode. But the price to pay
is a lot of used up space in the image. I think later I will write (or port) a
bytecode disassembler (I think there is one in one of the later versions/forks
of Little Smalltalk?). For now, I disabled the sourcecode generation in the
ImageBuilder, and it resulted in a much smaller image of 49KB. Now that's a
size I have at least some chance of fitting into the CPC RAM! So let's try that.
The porting of the Smalltalk VM to the CPC is pretty similar to what I did
for the ImageBuilder. I compile the C code, set up a linker script, replace
file IO with AMSDOS routines which are much simpler than POSIX, but should
still be enough for our needs at least to load the initial image. I encounter
similar problems in the image loading than in the image saving (too much stack
usage by recursive functions) and apply the same workaround (moving the stack
to a different place before calling the heavily recursive functions).
Anyway, I got it running. Sort of. And then I ran out of memory again.
Memory layout of the CPC
To understand the problem (and the next steps), it is useful to understand
how memory is addressed on the CPC. First, remember that the machine uses a Z80
CPU, and so, it can address 64KiB of memory using 16-bit addresses. On the CPC
this is organized as such (rounded to the next 256th byte for simplicity):
0000-0100: Reset vectors and "low jumpblock" from the firmware. This is the
handlers for the RST instruction, interrupts, etc.
0100-A700: Available for applications.
A700-BFFF: Used by the OS. Variables from the disk ROM, indirections for
various system routines that are actually implemented in ROM, etc.
C000-FFFF: video RAM
So we have about 42KiB of RAM free for our application. Initially I tried to
fit both the interpreter code and the Smalltalk heap (and any variables needed
for the interpreter) into this space, and it didn't work. I removed various parts
of the interpreter, I replaced the garbage collector with a stupid one that
can't free any memory (this particular idea being brought up by one of the
papers on garbage collectors I read, apparently some people were using their LISP
machines this way for performance reasons and it was used also as a benchmark
of the fastest possible GC algorithm).
Anyway, even with just half an interpreter, I still was out of memory rather
quickly. So it's time to find other approaches.
Mocing the code to a ROM
The Amstrad CPC OS is well designed to allow running code from expansion ROMs.
These ROMs are mapped at C000-FFFF (overlapping the screen memory), and can
easily call into the system ROM (which maps at a different address) and also call
into other ROMs as neede (for example, the AMSDOS ROM for disk access).
To put our code in ROM, we need some changes to our linker script and CRT0.
I based this on scripts I wrote earlier while making a ROM version of the
Contiki operating system.
The main change here is that _CODE is now at 0xC000, and we also add that
_INITIALIZED is at 0x0100. _INITIALIZED is where the RAM area starts, the other
RAM sections are put right after it. So now the linker will put our code in
ROM, and our data in RAM.
Of course we also need to adjust the CRT0 file.
;; FILE: crt0.s
;; Generic crt0.s for runing code as a CPC ROM.
.module crt0
.globl _main
.globl _progend
;; Ordering of segments for the linker.
; Things that go in RAM
.area _INITIALIZED
.area _DATA
.area _BSS
.area _HEAP
_progend::
;; Things that go in ROM
.area _HOME
.area _CODE (REL,CON)
.area _INITIALIZER (REL,CON)
.area _GSINIT (REL,CON)
.area _GSFINAL (REL,CON)
.area _CODE (REL,CON)
;; The _CODE area starts with the ROM header.
;; This has some version markers, a ROM type (background here, meaning
;; it will not immediately take control of the system during boot),
;; And a table of "RSXs", that is, commands that can be called from
;; BASIC or ASM code by name.
.db #1 ; Background ROM
.db #4 ; Version 4.0
.db #0
.db #0
.dw rsx_table
;; The first routine is called during boot, in our case we do nothing
;; at that point. The second is invoked by the name defined in the
;; table below (|ST in our case).
jp do_nothing
jp init
rsx_table:
.db 'T' + #0x80
.ascii "S"
.db 'T' + #0x80
.db #0
init:
; This is the program entry point. Execution starts here
;; Initialise global variables, clear BSS areas
;; Note: not used in Smalltalk, I removed all initialized variables.
;; Probably still a good idea to clear the BSS part so all global variables are
;; initially set to 0...
; ld bc, #l__INITIALIZER
; ld a, b
; or a, c
; jr Z, gsinit_next
; ld de, #s__INITIALIZED
; ld hl, #s__INITIALIZER
; ldir
;gsinit_next:
; Clear BSS sections
; ld hl,#s__DATA
; ld (hl),#0
; ld de,#s__DATA + #1
; ld bc,#s__DATA + #0x200
; ldir
; Initialize disk ROM. We do the simplest thing here and just initialize ROM 7.
; It wouls be more compatible to save the currently active drive and see if
; there are other ROMs to initialize.
ld hl,#0xabff
ld de,#0x40
ld c,#7
call 0xbcce
; Init the heap. Do it after the disk ROM is ready, because that may change HIMEM
; Not needed: no malloc/free is used.
; call __sdcc_heap_init
; Enter the C main.
call _main
; TODO figure out how to return to BASIC cleanly (we probably erased a bit much)
endless:
_exit::
jr endless
do_nothing:
scf
ret
That's all we need, we can now run our C code from ROM. If it fits there, that is.
Making the code a bit smaller
At this point I had about 20-24K of code. A ROM is 16K, and, for now, I don't
want the headache of switching between different ROMs for different parts of the
code (maybe later, if I can't fit it all in one single ROM). So, I commented out
more code from the interpreter and garbage collector and started simplifying
some things. There is no difference anymore between static and dynamic allocations.
All integer math code is removed as well as most other things. Also a big way to
save space was replacing printf with simpler custom functions that don't do much
custom formatting. I have way to print 16 and 8 bit values, NULL terminated strings,
and "Pascal" style strings with a length known in advance. That is good enough,
and it saved several kilobytes to not need printf's % parsing, decimal integer
formatting, etc.
After this, I could finally try to run my VM again. And... it ran out of memory.
I guess that makes sense. Even after moving the VM code out of the way in a ROM,
I still get only 42K of RAM free. And that has to hold not only the Smalltalk image
data, but also all variables needed by the interpreter, the AMSDOS file buffers, etc.
Well, it doesn't fit.
But, if you know a bit about the CPC, you have noticed the existence of the
CPC 6128, a machine with twice as much RAM as what I have explained so far.
How does this work? Well, the CPU cannot access any more RAM directly. So, there
is a special hardware register to map extra RAM pages. There are various ways to
use this, they are somewhat handcrafted to run CP/M Plus, a modified version of
CP/M that was shipped with the machine and put the user code all in expansion
memory, allowing applications up to 63K of RAM usage, with the video memory,
and all system internals remaning in the main 64K of RAM.
Anyway, I decided to do something simple, and the simplest way to handle
this memory in a way that's friendly to the CPC firmware is to map it by blocks
of 16K at addresses 0x4000-0x7FFF. In my previous article about garbage collectors,
I considered some more advanced options, but in the end I decided to keep things
as simple as possible: all the Smalltalk heap and objects will live in this 4000-7FFF
space, and I can keep the remaining parts of RAM (0100-3FFF and 8000-A7000) for the
internal needs of the interpreter and GC (which will both need some variables and
structures).
Of course, this create a new difficulty: the C compiler isn't designed to
work with such memory banks. So, I will have to explicitly map the right
memory page before accessing these variables. There are again multiple ways to
handle this (so many choices...). For example, I could use a 16-bit address
referring to a position in the 64K of extended RAM. But that would prevent having
anything more than 64K of extended RAM. There are memory expansions for the CPC,
and if just loading the base image already requires nearly 50K, I should rather
prepare for use of one of these.
So, I will instead use indirect pointers. The idea is that pointers to objects
can be represented as such:
The "bank" here is the value to send to the Gate Array (the Amstrad CPC memory management chip)
to map the correct RRAM page. For now we have to know about the vlaues C4, C5, C6 and C7 (these
allow access to the 64K of extended memory) as well as C8 (this will put the 4000-7FFF from the
base RAM back where it was).
The code to dereference an indirect pointer is like so:
__sfr __banked __at(0x7F00) GateArray;
// __sfr means this is accessed with the IN and OUT instructions of the Z80, instead of RAM access instructions
// __banked means the address is still 16bit (normally for IN and OUT it should be 8 bits, but manufacturers of hardware using the Z80 didn't respect this)
// __at(0x7f00) forces the variable to be at that address
// This means we can now write:
// GateArray = xx;
// And this will generate the appropriate assembler code:
// LD BC,0x7f00
// LD A,xx
// OUT (C),A
struct object* DEREF(struct indirection* x)
{
// Get the pointer first, in case the indirection is itself stored in
// the currently active bank (I didn't use that in the end, so maybe I
// can simplify this)
struct object* o = x->ptr;
GateArray = x->bank;
return o;
}
I then modified the Smalltalk object structure to contain "indirections" instead
of pointers to other objects. And I also added a lot of calls to DEREF all over the
code of the interpreter, to map the correct memory page before accessing anything.
As a first step, I didn't try to be smart about it (well, of course I did, then I
had a lot of bugs, and I stopped trying to be smart, and then it worked better).
So, I made a rule to not have any direct object* pointer stored anywhere in the
interpreter code, only indirection* and calls to DEREF over and over. This will
not be fast, as every other instruction will be switching to a different memory
bank (or uselessly activating a bank that is already activated). But let's try
to make it work this way first.
Starting from here, I have two choices. I can store indirections directly
inside objects, or separately. Basically, one of these two:
As you probably guessed by the fact that I named this structure "indirection",
I went with the second option. The reason for this is that it is much easier to
code when object sizes are in multiple of 2 bytes, and not in "multiples of 3 + 2".
And also because you would have to be very careful to store the destination pointer
before connecting the new bank, if the indirection itself was stored in a bank.
So, now, I have a big table of indirections living somewhere in 0100-3FFF.
This table is filled with pointers to objects in the banked RAM. It creates
a limitation to about 5000 objects in the system (16K/3). I have not hit this
limit yet, but the initial image is already about half of that. We'll see how
that goes.
This indirection also may simplify work for the garbage collector: it allows
to easily move objects from one address to another, since all references to an
object will be through the indirection table: just update the indirection table
entry, and you're done. I think the garbage collector will benefit from that
simplicity of not having to crawl through all other objects to find references
to the one being moved.
Another advantage, mainly for debugging, is that there are now a lot of
constraints on what a valid pointer is. An indirection must have an address in
the 0100-3fff range. The pointed object must have a bank in the C4-C8 range, and
a pointer in the 4000-7FFF value range. I have added some checks to my DEREF
function to validate this. If the code attempts to dereference an invalid
reference, execution stops with an error message.
Finally, these contraints also mean we have several unused bits, both in the
pointers and in the indirections themselves. These are useful to store extra
information. In indirection pointers, the high bit being set means that the
value is not an indirection, but a direct 15-bit integer value. In indirections,
unused bits in the bank or in the pointer fields can be used by the garbage
collector to track which objects have already been garbage collected (the original
code also did similar things, but using the size field in the objects themselves for the gc,
and the lower bits of the pointers to mark integers, relying on the fact that
allocations are aligned to 2 bytes and so for pointers that bit is always 0).
So, after all these changes and a bit of debugging, we're getting somewhere.
The full image can be loaded into banks, and then execution starts. Unfortunately,
it seems to be confused after a short time, returning to the wrong place after executing a
"block2 (blocks are similar to lambdas in other programming languages).
So, I have more debugging to do. I have to keep a balance between adding more
debug statements in the code, and not overrunning my budget of 16K of code in a
single ROM. For now, I do not want to leave C and start rewriting things in
assembler, because it would make it harder to change things and try new ideas. I
hope I can delay that until the code is mostly running and there are at least some
parts I'm sure won't change anymore. Then I can rewrite or hand optimize these first, and save enough space
for debugging the other parts. Also, working in the ACE emulator has been very helpful,
setting breakpoints in strategic places allowed me to find and fix many of the problems.
I continued debugging, identifying and fixing various issues. For example,
if I use the DEREF function twice in the same line of code, like so:
DEREF(x) = DEREF(y);
that doesn't work right, because the = operator is not a sequence point. That
means the bank activations for each side will not happen in the right order for
things to be read and written where they should. So this must always be done through
a temporary variable, which ensures proper sequencing.
Other mistakes include too many or not enough calls to DEREF, or simply writing
code that doesn't behave the same way as the original while adding the DEREF calls.
Eventually, I got it to run! Well, sort of.
Running out of space (again)
One of the very first things the Smalltalk image does is creating the 256
"Char" objects for each of the ASCII characters. Each of them is a full-blown
object, using up 6 bytes in the heap. This saves a lot of special-casing in the
VM. But, the loop to create them ends up creating a lot of objects and filling up
my indirection array. Eventually the indirection array grows into the 4000-FFFF
space, overwrites some objects there, and things crash badly in various ways.
So maybe this indirection was not such a great idea. Or maybe I need to start
thinking about the garbage collection, and see how many entries in that array
it would manage to free. But the problem with that is, at the moment, I have
less than 128 bytes left in my ROM, so a GC is not going to fit. Unless I start
removing debugging traces and optimizing some code in assembler. Which I don't
want to do until I'm sure the indirection table approach is working.
Well, that's all for today. See you in the next article in the series...
Please do not mirror my whole website using wget mirroring mode, WebReaper,
or other similar tools. It results in a lot of requests to my poor old server,
too much memory and CPU use, and the server fan spins up and makes a lot of
annoying noise. So I will ban your IP or IP range from accessing the website
if you try this.
If you want a full copy of some parts of the website, please contact me so
we can set up a better way to share the data (I can set up an ftp or sftp
or rsync thing, for example)
This is the new server of the PulkoTeam! Enjoy your stay here.
On this website you can find various stuff I work on, around the Haiku project, electronics, 8bit computers, and random things depending on the day mood.
Categories
CLHBA (4)Commité de Libération des Huîtres du Bassin d'Arcachon
Conferences (3)Various talks I did for various things