Menu

#323 ULAplus interface

future
open
nobody
None
5
2018-12-30
2014-03-11
No

These patches try to emulate the ULAplus interface, based on previous work by Gilberto Almeida available in [patches:#209], [patches:#210], [patches:#211], [patches:#212] and [patches:#213].

Changelog:

  • Code has been modernized upto revision r5074.
  • Code has evolved from a Spectrum SE mode to a separate interface because ULAplus will soon be a real replacement for 48k models.
  • Support for GTK+ and win32 UIs.
  • Support for reading/writing to SZX snapshots.
  • Removed registerport_read as it is not defined in the current specification.
  • By default the whole palette is black and it is initialized to black on reset.
  • Don't invalidate display because of flash attribute.
  • Widgets menus are drawn with the standard palette and don't use the current ULAplus palette. It is the same with keyboard help.
  • As Philip suggested, ulaplus_dataport_write() refreshes display only on changes. However refreshing the whole display is inefficient and essentially wrong as it breaks palette cycling (see multicolour demo).
  • As Fred suggested, remove clut_update_palette() and uidisplay_plot8_64() by using a pre-computed palette of 256 colours.

Still pending:

  • Fix dirty display on palette cycling.
  • Save/load screen dumps.
  • Support more UIs. Currently unsupported UIs are fb, svga, wii and xlib.
  • Check non-sinclair machines, i.e., timex and pentagon.
  • Perhaps allow to load pre-configured palettes.
2 Attachments

Related

Feature Requests: #95
Patches: #209
Patches: #210
Patches: #211
Patches: #212
Patches: #213
Wiki: Fuse 1.2 Release Plan
Wiki: Fuse 1.2.2 Release Plan
Wiki: Fuse 1.3.0 Release Plan
Wiki: Fuse 1.3.1 Release Plan
Wiki: Fuse 1.3.2 Release Plan
Wiki: Fuse 1.3.3 Plan
Wiki: Fuse 1.3.4 Release Plan
Wiki: Fuse 1.3.5 Release Plan
Wiki: Fuse 1.3.6 Release Plan
Wiki: Fuse 1.3.7 Release Plan
Wiki: Fuse 1.3.8 Release Plan
Wiki: Fuse 1.4.0 Release Plan
Wiki: Fuse 1.4.1 Release Plan
Wiki: Fuse 1.5.0 Release Plan
Wiki: Fuse 1.5.1 Release Plan
Wiki: Fuse 1.5.2 Release Plan
Wiki: Fuse 1.5.3 Release Plan
Wiki: Fuse 1.5.4 Release Plan
Wiki: Fuse 1.5.5 Release Plan
Wiki: Fuse 1.5.6 Release Plan
Wiki: Fuse 1.5.7 Release Plan
Wiki: Fuse 1.6.0 Release Plan
Wiki: Fuse Next Release Plan

Discussion

1 2 3 > >> (Page 1 of 3)
  • Fredrick Meunier

    I've been thinking about the general topic some more and while I haven't read these patches yet (probably by the weekend), I gave the following comments in a recent discussion on the topic - what do you think?

    Fuse is optimised for machines where all required colour information is extracted by the ULA in two byte chunks every few tstates and other mode information takes up no more than two more bytes while the screen is being drawn and it then passes the current attributes, byte patterns and prevailing modes off to the UI level drawing routines where colour mapping etc. are done based on the fixed and known palette. This is used to minimise redraw and calculation overheads.

    The problem with emulating ULAplus is that the reprogrammable CLUTs do not resemble a normal spectrum and have more data that can vary over the frame outside of the conventional reads containing the pixel and attribute and mode information used in conventional Speccy-like systems. The CLUT contents and Timex display mode information cannot comfortably fit into the 3 bytes available for this mapping in the system.

    I think that this means that the best way to do this would be to have a new rendering system that was switched on when emulating ULAplus that bypasses all of that at the cost of lower frame rendering performance and not supporting the movie recording features which are also attribute-based. That system would then need to be supported by all the UI backends. The other obvious option would be stripping out all that code and having a less optimised approach drawing code path at the cost of using more resources all the time.

     
  • Sergio Baldoví

    Sergio Baldoví - 2014-03-13

    I'm not fully familiar (yet) with the display routines. My first thought is that display should store the indexed colours and mark dirty areas on colour changes (following the beam) while UIs should map indexed colours to pixel formats (RGB), i.e., prioritize colours over attributes.

    [...] The CLUT contents and Timex display mode information cannot comfortably fit into the 3 bytes available for this mapping in the system.

    Let's name it. You are referring to the display_last_screen array, isn't it?

    I think that this means that the best way to do this would be to have a new rendering system that was switched on when emulating ULAplus that bypasses all of that [...]

    I’m sorry but I’m not sure I understood correctly. We certainly need to extend or make display routines more flexible. If a massive change is needed, maybe two display drivers seems a good idea but I'm afraid that a frame could use both ULAplus and standard palettes.

    I need to develop (and play with) these ideas. Any suggestions are welcome.

     
  • Fredrick Meunier

    My first thought is that display should store the indexed colours and mark dirty areas on colour changes (following the beam) while UIs should map indexed colours to pixel formats (RGB), i.e., prioritize colours over attributes.

    The display would potentially also need to record what mode the display was in at the time of drawing (hi or standard resolution). ULAplus has a 256 colour palette when active so storing the active indexed ink and paper colours requires 2x8 bits per 8x1 row plus at least another one bit for the active resolution per 8 pixels, with another bit per 8 pixels to record if ULAplus modes are enabled and the normal 8 bits for the pixel state information. Hmm, stated this way it could be accommodated in the existing scheme at least for low-res screens (in hi-res mode you would need an additional 8 bits of pixel data crowding out the room that would be for mode data as described above).

    Let's name it. You are referring to the display_last_screen array, isn't it?

    Yes exactly. This array stores 4 bytes per 8 pixel row with the pixel and attribute bytes read for the 8 pixel group along with the SCLD screen mode and the FLASH state for a relatively compact representation of the screen state in this block (effectively a superior SCR format as it handles attribute and screen mode changes per 8x1 block at the point the bream draws it encoded in a relatively compact 40K frame).

    If a massive change is needed, maybe two display drivers seems a good idea but I'm afraid that a frame could use both ULAplus and standard palettes.

    Indeed, we have custom display routines for when we are emulating a plain Sinclair, a Timex or a 16 colour capable Pentagon so that the general case isn't complicated by the code required to handle a particular machine's display oddities.

    In this case I am now thinking that in ULAplus mode a custom display_write_if_dirty() routine can be added that would use the same handling as the existing display_write_if_dirty_sinclair() routine with an additional bit to flag the ULAplus active status this 8x1 block (chunk) and would interpret the data as 8x1 pixel data, 2x8 bit indexes to the active colours from the current CLUT when the ULAplus is active.

    For now I'd be tempted to ignore including hi-res support in the code given the lack of software for the mode (even in straight Timex form) and the additional complexity it presents.

    Does it make any sense?

     
  • Sergio Baldoví

    Sergio Baldoví - 2014-03-14

    Hmm, stated this way it could be accommodated in the existing scheme at least for low-res screens (in hi-res mode you would need an additional 8 bits of pixel data crowding out the room that would be for mode data as described above).

    That's pretty close to what I have in mind, start with the low-res mode and resize to qword or add another array for ULAplus data (if needed).

    In this case I am now thinking that in ULAplus mode a custom display_write_if_dirty() routine can be added that would use the same handling as the existing display_write_if_dirty_sinclair() routine with an additional bit to flag the ULAplus active status this 8x1 block (chunk) and would interpret the data as 8x1 pixel data, 2x8 bit indexes to the active colours from the current CLUT when the ULAplus is active.

    I see where are you going, that seems a good format for storing information but I suspect that display_write_if_dirty relies on static data (see last_chunk_detail) as the routine is invoked when the screen memory is written to and when happens the end of a frame. Many palette values could change in a frame (by port 0xFF3B) so IMO another tricky part is detecting the active colours at the beam position.

    Does it make any sense?

    That clarifies a lot, thanks. I'll give a go over the next week.

     
  • Fredrick Meunier

    I see where are you going, that seems a good format for storing information but I suspect that display_write_if_dirty relies on static data (see last_chunk_detail) as the routine is invoked when the screen memory is written to and when happens the end of a frame. Many palette values could change in a frame (by port 0xFF3B) so IMO another tricky part is detecting the active colours at the beam position.

    This is what we would be dealing with by recording the active colour indexes for ink and paper in the chunk so regardless of the palette changes we would only record the relevant active state at the time the beam crossed the 8x1 cell (chunk). The palette change case would be completely covered in the same way that attribute changes during raster drawing are handled.

    We can handle hires without increasing the memory footprint if we are willing to cheat and omit emulation of screens that are part hires/part not in a similar way to the handling of 16 colour Pentagon screens. I think that is a reasonable tradeoff at this point, though it is obviously a point that could be debated.

     
  • Sergio Baldoví

    Sergio Baldoví - 2014-03-23

    I've made some progress with sinclair machines by using this chunk:

    1 bit for ULAplus palette mode
    1 bit for flash status
    8 bits for active indexed ink
    8 bits for active indexed paper
    8 bits for data
    

    and display_write_if_dirty_sinclair_ulaplus() for plotting to uidisplay.

    The palette changes are triggered by display_dirty_ulaplus() when writing to the ULAplus dataport. But this refresh is inefficient and need a high CPU usage. A border change is allocated if needed.

    The border colour is stored with this chunk format:

    1 bit for ULAplus palette mode
    8 bits for active indexed colour
    8 bits for standard border colour
    

    I've added I/O contention for ULAplus ports too (same as port #FE).

    At this point the multicolour demo works well except for a misalignment between the left/right border and the main screen. The misalignment in the middle of the screen is right.

    The HAM256 viewer looks perfect.

    Still pending:

    • Make display_dirty_ulaplus more efficient.
    • Fix border misalignment.
     
  • Fredrick Meunier

    As it happens I'd done some hacking on this too but as my email was down for a few days I didn't see you had been working on it, this is my version to date. A combination of the two patches should help resolve some of the outstanding issues.

    For reducing the overhead of palette changes you should use display_update_critical( 0, 0 ) and display_refresh_main_screen(); (see the attached patch).

    The status change of this patch from the first one is:
    Fixed:
    * Palette cycling
    * Flashing dirtying in ULAplus in end of frame - FLASH state change will depend on prevailing ULAplus mode at beam time
    * Hard reset when ULAplus peripheral is enabled

    Additional TODOs
    * Border drawing
    * Switch to ULAplus drawing modes only when ULAplus is enabled
    * Properly handle ULAplus data in display_getpixel()
    * FMF movie recording ULAplus movies
    ????

     

    Last edit: Fredrick Meunier 2014-03-26
  • Fredrick Meunier

    Here is my attempt at blending my tweaks to Sergio's patch 02. Functionally it is similar, key changes are:

    • Border colour always stored as ULAplus colour index as paper value - this can cover both ULAplus enabled and disabled cases so makes things a little simpler
    • Moved ULAplus FLASH handling to its own function ala Pentagon 16 colour handling
    • Changed ulaplus_dataport_write to use display_update_critical/display_refresh_main_screen instead of display_dirty_ulaplus
     
  • Sergio Baldoví

    Sergio Baldoví - 2014-03-29

    For reducing the overhead of palette changes you should use display_update_critical( 0, 0 ) and display_refresh_main_screen();

    Brilliant! That's what I was looking for.

    Here is my attempt at blending my tweaks to Sergio's patch 02.

    Well done. I've patched some issues:

    • The ULAplus FLASH handling was assigned to a wrong function pointer.
    • There is no need to refresh the screen on palette changes if the palette mode is not enabled.

    There are some outstanding issues:

    HAM256 viewer showing some horizontal lines and dash-dotted lines. Refreshing the screen on every dataport write helps to mitigate this problem. There is no logical reason to do this, though.

    IMO modifying display_flash_reversed every 16 frames (on palette mode) could invalidate colour chunks leading to unnecessary plotting.

    Border colour always stored as ULAplus colour index as paper value - this can cover both ULAplus enabled and disabled cases so makes things a little simpler

    That not cover the case when the paper value is the same value than the ULAplus colour index, e.g.,

    10 REM Set ULAPlus paper to blue
    20 OUT 48955,15
    30 OUT 65339,7
    40 REM Enable ULAplus
    50 OUT 48955,64
    60 OUT 65339,1
    

    I think we need at least 1 bit for ULAplus palette mode and 8 bits for the colour. That requires a word of space.

     

    Last edit: Sergio Baldoví 2014-03-29
  • Fredrick Meunier

    Thanks for the feedback. I am most likely to be able to have another look at where we are over the next week.

    IMO modifying display_flash_reversed every 16 frames (on palette mode) could invalidate colour chunks leading to unnecessary plotting.

    OTOH having ULAplus palette enabled only for the bottom half of the screen should still see the FLASH attribute areas of the conventional screen update.

    Border colour always stored as ULAplus colour index as paper value - this can cover both ULAplus enabled and disabled cases so makes things a little simpler

    That not cover the case when the paper value is the same value than the ULAplus colour index

    It is as you say - I had mistakenly thought that the ULAplus palette was now a superset of the conventional palette and could do double duty as I hadn't looked deeply enough into the uidisplay_area() changes.

    Based on that, I think we should have a separate ULAplus plot8 routine vs the conventional plot8 and leave it to the display layer to decide which palette to use in the drawing layer. An area could contain areas drawn with both palettes if mid-frame mode switching is used.

     

    Last edit: Fredrick Meunier 2014-03-30
  • Sergio Baldoví

    Sergio Baldoví - 2014-03-30

    IMO modifying display_flash_reversed every 16 frames (on palette mode) could invalidate colour chunks leading to unnecessary plotting.

    OTOH having ULAplus palette enabled only for the bottom half of the screen should still see the FLASH attribute areas of the conventional screen update.

    Right. I haven't thought too much about screens that are part standard / part ULAplus. In this case, the best option is to ignore the FLASH flag in ULAplus mode.

    I had mistakenly thought that the ULAplus palette was now a superset of the conventional palette and could do double duty as I hadn't looked deeply enough into the uidisplay_area() changes.

    ULAplus has a separate palette, in fact, there are 4 standard colours that cannot be represented. The pixel chunks store indexes to these palettes and could need 1 bit for selection.

    I think we should have a separate ULAplus plot8 routine vs the conventional plot8 and leave it to the display layer to decide which palette to use in the drawing layer. An area could contain areas drawn with both palettes if mid-frame mode switching is used.

    That makes sense if pixel plotting are accumulated at the end of the frame. Sounds like a wrapper that set the current palette and call the conventional plot8.

    I've made another patch with these changes:

    • Ignore FLASH flag in ULAplus mode to reduce unnecessary plotting.
    • Use display_get_attr() instead display_get_attr_byte() + display_parse_attr()
    • Use a word to store the border colour.
    • Preliminary timex and SE support.

    Timex pixel chunks use an additional array (2 bytes) to store the ink/paper colour. I think we could reduce 1 byte by merging 6 (lowest) bits from scld_last_dec + 1 bit for ULAplus mode + 1 bit for display_flash_reversed.

    The HAM8x1 demo shows similar glitches to HAM256 viewer. It seems that some cells take the colour from the second lower row.

     

    Last edit: Sergio Baldoví 2014-03-30
  • Stuart Brady

    Stuart Brady - 2014-04-04

    Seems to be some good progress here!

    I take it from my brief scan of the code[1] that we don't intend to support use of all 260 colours on screen at once? It would be nice to support this, but I can appreciate that it's unlikely to be easy. For one thing, we'd have to modify certain UIs to drop support for 8 bpp[2] and even 4 bpp framebuffers! We could always leave support for simultaneous palettes unimplemented in these modes, though, or even settle for colours that are a little bit off.

    The way that the ULAplus renders the standard 16 Spectrum colours doesn't seem to be well documented. The high intensity values must surely map to 0b111 and I assume that low values map to 0b101, as this fits with the fact that standard Spectrum colours with low intensity blue cannot be represented in the ULAplus's 8×8×4 colour cube[3] and it's about the right level of intensity[4]. Still, it'd be nice to have this confirmed.

    When using the standard Spectrum attributes mode of the ULAplus, we should probably use exactly the colours that would be used by the ULAplus, rather than the original Spectrum palette, even if the differences are fairly minor.

    At some point I would like to finish and merge my patches for SAM Coupé emulation as we discussed some time previously. If you can, it'd be worth keeping the changes I will need to make in mind when adding ULAplus support[5]. Whilst we'll never need to to make use of the ULAplus and SAM palettes simultaneously, certain things such as palette handling would be best handled in a generic manner, to avoid duplication of effort.

    I'm not quite sure whether generation of the ULAplus palette is correct:

    green = ( green << 5 ) | ( green << 2 ) | ( green & 0x03 );
    red = ( red << 5 ) | ( red << 2 ) | ( red & 0x03 );
    blue = ( blue << 5 ) | ( blue << 2 ) | ( blue & 0x03 );

    Whilst this is what is written on the ULAplus web site, I find this somewhat perplexing. The lowest intensity value for each component is 0b00100101 (0x25). The next values are double this (0x4a), and then triple (0x6f). Then we get 0b10010000 (0x90). It seems 0x90 − 0x6f = 0x21. This is then followed by three more intervals of 0x25, giving deltas of 0x25, 0x25, 0x25, 0x21, 0x25, 0x25, 0x25.

    The scheme that I would have imagined is this:

    green = ( green << 5 ) | ( green << 2 ) | ( green >> 1 );
    red = ( red << 5 ) | ( red << 2 ) | ( red >> 1 );
    blue = ( blue << 5 ) | ( blue << 2 ) | ( blue >> 1 );

    This gives us 0b00100100 (0x24), followed by 0b01001001 (0x49), 0b01101101 (0x6d), 0b10010010 (0x92), 0b10110110 (0xb6), 0b11011011 (0xdb) and 0b11111111 (0xff), with deltas of 0x24, 0x25, 0x24, 0x25, 0x24, 0x25 and 0x24. Much more of a consistent spread. Does ULAplus implementations actually extend to eight bits in the way that's described, or should we instead implement the palette that I've described, as it's a better extension of a three-bit value into eight bits.

    I would try to share the palette setup code between displays, if possible, including generation of a greyscale palette from a black and white palette. I don't see any reason to doing this in avoid two passes — an emulation-specific function (one for Spectrum ULA, one for ULAplus, one for SAM) one to generate the colour palette, and then a common function for generating a greyscale palette from a colour one. I appreciate this was a problem with the existing code, but this is only going to get worse if/when we add SAM emulation.

    There's a particular bit of code I would suggest rewriting:

    green = ( i & 0xe0 ) >> 5;
    red = ( i & 0x1c ) >> 2;
    blue = ( ( i & 0x03 ) << 1 ) | ( i & 0x01 );

    I would write instead:

    green = ( i >> 5 ) & 7;
    red = ( i >> 2 ) & 7;
    blue = ( ( i & 3 ) << 1 ) | ( i & 1 );

    The constants are then immediately obvious in their purpose when written this way. It's also not worth thinking of these values as 8-bit quantities. Indeed, 0x03 would still be of type 'int', and i is an unsigned int, so the prefixing of a 0 just seems misleading somehow. '0x' is fine, but I probably wouldn't bother. As far as the type of i is concerned, does it need to be unsigned for some particular reason? I'd probably just use 'int'.

    Lastly:

    ink2 = attr & 0x07;
    paper2 = ( attr & 0x38 ) >> 3;
    base = ( attr & 0xc0 ) >> 2;

    I would write instead:

    ink2 = attr & 7;
    paper2 = ( attr >> 3 ) & 7;
    base = ( ( attr >> 6 ) & 3 ) << 4;

    Any half-way decent compiler should deal with this, and even if it doesn't, I don't see this as a problem.

    The bulk of the work, especially the display.c wizardry, so please keep up the good work!

    Cheers,
    Stuart

    --

    [1] display_parse_attr() returns a libspectrum_byte for ink, and another libspectrum_byte for paper, so these values are restricted to the range of 0–255.

    [2] It's worth being aware that 8 bpp video modes can sometimes have a palette entry reserved as a 'transparent' colour, for the purposes of showing the 16/24/32 bpp pixel 'beneath' it. This was used as a means of providing good desktop performance and good image quality at a time when memory bandwidth was more limited, especially on high-end workstations. Fortunately, in these cases a higher resolution mode should be available, but it's true to say that just because we have 8 bits per pixel in a framebuffer, it doesn't mean we can successfully allocate 256 colours. For X, the use of a private colourmap solves most of the problem with colour allocation... but the point here is that that whilst we did support 8 bpp modes previously, we didn't support use of all 256 colours, so care is needed.

    [3] My thinking is that for consistency across the palette, all colour components must be handled in the same manner. Standard Spectrum colours of low intensity with the blue bit set are not representable in the ULAplus palette, but all others are representable. The ULAplus extends each two bit blue intensity value to a three bit value by copying the least significant bit. For low intensity to be representable as a three bit value, but not as an extended two bit value, bits 0 and 1 of the three bit value must differ. As Goldilocks might say, 0b001 and 0b010 are too dark, and 0b110 is bright and 0b101 is 'just right'.

    [4] Fuse uses 0xbf or 0xc0 for low intensity in its existing palettes, and 0b101/0b111 is nearest to 0xb6/0xff, so Fuse's existing palette would be slightly too bright for use with the ULAplus.

    [5] The SAM's video modes are:

    • MODE 1, the standard Spectrum mode.
    • MODE 2, the same as the Timex 8×1 pixel attributes mode.
    • MODE 3, high resolution (512×192) linear 2 bpp mode.
    • MODE 4, standard resolution (256×192) linear 4 bpp mode.

    The SAM's colour lookup tables are used in all modes, so some extra complexity is added even for MODEs 1 and 2.

     
  • Stuart Brady

    Stuart Brady - 2014-04-04

    Oh, I forgot to mention, whereas the ULAplus uses an 8×8×4 colour cube, the SAM uses a 4×4×4×2 cube, with the final '2' being a half-bit of brightness applied to all three colour components. This results in 4×4×4 colours (totalling 64) being common to both the ULAplus and SAM.

    Specifically, the common colours are those on the SAM where the bright bit matches the least significant blue bit (which the ULAplus will copy when extending to three bits), and those on the ULAplus where the low intensity bits of red, green (3-bits each) and blue (2-bits, extended to three) all match.

    I'm not sure how much of a difference this information makes, but I'm providing it in case we need to plan palette handling on 8-bit displays in a way that suits both palettes, as neither is a subset of the other.

    Sadly, six of the Spectrum's standard colours aren't representable on the SAM (those with the bright bit, with the exception of bright black and bright white) as the SAM's half-bright bit applies even to components that would otherwise have no intensity... but since a SAM palette would only require 128 colours, this should hopefully not impose a problem.

     
  • Sergio Baldoví

    Sergio Baldoví - 2014-04-05

    Stuart, thank you for the extensive feedback.

    I take it from my brief scan of the code[1] that we don't intend to support use of all 260 colours on screen at once?

    It is supported but has not been properly tested. ULAplus can only handle 256 colours in palette mode. There are 4 standard colours that cannot be palettized, so a frame could show upto 260 colours on screens that are part standard mode / part palette mode. Currently we keep two palettes.

    For one thing, we'd have to modify certain UIs to drop support for 8 bpp[2] and even 4 bpp framebuffers! We could always leave support for simultaneous palettes unimplemented in these modes, though, or even settle for colours that are a little bit off.

    Philip once suggested to deprecate fb, svga and xlib UIs. I would not put too much effort into limited colour depths.

    The way that the ULAplus renders the standard 16 Spectrum colours doesn't seem to be well documented. The high intensity values must surely map to 0b111 and I assume that low values map to 0b101, as this fits with the fact that standard Spectrum colours with low intensity blue cannot be represented in the ULAplus's 8×8×4 colour cube[3] and it's about the right level of intensity[4]. Still, it'd be nice to have this confirmed.

    Indeed. If things are not detailed then are open to interpretation. It is worth to look at the Miguel Ángel Rodriguez' implementations. There is an old verilog core at opencores.org and a new development for ZX-Uno. Fortunately, the later is well documented:

    // LUT-based translatator from IGRB to 9-bit GRB
    `define none 3'b000
    `define half 3'b101
    `define full 3'b111
    

    When using the standard Spectrum attributes mode of the ULAplus, we should probably use exactly the colours that would be used by the ULAplus, rather than the original Spectrum palette, even if the differences are fairly minor.

    I'm a bit confused. If you are referring to the 332-GRB equivalents, that would make 260 colours impossible! IMO the ULAplus datapath show two different palettes.

    [4] Fuse uses 0xbf or 0xc0 for low intensity in its existing palettes, and 0b101/0b111 is nearest to 0xb6/0xff, so Fuse's existing palette would be slightly too bright for use with the ULAplus.

    I have always thought that Fuse was idealizing the standard colours as every TV set might alter the colours outputted by the ULA. So the whole point is that standard colours could mix better with ULAplus colours in the same screen?

    Maybe Fuse should allow to load a custom standard palette. If I remember correctly, Chris Smith once stated that the ULA cannot output the full bright white as (255,255,255).

    At some point I would like to finish and merge my patches for SAM Coupé emulation as we discussed some time previously. If you can, it'd be worth keeping the changes I will need to make in mind when adding ULAplus support[5]. Whilst we'll never need to to make use of the ULAplus and SAM palettes simultaneously, certain things such as palette handling would be best handled in a generic manner, to avoid duplication of effort.

    I agree. There is a SPECTRA interface with some exotic screen modes too. Fuse has the standard palette replicated in the UIs, screenshot.c and the SVG patch proposal. The palette handling should be as generic as possible.

    I'm not quite sure whether generation of the ULAplus palette is correct:
    green = ( green << 5 ) | ( green << 2 ) | ( green & 0x03 );
    red = ( red << 5 ) | ( red << 2 ) | ( red & 0x03 );
    blue = ( blue << 5 ) | ( blue << 2 ) | ( blue & 0x03 );
    Whilst this is what is written on the ULAplus web site, I find this somewhat perplexing. The lowest intensity value for each component is 0b00100101 (0x25). The next values are double this (0x4a), and then triple (0x6f). Then we get 0b10010000 (0x90). It seems 0x90 − 0x6f = 0x21. This is then followed by three more intervals of 0x25, giving deltas of 0x25, 0x25, 0x25, 0x21, 0x25, 0x25, 0x25.

    Sounds interesting. I've checked how the blue component is scaled from 2 to 3 bits. Surprisingly, Fuse is not doing the same scaling than ZX-Uno:

    // 332-GRB to 333-GRB (blue turns from B1 B0 into B1 B0 B1)
    wire [8:0] ULAplus9bitColour = {ULAplusPixelColour,ULAplusPixelColour[1]};rgbi pal
    

    That would give deltas of 2-3-2 instead of 3-1-3 for when scaling the blue component to 3 bits (i.e., 0b000, 0b010, 0b101, 0b111)

    The scheme that I would have imagined is this:
    green = ( green << 5 ) | ( green << 2 ) | ( green >> 1 );
    red = ( red << 5 ) | ( red << 2 ) | ( red >> 1 );
    blue = ( blue << 5 ) | ( blue << 2 ) | ( blue >> 1 );
    This gives us 0b00100100 (0x24), followed by 0b01001001 (0x49), 0b01101101 (0x6d), 0b10010010 (0x92), 0b10110110 (0xb6), 0b11011011 (0xdb) and 0b11111111 (0xff), with deltas of 0x24, 0x25, 0x24, 0x25, 0x24, 0x25 and 0x24. Much more of a consistent spread.

    You are suggesting that a hmlhmlhm conversion pattern instead of hmlhmlml would give a smooth palette for green and red components, isn't it?

    For the blue component, the hmlhmlhm pattern gives 0b01001001 (0x49), 0b10110110 (0xB6) and 0b11111111 (0xFF), with deltas of 0x49, 0x6D and 0x49. Whereas the hmlhmlml pattern gives 0b01001010 (0x4A), 0b10110101 (0xB5) and 0b11111111 (0xFF), with deltas of 0x4A, 0x6B and 0x4A. That's pretty similar.

    I should check that with Miguel Angel or Andrew.

    Does ULAplus implementations actually extend to eight bits in the way that's described, or should we instead implement the palette that I've described, as it's a better extension of a three-bit value into eight bits.

    It seems out of scope for ULAplus. The ULA output 333-RGB:

    // Final stage. Final colour is connected to PAL generator
    always @* begin
       if (ULAplusEnabled) begin
          gi = ULAplus9bitColour[8:6];
          ri = ULAplus9bitColour[5:3];
          bi = ULAplus9bitColour[2:0];
       end
       else begin
          gi = Std9bitColour[8:6];
          ri = Std9bitColour[5:3];
          bi = Std9bitColour[2:0];
       end
    end
    

    So the question is how a Spectrum should output 333-RGB colours?

    I would try to share the palette setup code between displays, if possible, including generation of a greyscale palette from a black and white palette. I don't see any reason to doing this in avoid two passes — an emulation-specific function (one for Spectrum ULA, one for ULAplus, one for SAM) one to generate the colour palette, and then a common function for generating a greyscale palette from a colour one. I appreciate this was a problem with the existing code, but this is only going to get worse if/when we add SAM emulation.

    I don't fully understand you. The ULAplus colour cube is generated with the pixel format needed by the UI (e.g., x8b8g8r8 or x8r8g8b8 for GTK UI). If we could standardize a ui_map_rgb32() maybe we could have a common setup.

    There's a particular bit of code I would suggest rewriting:
    green = ( i & 0xe0 ) >> 5;
    red = ( i & 0x1c ) >> 2;
    blue = ( ( i & 0x03 ) << 1 ) | ( i & 0x01 );

    I would write instead:
    green = ( i >> 5 ) & 7;
    red = ( i >> 2 ) & 7;
    blue = ( ( i & 3 ) << 1 ) | ( i & 1 );
    The constants are then immediately obvious in their purpose when written this way. It's also not worth thinking of these values as 8-bit quantities. Indeed, 0x03 would still be of type 'int', and i is an unsigned int, so the prefixing of a 0 just seems misleading somehow. '0x' is fine, but I probably wouldn't bother. As far as the type of i is concerned, does it need to be unsigned for some particular reason? I'd probably just use 'int'.

    Sounds good.

    Lastly:
    ink2 = attr & 0x07;
    paper2 = ( attr & 0x38 ) >> 3;
    base = ( attr & 0xc0 ) >> 2;

    I would write instead:
    ink2 = attr & 7;
    paper2 = ( attr >> 3 ) & 7;
    base = ( ( attr >> 6 ) & 3 ) << 4;

    Sounds good too.

     
    • Stuart Brady

      Stuart Brady - 2014-04-05

      It is supported but has not been properly tested. ULAplus can only handle 256 colours in palette mode. There are 4 standard colours that cannot be palettized, so a frame could show upto 260 colours on screens that are part standard mode / part palette mode. Currently we keep two palettes.

      My concern was that we shouldn't switch between those two palettes in the UI, but instead, they should both be active at once. Switching between colour and greyscale palettes at the UI level is more acceptable, although I'm not sure whether it's a better to have the widget UI be in greyscale or not if the 'black and white TV' option is enabled — currently it is.

      Philip once suggested to deprecate fb, svga and xlib UIs. I would not put too much effort into limited colour depths.

      Agreed, although if we could keep them without putting much effort in, that might be a nice thing. I'm not sure what sort of hardware Fuse requires these days, but probably something with a Pentium, where a 640×480 16-bpp mode or better is likely to be available, but then the only 64-bit big-endian hardware that I have has an 8-bpp display.

      Leaving low bit depths unsupported does raise awkward questions about what to do UI-wise should Fuse be run on such a system. Rather than try to resolve these questions, it'd probably be easier to disable ULAplus emulation at runtime, or have only on of the two ULAplus palettes active at once. If we do this, we may as well keep the fb, svga and xlib UIs unless we're likely to make major changes that would risk breaking them.

      Indeed. If things are not detailed then are open to interpretation. It is worth to look at the Miguel Ángel Rodriguez' implementations. There is an old verilog core at opencores.org and a new development for ZX-Uno. Fortunately, the later is well documented:

      // LUT-based translatator from IGRB to 9-bit GRB
      `define none 3'b000
      `define half 3'b101
      `define full 3'b111
      

      I'm only going on what's stated in the ULAplus article in the Sinclair FAQ Wiki — unfortunately, it doesn't state its source, if there even is a source! It seems this was transferred across from the Spectrum Tech Wiki, and actually changed from BbB to Bbb at one point.

      We should consider the possible values here. Under the Bbb scheme: 0b000 (0), 0b011 (3), 0b100 (4), 0b111 (7). Under the BbB scheme: 0b000 (0), 0b010 (2), 0b101 (5), 0b111 (7). I don't quite see how 'the jump in intensity happens in the lower range where it is less noticeable', when going from four to seven is just as much of a jump. I couldn't say which is the better scheme, though.

      Under the BbB scheme, all sixteen Spectrum colours would be representable in the ULAplus palette, as 0b000, 0b101 and 0b111 all fit the pattern. Is the specification finalised? Do we even know which scheme people are designing art for? Also, what do implementations besides ZX-Uno (which you describe below as expanding to BbB) use?

      I'm a bit confused. If you are referring to the 332-GRB equivalents, that would make 260 colours impossible! IMO the ULAplus datapath show two different palettes.

      Having two palettes defined is fine — I guess you're saying the ULAplus defines a 8×8×4 colour cube, and then a fixed palette of the 16 standard Spectrum colours, and you want these as two separate arrays.

      The ULAplus FAQ states that there can be 'a total of 260 colours on screen' and the Sinclair FAQ Wiki page states 'This gives access to a fixed half the potential 512 colour palette' regarding the 8×8×4 cube. My interpretation of this is that the ULAplus maps twelve of the standard Spectrum colours to exact matches in the 8×8×4 cube, and that the remaining four colours map to exact matches in the potential 8×8×8 cube.

      If true, this means we should use the exact RGB values for 'Spectrum' mode as defined by the ULAplus specification. That is to say, 0xb5 or 0xb6 rather than 0xc0, assuming we use 0b101 as our intensity level. Otherwise, colours that are supposed to look the same would look different, by as much as 4% luminosity. I've checked, and the eye is sensitive enough to easily tell the difference between these two shades when adjacent, or when switching the screen between the two shades.

      That said, I'm all in favour of reusing a single standard Spectrum palette array, but with different values set for different machines, if using a single array improves performance or simplifies the code.

      I kinda guessed that this was your thinking anyway, but just something you hadn't gotten around to, as getting the rasterising working and with accurate timings is more of a priority.

      FWIW, I did make a start on using a palette calculated from resistor values of the original ULA. The 128K models would use a different palette, although perhaps this is close enough to the ideal not to be worth bothering with. There's also the TK90X family to consider, for which I intend to add emulation, and the various eastern European clones such as the Pentagon and Scorpion. If we should ever decide to allow the use of a user-defined palette, I would expect this not to apply under ULAplus emulation.

      I have always thought that Fuse was idealizing the standard colours as every TV set might alter the colours outputted by the ULA. So the whole point is that standard colours could mix better with ULAplus colours in the same screen?

      Although as far as I'm aware, CRTs at least from the 80s onwards are fairly accurate in their colour reproduction. The real problem is that given the Spectrum's non-standard video signal, especially in the earlier issues, different TVs could react in different ways.

      You're right, my point is that standard colours when emulating ULAplus should relate to colours in ULAplus mode in the same way that they would do with real hardware. The twelve standard Spectrum colours that are available in ULAplus mode should appear unchanged when switching to Spectrum compatibility mode.

      This does not mean that we should necessarily adopt the ULAplus palette as Fuse's idealised palette, as we should retain some flexibility in that regard, and it's best that the standard Spectrum colours in ULAplus are explicitly defined in terms of the ULAplus's hypothetical 8×8×8 colour cube. We shouldn't use colours that fit into the ULAplus's scheme 'by coincidence' — although to be honest, I guess all that really means is there should at least be a comment making it clear that accurate ULAplus emulation relies on the standard Spectrum colours being left as we define them.

      Sounds interesting. I've checked how the blue component is scaled from 2 to 3 bits. Surprisingly, Fuse is not doing the same scaling than ZX-Uno:

      // 332-GRB to 333-GRB (blue turns from B1 B0 into B1 B0 B1)
      wire [8:0] ULAplus9bitColour = {ULAplusPixelColour,ULAplusPixelColour[1]};rgbi pal
      

      That would give deltas of 2-3-2 instead of 3-1-3 for when scaling the blue component to 3 bits (i.e., 0b000, 0b010, 0b101, 0b111)

      Yeah, the 'jump in intensity' under the 3-1-3 scheme, and the lack of any real jump between 01 and 10... that does seem undesirable.

      I understand the analogy you're drawing between the 2-bit to 3-bit expansion and 3-bit to 8-bit expansion, and believe it to be a correct one to draw. I'll get back to this further down, but as an aside, I'd recommend that we consider 2-bit to 3-bit and 3-bit to 8-bit expansion as two separate (although related) topics. In particular, 3-bit to 8-bit expansion should not attempt to 'undo' the effect of 2-bit to 3-bit expansion in any way.

      Favouring expansion from Bb to BbbBbbBb over BbbBbbbb so as to produce a closer match to the original Bb (i.e. BbBbBbBb by convention) would be madness, even if I do favour the scheme for other reasons. I feel the aim here should be to maintain consistency across the whole 8×8×4 cube, rather than to pay the blue component any special attention. We might find that images converted to the ULAplus would slightly appear better if their conversion didn't properly take into account 2-bit to 3-bit expansion, but we would not be emulating the ULAplus's palette as accurately as we could.

      You are suggesting that a hmlhmlhm conversion pattern instead of hmlhmlml would give a smooth palette for green and red components, isn't it?

      Yes, that is my suggestion.

      For the blue component, the hmlhmlhm pattern gives 0b01001001 (0x49), 0b10110110 (0xB6) and 0b11111111 (0xFF), with deltas of 0x49, 0x6D and 0x49. Whereas the hmlhmlml pattern gives 0b01001010 (0x4A), 0b10110101 (0xB5) and 0b11111111 (0xFF), with deltas of 0x4A, 0x6B and 0x4A. That's pretty similar.

      You're right, it's very minor. Even so, if 8-bit expansion is not a part of the specification, I would use hmlhmlhm instead as it gives a better set of deltas. At the very least, there should be a comment in the source explaining that this is not in error, if we decide to keep it this way.

      I wouldn't restrict yourself to looking at the blue component, here. The more severe delta is between 0b0111 and and 0b1000. For hmlhmlml, the sequence of deltas is 37-37-37-33-37-37-37, whereas for hmlhmlhm it is 36-37-36-37-36-37-36.

      I would also say that hmlhmlhm is more easily understandable, whereas hmlhmlml stood out to me as looking like a minor bug, leading me to think 'is there something I'm not getting here?'

      I should check that with Miguel Angel or Andrew.

      Thanks!

      It seems out of scope for ULAplus. The ULA output 333-RGB:
      [...]
      So the question is how a Spectrum should output 333-RGB colours?

      Fair enough. I presume for any hardware 48K ULA implementation, there must be /Y, U and V outputs, and there will be no internal extension to eight bits. In that case, we should ignore the recommendation to use hmlhmlml and go for the scheme that produces the better output, which is hmlhmlhm IMO.

      I don't fully understand you. The ULAplus colour cube is generated with the pixel format needed by the UI (e.g., x8b8g8r8 or x8r8g8b8 for GTK UI). If we could standardize a ui_map_rgb32() maybe we could have a common setup.

      Right, that's exactly what I mean.

      We'd divide idealised and ULAplus palette setup into three parts:

      1. Generate the palette as a tuple of red, green and blue values.
      2. Convert the colour palette to greyscale.
      3. Convert palettes to packed format needed by display code.

      Parts one and two need not even belong in the UI code.

      For the standard Spectrum's ULA, we might actually generate the colour palette from the black and white palette (well, sort-of!) so it's worth keeping some flexibility, especially if we add controls for the black level (i.e. 'brightness'), contrast and saturation at some later stage.

       
    • Stuart Brady

      Stuart Brady - 2014-04-05

      Addressing this separately as it largely concerns emulation of the standard ULA.

      I'll raise a separate ticket for the standard ULA emulation at some stage, and copy and paste much of what I say below into that ticket.

      Maybe Fuse should allow to load a custom standard palette. If I remember correctly, Chris Smith once stated that the ULA cannot output the full bright white as (255,255,255).

      The original Spectrum's ULA has separate circuits for outputting Y, U and V signals and the 128K ULA outputs digital red, green, blue and bright signals which are then encoded externally. The hues of all machines are fairly accurate from what I recall — the main quirk is that the dark blue component is brighter than it ought to be on the original Spectrum's ULA, although this does not compromise the hue. On a black and white TV, this yields a more or less linear increase in luminosity, with the exception of magenta and green, which are closer together than you would otherwise expect.

      For Colour TVs, values may be clamped as they fall outside of the range permitted by BT.601. For Fuse, values would need to be clamped, they tend to fall outside of the RGB colour space when reasonable levels of saturation and contrast are applied. Bright yellow and bright cyan are especially badly affected, followed by bright green and bright magenta.

      I can provide a table of /Y, U and V outputs for 5C ULAs and another for 6C ULAs, which we could potentially use to generate a palette at run-time, given a specification of ULA, and brightness and contrast settings. There's a fair amount of analogue circuitry between the ULA and the LM1889, which we'd need to model — I would need some help figuring out what's going on here, although it's probably not much of a big deal to anyone used to dealing with analogue electronics. I would probably only bother emulating the 6C ULA with an issue 6A board, though.

      The figures that I have for /Y indicate that Fuse greatly overestimates the amount of difference that the bright bit makes. The actual values would be closer to 0xe0 and 0xff than 0xb0 and 0xff. Given the number of times I've seen monitors set such that #f0f0f0 and #ffffff are indistinguishable, though (backlights often aren't bright enough, perhaps?), I'd be tempted to use 0xd0 and 0xf0 in the idealised palettes, though, or make this an option.

      This doesn't really fit with my recollection of bright white being very bright, though. (Brighter than I can achieve with any modern LCD screen, I should add!) Granted, we did turn contrast up quite high on our TV, and perhaps we turned brightness down quite some way. The settings on my TV are hardly of what's interest here, though.

      If the figures I have are right, though, then this would have implications for the ULAplus, where 0b110 would would be a better match for low intensity components of the original Spectrum palette. Neither the 'Bb to Bbb' nor 'Bb to BbB' scheme for 2-bit to 3-bit extension would be able to handle this case.

      For black and white TV emulation, it's probably best to avoid converting from Y to a clamped RGB space and then back to Y again, as I'm fairly confident that black and white TVs will ignore U and V entirely, although we would still implement brightness and contrast controls.

       
  • Sergio Baldoví

    Sergio Baldoví - 2014-04-10

    Andrew wrote:

    The way that the ULAplus renders the standard 16 Spectrum colours doesn't seem to be well documented. The high/full intensity values must surely map to 0b111 and I assume that low/half values map to 0b101.

    It's not documented because when the palette mode is not in use it renders the standard 16 Spectrum colours exactly the same way as the normal ULA. Which is to say, it bypasses the palette system entirely. This means that technically you can get 263 colours on screen at once by cycling and switching the palette, because the palette does not contain an exact match for the non-BRIGHT colors.

    The Sinclair FAQ Wiki states that the 2-bit to 3-bit expansion for blue component should use the Bbb pattern (Bb becomes Bbb). At some point, the Spectrum Tech Wiki changed BbB pattern to Bbb. ZX-Uno currenty use BbB.

    The only difference is whether 10h becomes 100h (4d) or 101h (5d). Miguel is doing the hardware for the real ULAplus and the ZX-Uno. I know he changed the scheme on the ULAplus because he found it looked better so I'm assuming it's the same on ZX-Uno. As these are the real hardware implementations I would follow these examples and use BbB. But it doesn't actually make that much of a difference.

    The Sinclair FAQ Wiki suggest the use of hmlhmlml pattern when scaling 3-bits of colour data to 8-bits, that gives 0x25, 0x25, 0x25, 0x21, 0x25, 0x25 and 0x25 increments of intensity. The hmlhmlhm pattern would give a smoother palette for green and red components with 0x24, 0x25, 0x24, 0x25, 0x24, 0x25 and 0x24 increments of intensity. It seems that there isn't any circuitry that do 3-bit to 8-bits scaling, so is the hmlhmlml pattern just a reference?

    It's just a reference. It might be worth looking at what the MSX2 does. It has a VDP that can do 9-bit RGB so it's also doing an 8-bit to 9-bit translation to give half the palette. The other thing to remember is that ULAplus uses GRB values (which the original ULA also uses). This has the neat side effect of giving 256 intensity levels from 0 to 255 when you enable B/W television mode. I really should have put a OUT in the design to disable the color, but I was trying to keep it simple.

    The other thing to note is that ULAplus as implemented by Miguel also gives access to the Timex modes, even on a 48K machine. And on a 128K machine you get the four screen areas, as on the Spectrum SE. This is important because the HAM8x1 viewer is written for 48K timings and won't actually work on the Timex models. Writes to port #FF are handled as per the Timex for selecting video modes but reads still return the floating bus.

     
  • Sergio Baldoví

    Sergio Baldoví - 2014-04-10

    My concern was that we shouldn't switch between those two palettes in the UI, but instead, they should both be active at once. Switching between colour and greyscale palettes at the UI level is more acceptable, although I'm not sure whether it's a better to have the widget UI be in greyscale or not if the 'black and white TV' option is enabled — currently it is.

    My concern is that display routines try to store the status (pixel, attribute, screen mode, colour) in 32 bits. That will be a difficult task with more than 256 indexed colours.

    If true, this means we should use the exact RGB values for 'Spectrum' mode as defined by the ULAplus specification. That is to say, 0xb5 or 0xb6 rather than 0xc0, assuming we use 0b101 as our intensity level. Otherwise, colours that are supposed to look the same would look different, by as much as 4% luminosity. I've checked, and the eye is sensitive enough to easily tell the difference between these two shades when adjacent, or when switching the screen between the two shades.

    Sounds reasonable. If signals for both standard and ULAplus colours are 9-bit GRB, then colours should use the same conversion and match.

    Under the BbB scheme, all sixteen Spectrum colours would be representable in the ULAplus palette, as 0b000, 0b101 and 0b111 all fit the pattern.

    Indeed. Andrew is assuming that low values are not necessarily 0b101, Spiffy uses 0xc8, Jspeccy 0xc0 and SpecEmu 0xb2

    Also, what do implementations besides ZX-Uno (which you describe below as expanding to BbB) use?

    ZX-Badaloc Reloaded use a FPGA Board (Nexys2) that supports GGGRRRBB output.

    Both JSpeccy and Spiffy use Bbb scheme and hmlhmlml conversion pattern. SpecEmu use Bbb scheme with some odd levels:

    Green/Red
    0b001 -> 0b00100110 (hmlhml??)
    0b010 -> 0b01001010 (hmlhmlml)
    0b011 -> 0b01110000 (hml?????)
    0b100 -> 0b10001111 (hml?????)
    0b101 -> 0b10110101 (hmlhmlml)
    0b110 -> 0b11011001 (hmlhml??)
    0b111 -> 0b11111111 (hmlhmlml)
    
    Blue
    0b01 -> 0b011 -> 0b01110000 (hml?????)
    0b10 -> 0b100 -> 0b10001111 (hml?????)
    0b11 -> 0b111 -> 0b11111111 (hmlhmlml)
    

    Some aspects seem dependent on the implementation. I would pick:

    • BbB scheme for 2-bit to 3-bit expansion. It is likely to be used by the ULA replacement. Allow to map low intensity blue.
    • hmlhmlhm pattern for 3-bit to 8-bit expansion. It has a uniform distribution close to ideal 255/7 level.
    • Map low values from standard palette to 0b101 (when ULAplus is attached). All standard colours are representable in the ULAplus palette. There are 256 distinct colours.
     
  • Stuart Brady

    Stuart Brady - 2014-04-13

    At http://map.grauw.nl/articles/vdp_guide.php, it's explained that the V9938 maps colours in 'graphics 7' mode (aka MSX mode 8) as follows: 0→0, 1→2, 2→4, 3→7.

    I would perhaps have recommended the same mapping as compatibility with the V9938 colour scheme can only be a good thing. If 4/7 is too dark for components of low intensity colours in the original Spectrum palette, we would need a combined palette of 260 colours, which is unfortunate, but not disastrous. I guess we need to ask Andrew?

    As far as the '263 entry' palette is concerned, I'm really puzzled now. Does he really mean that all 7 of the low intensity non-black colours would not necessarily fit into the ULAplus mode's 256 colour palette, and not just the four with a blue component? If so, that's fine, but it contradicts the ULAplus FAQ, and existing implementations seem to pick values in the 512-colour space that we discussed previously.

     

    Last edit: Stuart Brady 2014-04-13
  • Sergio Baldoví

    Sergio Baldoví - 2014-04-13

    At http://map.grauw.nl/articles/vdp_guide.php, it's explained that the V9938 maps colours in 'graphics 7' mode (aka MSX mode 8) as follows: 0→0, 1→2, 2→4, 3→7.

    Good find. That exposes a big argument against Bbb scheme:

    [...] because of this, gray colours can be really gray and not blue-ish or yellow-ish teints.
    

    That is noticeable in the gray row. Both BbB and MSX2 schemes fix these tints.

    Both openMSX and blueMSX use 255/7 intensity levels (a.k.a. hmlhmlhm pattern)

    Both openMSX and blueMSX apply gamma correction to the RGB values.

    I would perhaps have recommended the same mapping as compatibility with the V9938 colour scheme can only be a good thing.

    The palette with MSX2 scheme is less blue-ish than with BbB scheme. I wonder if that fits better with the aim of GGGRRRBB format and the human eye perception.

    As far as the '263 entry' palette is concerned, I'm really puzzled now. Does he really mean that all 7 of the low intensity non-black colours would not necessarily fit into the ULAplus mode's 256 colour palette, and not just the four with a blue component? If so, that's fine, but it contradicts everything else anybody's said or implemented, including the ULAplus spec and FAQ!

    I suppose there could be emulators that use more than 3 bits to represent a component of a standard colour?

     
  • Stuart Brady

    Stuart Brady - 2014-04-14

    I'd say that the Bbb scheme has had more than just a few holes shot into it. openMSX and blueMSX's use of hmlhmlhm supports our use of it — I would push to get the ULAplus spec changed in that regard. Given that it's on the SinclairFAQ Wiki, I imagine we can just do it ourselves if Andrew agrees.

    The ULAplus specification would be completely free to specify whatever behaviour its authors wanted for rendering of the standard 16 colours, though, and I suppose 'as close as possible to the original' makes sense — but which machine? I guess the intent is that people shouldn't switch display modes on the fly to mix graphical content using the two palettes, and expect any real consistency between the two.

    Looking into this in more detail, particularly in some of Richard Atkinson's videos (at http://youtu.be/94Ywx6uVn9E and http://youtu.be/UX4Z9dKKqpE), it seems there the TS2068 rendered bright black differently to dark black — which is highly noticeable on a lot of games — and there's a more pronounced difference between bright blue and dark blue, noticeable especially in Jet Set Willy, in the screens 'Emergency Generator', 'I'm sure I've seen this before', 'On a Branch Over the Drive' where it affects the guardians, and to a lesser extent, 'Cold Store' (where it affects the fixed nasties).

    More specifically, bright blue on the TS2068 includes a certain level of the red and green components, and bright black shows as dark grey. All other bright colours appeared slightly washed out in Richard's videos.

    This leads me to believe that we should really have separate colour palettes for the 16K/48K and the 128K, as it seems the difference between bright blue and dark blue is less perceptible than it is under many emulators.

     
  • Stuart Brady

    Stuart Brady - 2014-04-14

    FWIW, the 9-bit effective values that are used by the SAM Coupé ROM on startup:

           9-bit   ( SAM Coupé CLUT index )
         GgIRrIBbI (  GRBIgrb    Hex   Dec  User guide desc.)
     0 0b000000000 (0b0000000 = 0x00 =   0  pitch black     )
     1 0b000000100 (0b0010000 = 0x10 =  16  navy blue       )
     2 0b000100000 (0b0100000 = 0x20 =  32  Burgundy        )
     3 0b000100100 (0b0110000 = 0x30 =  48  woad            )
     4 0b100000000 (0b1000000 = 0x40 =  64  grass           )
     5 0b100000100 (0b1010000 = 0x50 =  80  cold steel      )
     6 0b100100000 (0b1100000 = 0x60 =  96  damp straw      )
     7 0b101101101 (0b1111000 = 0x78 = 120  turnip          )
     8 0b000000000 (0b0000000 = 0x00 =   0  pitch black     )
     9 0b000000110 (0b0010001 = 0x11 =  17  peacock         )
    10 0b000110000 (0b0100010 = 0x22 =  34  hellfire        )
    11 0b000110110 (0b0110011 = 0x33 =  51  amethyst        )
    12 0b110000000 (0b1000100 = 0x44 =  68  lime            )
    13 0b110000110 (0b1010101 = 0x55 =  85  turquoise       )
    14 0b110110000 (0b1100110 = 0x66 = 102  cowardice       )
    15 0b111111111 (0b1111111 = 0x7f = 127  moonlight       )
    

    'I' signifies extra intensity applied to all three components. Capital R, G and B signify high intensity red, green and blue, respectively. Lower case r, g and b signify normal intensity for the respective components.

    So components of normal intensity are 0b100 and bright components are 0b110, with the exception of bright white, which has the intensity bit set to become 0b111, and normal white, which has the intensity bit set to become 0b101.

    It makes perfect sense that the intensity bit should be clear for anything but shades of white/grey, but it's interesting that it's set for low intensity white. This kinda makes me wonder whether they'd have preferred 0b101 and 0b111 if not for the fact that inactive components in the Spectrum palette would then translate to 0b001.

    Interestingly palette entry 5 (normal cyan) is documented in the SAM Coupé technical manual as being set to colour 18 (0x12) by default, but this is 'dark purple' so that's obviously in error. I have checked the values above and they match the source code for ROM 3.0 (see INITCOLS in ROM 1).

     

    Last edit: Stuart Brady 2014-04-14
  • Fredrick Meunier

    Sergio writes:

    The HAM8x1 demo shows similar glitches to HAM256 viewer. It seems that some cells take the colour from the second lower row.

    Hmmm, I have been snowed under but am still hopinh to have a look at the latest patches "soon".

    Sergio writes:

    Philip once suggested to deprecate fb, svga and xlib UIs. I would not put too much effort into limited colour depths.

    I also think this would be fine.

    Stewart writes:

    it seems there the TS2068 rendered bright black differently to dark black — which is highly noticeable on a lot of games

    I can confirm this, it is the same on my TS2068.

     
  • Sergio Baldoví

    Sergio Baldoví - 2014-04-23

    Miguel Angel wrote:

    (About blue-ish or yellow-ish tints in grey shades) I once saw that in my 1084 monitor, but I was aware that it is impossible to get 8 grey shades because blue is not balanced with green/red in half the cases.

    I never thought about choosing a combination of colours to get a better looking image. I tried to get blue levels as much linear as possible. Since red and green components have a linear scale, I thought that blue should behave the same way.

    BbB pattern comes from a mathematical proof: it is possible to (linearly) scale an unsigned value from N bits to N+ḱ bits by repeating the original value to the right as many times as needed until completing N+K bits, e.g., scaling a value from 3 bits to 7 bits would be ABC->ABCABCA and scaling from 2 bits to 3 bits would be AB -> ABA.

    Linearity is only preserved when destination bits (N+K) are multiple of N, i.e., 2 bits to 4 /6/8 bits or 3 bits to 6/9/12 bits. In the case of 2 bits to 3 bits, linearity can't be preserved and we will get 4 "true" grey levels and 4 "tinted" grey levels. This is the reason why I chose BbB pattern for ZX-Uno. But now I'm reconsidering my implementation...

    As you are using an emulator with 8 bits per component, I've thought that another option is scaling from 2 to 8 bits for blue component and 3 to 8 bits for red/green components. This is equivalent to a hardware 3-bits DAC for red/green components and 2-bits DAC for blue component.

    I've written a program to compare these methods when scaling ULAplus to PC colours:

    R | G | B | R8 | G8 | B8 | B28 | B238 | B238' | E1 | E2 | E3
    --|---|---|----|----|----|-----|------|-------|----|----|---
    0 | 0 | 0 |   0|   0|   0|   0 |    0 |     0 |  0 |  0 |  0
    1 | 1 | 0 |  36|  36|  36|   0 |    0 |     0 | 36 | 36 | 36
    2 | 2 | 1 |  73|  73|  73|  85 |   73 |   109 | 12 |  0 | 36
    3 | 3 | 1 | 109| 109| 109|  85 |   73 |   109 | 24 | 36 |  0
    4 | 4 | 2 | 146| 146| 146| 170 |  182 |   146 | 24 | 36 |  0
    5 | 5 | 2 | 182| 182| 182| 170 |  182 |   146 | 12 |  0 | 36
    6 | 6 | 3 | 219| 219| 219| 255 |  255 |   255 | 36 | 36 | 36
    7 | 7 | 3 | 255| 255| 255| 255 |  255 |   255 |  0 |  0 |  0
    
    • R, G and B are values generated by ULAplus.
    • R8 and G8 are values scaled from 3 to 8 bits.
    • B8 is the 8 bits value for blue, assuming that ULAplus would have 3 bits for blue instead of 2 bits. This is the ideal reference.
    • B28 is the 8 bits value for blue, scaling from 2 bits to 8 bit.
    • B238 is the 8 bits value for blue, scaling from 2 bits to 3 bit by BbB pattern and later 3 bits to 8 bits.
    • B238' value is similar to B238 but using Bbb pattern.
    • E1,E2 and E3 are, respectively. the error of B28, B238 and B238' methods. This is the absolute difference between ideal value (B8) and real value.

    The B28 method has more errors (E1) than B238 and B238', but the difference is not greater, i.e., error distribution of E1 is more homogeneous than E2 and E3. Sincerely, not too much, the standard deviation is 2.5 times lower than E2 and E3.

    The B238 (E2) and B238' (E3) methods have the same magnitude errors in different levels.

    The B238' method have errors (E3) in the upper/lower extremes of the grey scale instead of the middle part. I wonder if these errors are concealed in the extremes.

    The B238 method distribute errors (E2) homogeneously, but there are errors in the middle part. The grey levels made with this blue levels will be different from the ideal grey and it is likely to be more visible than B238' method.

    I get grey levels using this formula:

    GREY = R*2989 + G*5870 + B*1140
    
    R | G | B | Grey id | Grey28  | Grey238 | Grey238'|    E1 |    E2 |    E3
    --|---|---|---------|---------|---------|---------|-------|-------|------
    0 | 0 | 0 |       0 |       0 |       0 |       0 |     0 |     0 |     0
    1 | 1 | 0 |  359964 |  318924 |  318924 |  318924 | 41040 | 41040 | 41040
    2 | 2 | 1 |  729927 |  743607 |  729927 |  770967 | 13680 |     0 | 41040
    3 | 3 | 1 | 1089891 | 1062531 | 1048851 | 1089891 | 27360 | 41040 |     0
    4 | 4 | 2 | 1459854 | 1487214 | 1500894 | 1459854 | 27360 | 41040 |     0
    5 | 5 | 2 | 1819818 | 1806138 | 1819818 | 1778778 | 13680 |     0 | 41040
    6 | 6 | 3 | 2189781 | 2230821 | 2230821 | 2230821 | 41040 | 41040 | 41040
    7 | 7 | 3 | 2549745 | 2549745 | 2549745 | 2549745 |     0 |     0 |     0
    

    Grey id: ideal grey, using 3 bits for blue like other components
    Grey28: grey with blue component scaled from 2 bits to 8 bits.
    Grey238: grey with blue component scaled from 2 bits to 3 bits (BbB) and later to 8 bits.
    Grey238': grey with blue component scaled from 2 bits to 3 bits (Bbb) and later to 8 bits.
    E1,E2 and E3 are the respective errors between the real grey and the ideal grey.

    I can deduce:
    - Bbb pattern does perfect greys in the middle of the scale and imperfect greys in the extremes. Imperfect greys in extremes are (maybe) less visible than in the middle part.
    - BbB pattern does imperfect greys in the middle of the scale and alternatively does perfect and and imperfect greys in extremes.

    If we compose two colour images with shades of grey generated by each method, and then we convert both images to grey, the image that comes from BbB pattern is likely to have a more linear scale than Bbb, i.e., luminance of BbB image are closer to ideal luminance.

    Grey levels generated by 2 to 8 bits method are erroneous in almost every level, except for white and black. Grey colour in some levels will be better or worse than grey produced by BbB or Bbb methods. If we compose a colour image with shades of grey generated by this method, and then converted it to grey, the grey scale will likely look quite linear (like the BbB case).

    The question I can't answer is:

    If a colour image with shades of grey with errors is converted to grey and the shades look homogeneous and linear, would be the original image nicer to the sight or closer to the ideal levels of grey? In order to get an image closer to the ideal levels of grey, would it be enough that errors are minimized in the middle part of the scale?

    Since errors are inevitable... Is it better to force blue-ish or yellow-ish tints?. i.e., Are human eyes more sensitive to blue-ish or yellow-ish tints?

     
  • Sergio Baldoví

    Sergio Baldoví - 2014-04-28

    Claus Jahn wrote:

    Could you confirm me how Image2ULAplus expand the blue component from 2-bits to 8-bits?

    It’s very easy. To convert the GGGRRRBB format from ULAPlus to real RGB (24 bit) you only have to transform the BB part to BB000000 or BB111111. I use the BB000000 method. This is calculated by (color and 3) shl 6.

     
1 2 3 > >> (Page 1 of 3)

Log in to post a comment.