Closed ped7g closed 2 months ago
I have another example of discrepancy between ZEsarUX v.X and real hardware, continuing on from the last.
Two nex files are compiled from the code below: layer2_experiment_s1.nex and layer2_experiment_s2.nex.
The former is obtained by uncommenting just the 2 nextreg
lines in /* Section 1 */
.
The latter is obtained by uncommenting just the 2 writes to the Layer 2 Access Port in /* Section 2 */
.
On real hardware both nex files produce visually-identical results, comprised of: (a) a 320x256-mode Layer 2 screen covered with a mostly random jumble of multicoloured pixels, except for a fushia-coloured stripe on the leftmost 1/5th, being the fill of the first 16KiB-bank of the display with byte 133. (b) a pale pink border, with no flashing. This pale pink is the transparency fallback colour hex CF, I believe. Please find attached a photo of this on a TV via HDMI. Unfortunately, a Layer 2 screenshot taken using the NMI yellow button only seems to assume 256x192 mode, so there the fushia stripe is horizontal and occupies 1/3rd of the screen.
On ZEsarUX v.X calling directly layer2_experiment_s1.nex: (a) an entirely black 320x256-mode Layer 2 screen. (b) a multicolour flashing border as in nex_experiment.nex, but smaller due to 320x256 Layer 2 being displayed rather than ULA.
On ZEsarUX v.X calling directly layer2_experiment_s2.nex: (a) a mostly-black 320x256-mode Layer 2 screen, with a fushia-coloured stripe on the left 1/5th, being the fill of the first 16KiB-bank of the display with byte 133. (b) a multicolour flashing border as in nex_experiment.nex, but smaller due to 320x256 Layer 2 being displayed rather than ULA.
I should add that further experimentation with Layer 2 start bank locations indicate that the Layer 2 display is indeed enabled in the _s1.nex case.
The code is as follows:
OPT --syntax=abfw --zxnext
;-------------------------------------------------------------------------------
DEVICE ZXSPECTRUMNEXT ; ZX Spectrum Next (8 slots, 224 8Ki-Banks = "pages" in sjasm-speak, slot size 0x2000 = 1.75MiB RAM)
; default banks map: [ 14, 15, 10, 11, 4, 5, 0, 1] = [7, 5, 2, 0] in 16KiB terms, like ZXSPECTRUM128
; nexload banks map: [255, 255, 10, 11, 4, 5, 2*entry_bank, 2*entry_bank + 1]
; default slot = 7 = $E000 - $FFFF
LABELSLIST "layer2_experiment.lbl"
MACRO PORT16BIT_WRITE port, value
ld A, value
ld BC, port
out (C), A
ENDM
; 8-bit Ports
ULA_CONTROL_PORT_WRITE EQU $FE
; 16-bit Ports
LAYER_2_ACCESS_PORT EQU $123B
; Next registers
LAYER_2_RAM_PAGE EQU $12
GLOBAL_TRANSPARENCY EQU $14
SPRITE_AND_LAYERS_SYSTEM EQU $15
LAYER_2_X_OFFSET EQU $16
LAYER_2_Y_OFFSET EQU $17
CLIP_WINDOW_LAYER_2 EQU $18
CLIP_WINDOW_CONTROL EQU $1C
MEMORY_MANAGEMENT_SLOT_0_BANK EQU $50
MEMORY_MANAGEMENT_SLOT_1_BANK EQU $51
MEMORY_MANAGEMENT_SLOT_2_BANK EQU $52
MEMORY_MANAGEMENT_SLOT_3_BANK EQU $53
MEMORY_MANAGEMENT_SLOT_4_BANK EQU $54
MEMORY_MANAGEMENT_SLOT_5_BANK EQU $55
MEMORY_MANAGEMENT_SLOT_6_BANK EQU $56
MEMORY_MANAGEMENT_SLOT_7_BANK EQU $57
ULA_CONTROL EQU $68
DISPLAY_CONTROL_1 EQU $69
TILEMAP_CONTROL EQU $6B
LAYER_2_CONTROL EQU $70
LAYER_2_X_OFFSET_MSB EQU $71
TRANSPARENCY_COLOUR_FALLBACK EQU $4A
;-------------------------------------------------------------------------------
; Reset values
SOFT_RESET_GLOBAL_TRANSPARENCY EQU $E3
;-------------------------------------------------------------------------------
; My stuff
MY_TRANSPARENCY_COLOUR_FALLBACK EQU $CF
MY_LAYER_2_STRIPE_COLOUR EQU 133
MY_LAYER_2_START_BANK16K EQU 77
;-------------------------------------------------------------------------------
ORG $4000 ; Start of slot 2, default page 10
entry:
call initialise_screens
cycle_border:
xor A ; A <- 0
.loop:
out (ULA_CONTROL_PORT_WRITE), A ; set border colour
inc A
bit 3, A
jr z, .loop
jr cycle_border
initialise_screens:
nextreg TRANSPARENCY_COLOUR_FALLBACK, MY_TRANSPARENCY_COLOUR_FALLBACK ; Set fallback colour for pixels uncoloured by all layers (transparent or layer disabled)
nextreg GLOBAL_TRANSPARENCY, SOFT_RESET_GLOBAL_TRANSPARENCY ; Set palette index for transparency in Layer 2, ULA, LoRes and 1-bit Tilemap layers
nextreg ULA_CONTROL, %1'01'0'0'0'0'0 ; disable ULA output, no ULA/Tilemap blending, disable ULA+ and ULA half-pixel scroll, disable stencil mode
nextreg SPRITE_AND_LAYERS_SYSTEM, %0'0'1'000'1'0 ; disable LoRes
; sprite 0 lowermost
; SLU layer order
; enable sprites and clipping "over border"
; disable Sprite layer
nextreg TILEMAP_CONTROL, %0'0'0'0'0'0'0'0 ; disable Tilemap
; 40x32 mode
; use attribute byte
; first Tilemap palette
; disable 1-bit Tilemap
; 256 tile mode
; Tilemap below ULA
nextreg DISPLAY_CONTROL_1, %0'0'00000 ; disable Layer 2
; disable ULA shadow display
; Timex Sinclair bits 5-0 redundant as ULA output disabled
nextreg LAYER_2_CONTROL, %00'01'0000 ; 320x256x8bpp, set this before setting clip window
; Zero palette offset
nextreg LAYER_2_Y_OFFSET, 0
nextreg LAYER_2_X_OFFSET, 0
nextreg LAYER_2_X_OFFSET_MSB, 0
nextreg CLIP_WINDOW_CONTROL, %0000'1'0'1'1 ; Reset clip-window register indices for Sprite, Layer 2, Tilemap layers
nextreg CLIP_WINDOW_LAYER_2, 0 ; X1 X-coordinates are doubled for 320x256 mode, so actually 0
nextreg CLIP_WINDOW_LAYER_2, 159 ; X2 X-coordinates are doubled for 320x256 mode, so actually 318
nextreg CLIP_WINDOW_LAYER_2, 0 ; Y1
nextreg CLIP_WINDOW_LAYER_2, 255 ; Y2
nextreg LAYER_2_RAM_PAGE, MY_LAYER_2_START_BANK16K ; 5 16KiB-banks needed for 320x256
/* Section 1 */
;nextreg MEMORY_MANAGEMENT_SLOT_0_BANK, 2*MY_LAYER_2_START_BANK16K
;nextreg MEMORY_MANAGEMENT_SLOT_1_BANK, 2*MY_LAYER_2_START_BANK16K + 1
/* End Section 1 */
/* Section 2 */
;PORT16BIT_WRITE LAYER_2_ACCESS_PORT, %00'0'0'0'1'0'1
;PORT16BIT_WRITE LAYER_2_ACCESS_PORT, %000'1'0'000
/* End Section 2 */
ld HL, $0000 ; Write vertical stripe (set all of first Bank16k)
ld BC, $3FFF
.loop
ld (HL), MY_LAYER_2_STRIPE_COLOUR
inc HL
ld A, C
or B
jp z, finish
dec BC
jp .loop
finish:
nextreg DISPLAY_CONTROL_1, %1'0'000000 ; enable Layer 2
ret
;-------------------------------------------------------------------------------
; SAVENEX pseudo-ops
;-------------------------------------------------------------------------------
;
SAVENEX OPEN "layer2_experiment.nex", entry, $0000, 0, 2 ; strictly nex file standard V1.2
SAVENEX CORE 3, 0, 6 ; 320x256 Layer 2 required
SAVENEX CFG 5, 1, 0, 1 ; cyan border, file handle in BC, default NextRegs, 2MB required
SAVENEX AUTO
SAVENEX CLOSE
Thank you for another test case, but in this case only vanilla ZEsarUX is struggling, my fork works correctly (well, it doesn't randomize the memory content, but I don't find that worth of putting more effort into the dead fork), here are ZEsarUX 10.3 vs ZESERUse latest vs #CSpect 2.19.4.4 (CSpect does also randomize the memory). The cyan filling is my adjustment to the source code doing ~MY_LAYER_2_STRIPE_COLOUR
fill color (122 instead of 133), so I can tell if I'm running first or second variant of the code:
Generally speaking ZEsarUX is not exactly the most accurate emulator around, that's why I initially tried to help with pull requests fixing some of the bugs and later forked the project as Cesar was struggling to incorporate my pull requests while being comfortable to further develop and maintain his project.
You can check Next wiki for known discrepancies in the emulators, although I didn't update these for most recent versions (yet .. I'm sort of on forced hiatus WRT to Next stuff, but I hope I will be back eventually): https://wiki.specnext.dev/ZEsarUX:known_bugs https://wiki.specnext.dev/ZESERUse:known_bugs https://wiki.specnext.dev/CSpect:known_bugs
If you are interested into overall most current emulator which is being developed, then you should try CSpect. My ZESERUse fork is slightly more accurate if you don't mind being stuck on core 3.1.5 features (and I have no plans to work on it any further, although small bug fixes still happen). ZEsarUX author is at this moment least pro-actively fixing bugs, usually he prefers to receive the released game which does not work correctly, and fix emulation based on that, he's not very enthusiastic about fixing code based on test cases or work in progress projects AFAIK. At least my test suite keeps being ignored... :D
BTW I don't mind chatting about ZEsarUX bugs even here, but I would appreciate if you would then put effort into testing also against my fork (so I know if the bug/discrepancy happens in both ZEsarUX and ZESERUse), and also cross-check against known bugs list and if you have good test for bug which is not described at all, feel free to update the wiki as well and provide the test here (ideally we can cooperate to turn it into test in https://github.com/MrKWatkins/ZXSpectrumNextTests/ collection, so I can re-check the bug with every new release of any emulator)
Although with ZEsarUX there's too many small inaccuracies, so for example your code is covered by rather general:
ULA+tilemode does not support stencil mode, and transparency fallback color is not used in some edge cases (disabled ULA + clipped tiles)
I would document stuff more thoroughly and provide more test cases if there would be interest from the author, but seems I usually overwhelmed him so quickly, that such extra activity was in the end detrimental to the result. :) After all, it's a hobby and he has full right to proceed at the pace he likes and can sustain.
The issue is that the snap.c void load_nex_snapshot(char *archivo)
will call tbblue_out_port_32765(nex_header[139]);
which does
//para indicar a la MMU la pagina en los segmentos 6 y 7
tbblue_registers[80+6]=(value&7)*2;
tbblue_registers[80+7]=(value&7)*2+1;
so instead of requested bank 77 the bank 5 is mapped to the top of the RAM.
Guessing the fix, the loader maybe could or should call tbblue_out_port_32765
with value 0 to set Bank 0 first (and do all kind of other inits that routine does), and then switch only the MMU banks as if nextregisters were written? That way I will keep all that memory related stuff there, as it's not clear if it is needed or not (and I will never figure it out).
Sending the entry bank number to the tbblue_out_port_32765
is wrong, that port doesn't accept full 8 bit bank number, but includes ROM selection/etc IIRC, so that's another issue with load_nex_snapshot
implementation.
Thank you for your informative responses, and also for tracking down the bug. So the 16KiB-bank number 77 was masked with 7, surprisingly, resulting in the incorrect entry bank 5. I will follow your advice, set up both ZESARUse and CSpect - the latter appears to be essential for cross-platform development at this time - and if and when I find an interesting bug, upload it to MrKWatkins' GitHub page.
nah, the test suite is not about bugs, it's about tests used to check the behaviour. I depend strongly on that test suite to verify all that stuff noted on "known bugs" pages, so whenever somebody reports some bug in emulator, but I don't have test, the info gets outdated as there are newer versions of the emulator, as I generally don't care about testing it.
So the more stuff the test suite covers, the more verified newer version of emulators are. :) But writing good tests is not trivial, I'm not even fully happy about the ones which I already wrote, some of them are rather cryptic to "read" while running it, if you are not familiar with what the test does, sometimes even I have to check the txt or source to remember if something works as expected or not. :)
OK, I have been warned: don't underestimate the task of turning a bug into a test for it.
@OnceBitten I will show the fix (for entry bank in NEX file) to Cesar, maybe he will apply it to vanilla ZEsarUX too. On this fork it's fixed.
Thank you, ped7g.
See: https://github.com/z00m128/sjasmplus/issues/236#issue-2458343247
Both zeseruse and current vanilla ZEsarUX are failing.
As I did refactor direct nex loader implementation long time ago, it's more likely there is some init/reinit code later in the loading path cancelling the mapping of entry bank than completely ignoring entry bank in the loader itself, but somebody will have to check the code to be sure. Yay!
(also it's just my guess it's the missing mapping of the bank before jumping into the code. It could be also wrongly accepted interrupt handler as first instruction after load, while technically it should be in
DI
state and start with the code from the NEX file).