News:

Printed Amstrad Addict magazine announced, check it out here!

Main Menu
avatar_endangermice

CRTC Question

Started by endangermice, 21:50, 28 September 16

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

endangermice

Having got the FDC controller initially working, I've been looking into some of the problems with the CRTC code in XNACPC. I've so far managed to fix a bug that prevented register 9 (the Maximum Raster Address) from, having the proper effect. I'm now working on a problem with the implementation of Register 4 (Vertical Total).


What I'd like to understand is what happens if the Vertical Total is set to a value that is less than the Vertical Sync Position. At the moment, my the screen drawing code is hanging becuase, if the Vertical Total is set less than the Vertical Sync Position, the counters are never going to reach the vertical sync position as they get reset once the Vertical Total has been reached. How is this scenario handled by a Real CPC / decent emulator. I have observed the behaviour in Arnold and WinApe and it appears that these emulators duplicate the part of the screen drawn up to the Vertical Total until the complete screen is drawn. Is my understanding correct or does something else happen here?


Many thanks,


Damien
For all the latest Starquake remake news check out my website - www.endangermice.co.uk

endangermice

#1
Having looked at this a bit more, would I be right in saying that the Vertical Total affects the timing of the DISPEN state therefore the actual scan timings stay the same i.e. the counters scan from column 0 - 63 horizontal and columns 0 to 38 vertical regardless, only if we hit Vertical Total, we switch to DISPEN and draw the border. That would mean that VSync will always be hit regardless of the Vertical Total?


If also noticed that both Arnold and WinApe can lose picture sync if some CRTC registers are set to certain values (like on a real CPC). I believe that this has been achieved by simulating a Monitor with its own timings and having the Emulated CRTC sync up to this, with the monitor losing Sync if the timings of the CRTC no longer match the timings of the Monitor?
For all the latest Starquake remake news check out my website - www.endangermice.co.uk

PulkoMandy

The vertical total is the number of (character) lines the CRTC will generate in a screen. When the counter reaches vertical total, it starts over at 0 and a new screen begins (line 0, 1, 2, etc).


If the vertical sync position is out of that range, there will be no vertical blanking on that screen. This is not a problem. If the reg6 is higher than reg4, there will also be no vertical border. This is also not a problem.


Note that line 0 is the first one with characters. So, from the CRTC point of view, the top border of the screen is the end of frame N, and frame N+1 starts when it prints the first line of no-border.


The CTM display that comes with the CPC expects a vertical blanking to happen at every 50.01Hz (15625Hz/312 lines to be exact). This does not need to be related to the number of CRTC "screens" generated. for example, you could generate many screens of 1 character line. This is how the various "screen split" ("rupture") effects are achieved.


If there is no VBL in the expected time (a good test case for this is the Climax intro), the CTM has a built-in timeout and will start a new frame anyway. The timeout duration depends on the "vertical hold" potentiometer at the back of the display.

endangermice

#3

Thank you, that's a great explanation and makes a lot of sense.

If I understand correctly, the reason why I see the screen repeating (on a real CPC and the better Emulators) when I set a small Vertical Total e.g. 10 is because the CRTC finishes drawing the screen when it reaches character row 10, but the Monitor is at this stage only partially through a whole screen scan. When the CRTC starts drawing the next frame, the Monitor hasn't finished one entire screen scan so when the CRTC outputs the next screen it will appear on the monitor multiple times (depending on the height of the Vertical Total). While the image appears to be repeated, in reality it's actually drawing the next frame (impossible to tell if it's a static image like Basic).

I have noticed that on a real CPC, the screen rolls when the Vertical Total is less than the Vsync row. Is this because it's never reaching V-Sync so therefore no synchronisation signal is sent from the CRTC or is it because the timings are now out with what the monitor expects - perhaps both?

I have also found that if you set the VSync period within a shortened Vertical Total that the screen also rolls. I presume this is because the shorter screen draw and quicker VSync is no longer keeping in time with what the monitor expects.

It looks like both Arnold and WinApe simulate a monitor with its own timer. Both will show a loss of V-hold like on a real CPC though both show it in a slightly different way, which I suspect is due to the way they're simulating the v-hold.

The V-Hold makes sense too, that affects the TV's own internal timer which is used if no sync signal is found and is why you will see the screen rolling if no sync signal is produced by the CRTC, the TV cannot guarantee to sync its own timers  with the CRTC unless the CRTC sync signal is received.

I measured the VSync of the CPC yesterday with my multimeter to prove to myself it outputs at 15.6KHZ :) .
For all the latest Starquake remake news check out my website - www.endangermice.co.uk

PulkoMandy

If vertical total is less than vsync position, there is no VSync signal from the CRTC. The monitor does what it can to stay in sync (if you very carefully adjust the VHold, you can make the picture somewhat stable).


If the vertical total is less than the required number of lines, the VSync will happen more often. The monitor can compensate for that a little, but at some point it will give up and "skip" the synchronization signal, trying to sync again on the next one.


There aren't much details about the sync chip used in the CTM, but from observations of the horizontal sync behavior (and some weird code to exercise it), it is likely that the chip implements this with a PLL (https://en.wikipedia.org/wiki/Phase-locked_loop), which is a bit more complex than a simple timer would be. But for the vertical sync, in most cases, a timer will be enough. For the horizontal sync however, there are some tricks which need a more careful implementation. I can think of Climax G "loading" screen, and many demos from the 80s with very weird timings and screen distortions.


If you happen to try such demos, setting the brightness of the CTM to a high level will allow you to see the slight difference between normal black, and synchronization areas, which are even darker (of course with sane timings this is invisible outside screen borders).

endangermice

Thank you very much for all your help, it has enabled me to get to a point where I now have an initial emulation of the CRTC working - it's working with most standard games and a few CRTC effects are also working :).

I'm now working on implementing all of the correct results for the adjustment of the various registers. I'm currently working on getting the Display Address working properly for hardware character scrolling.

I'm trying to get my head around how it works. I understand the fundamentals i.e. that the address is used to determine where from memory to read the data to begin from column 0, row 0 but I'm not understanding how the correct area in memory is calculated.

The display start address (hight and low) is the address in memory where we satart to draw the screen i.e. where we read to display pixels in column 0, row 0. On the CPC the default screen address is 0xC000. The bit I don't understand is how this is derviced from the display start address. On startup, this is set to R12 (Display Start Address High) 0x30 and R13 (Display Start Address Low) 0x00 which makes a 16bit address of 0x3000.

In various emulators, the code to translate this to 0xC000 is a left bitshift by 2 places and then applying an & mask to remove all but the two highest bits. It appears to be converting a 14 bit number to a 16 bit number. What I don't understand is why this is neccessary when R12 and R13 combined would produce a 16 bit number. Why don't the combination of the two numbers produce 0xC000 instead of having to do the shift and mask too?

When the screen is scrolled i.e. say the start address for charcter 0, row 0 is changed to 0xC050, is the memory wrapped or will it read 0x50 bytes beyond the original end point of 0xFFFF? I'm assuming it wraps back to 0xC000 since anything beyond 0xFFFF would be outside of the maximum value addressable by the CPC's 16bit address bus.

I hope this all makes sense - I'm just a little unclear on one or two things here. I've been reading through the various documentation on this site and on Grimware along with the datasheets for the chips but I have't been able to find anything that clearly explains my queries - I may of course have missed something....
For all the latest Starquake remake news check out my website - www.endangermice.co.uk

PulkoMandy

You won't find the explanation in the datasheet, and not yet on grimware for this. You have to look very carefully at the CPC schematics.


The CRTC will increment its addresses internally starting from the R12:R13 setting. These generate the MA address. It is combined with the RA address which is generated from the pixel line inside each character (it goes from 0 to 7 in the default settings).


If you look at the CPC schematics, you will see that the RA and MA are wired to the RAM in a weird order:
- RA4 and RA5 are not used, so character heights above 7 will have strange results (looping over the first 8 lines of the character)
- MA9 and MA10 are also not wired, which are the two "ignored" bytes in R12
- The mapping of the other pins is in an unusual order which results on the unusual order of lines on the CPC display (add 0x800 to get to next pixel line, etc).

endangermice

Ah yes I recall now the strange wiring of the CPC to the CRTC, which is why programming the screen is a lot harder than it should be, thank you for the explanation. I'm getting there with the hardware scrolling but I'm having a few calculation issues.

I understand that a CPC screen is 16,384 bytes in size (0x4000). However I cannot work out how that figure is calculated. Logic suggests it should be the size of a character scanline in bytes which is: 2 x number of characters in a row (40 by default) x number of columns in a row (25 by default) * number of scanlines in a character (8 by default). However this calculation produces a figure of 16,000. What's happened to the other 384 bytes? Are they ignored?

Similarly I cannot figure out how we get 2048 (0x800) as the offset for the next scanline row. Again logic says it should be the size of a scanline in bytes (80 by default) * the number of vertical columns (25 by default). However this arrives at 2000 not 2048, again 48 bytes have gone missing.

I believe I need to understand how to calculate the offset for the scanline row so that I can produce the correct behaviour when the number of displayed horizontal characters is different to the default. I would imagine that the offsets also change in this scenario.

Am I doing these calculations wrong or is there some other reason why I cannot arrive at the correct values is it perhaps because the result has to be the nearest value that's a power of 2...?


Thanks again for your help, I think I'm making progress - slowly!
For all the latest Starquake remake news check out my website - www.endangermice.co.uk

andycadley

The extra 384 bytes just aren't being displayed, because those addresses aren't currently being generated by the hardware. They tend to be lumped into the display memory simply because they're scattered around in a way that means they aren't convenient to use and also they'll be used as the screen is hardware scrolled (ignoring overscan addresses generated by the hardware will wrap within each 16K block)

PulkoMandy

Again, this is all because of the way things are wired.


So yes, in the standard screen format there are 384 unused bytes. They start being used if you scroll the display (just press RETURN a few times in BASIC until the cursor reaches the bottom of the screen, it will change the R12/R13 value for scrolling).


The 0x800 bytes come from the way the CRTC is wired to the RAM.

pelrun

Quote from: endangermice on 21:27, 05 October 16
Similarly I cannot figure out how we get 2048 (0x800) as the offset for the next scanline row. Again logic says it should be the size of a scanline in bytes (80 by default) * the number of vertical columns (25 by default). However this arrives at 2000 not 2048, again 48 bytes have gone missing.


This is a very common thing in image buffers, not just on the CPC, and it's called the "stride" of the array. The scanline is 2000 bytes wide, but the array stride is 2048, so you have to add 2048 to move to the next row. And it's precisely because it's easier to work with powers-of-two instead of an arbitrary image width, even if you lose a few bytes.

PulkoMandy

Well, it gets a bit more tricky. The offset between two lines is always, no matter what you do, 0x800 bytes. Even if you change the display format. Even if you use 32K of video memory. It's just that the line address bits of the CRTC are wired to A10, A11, A12, etc. So, incrementing the line counter by 1 leads to incrementing the address by 0x800 bytes.

endangermice

#12
Hi Guys, thank you for all the clear explanations, I think I'm getting there. I created a monitor class last night that can sync to my CRTC code so now a lot more things look correct. However, there still seems to be a problem with the way I am generating screen addresses. It's working fine for all standard games, but anything like the Batman Forever demo is a complete mess and I'm not sure what the exact problem is.



The main issue is that I still don't fully understand the correct procedure for generating the addresses which isn't helping me to figure out what's wrong. My code for calculating addresses is as follows:



int CalculateScreenMemoryAddress()
{
    int crtc_screen_address = (GetRegister(Register.DisplayStartAddressHigh) << + GetRegister(Register.DisplayStartAddressLow);

   //crtc_screen_address |= GetRegister(Register.DisplayStartAddressLow);
   int screen_memory_address = ((crtc_screen_address << 2) & (0xF000));
   //int screen_memory_address = (crtc_screen_address << 2);
   //screen_memory_address = 0xc0A0;

   int scroll_offset = (((GetRegister(Register.DisplayStartAddressHigh) & 0x03) * 256) + (GetRegister(Register.DisplayStartAddressLow) & 255)) * 2; // From http://www.cpctech.org.uk/docs.html for 16K screen

   int bytes_across = m_horizontal_character_counter * 2; // 2 bytes per character
   int bytes_down = m_vertical_character_counter * (GetRegister(Register.HorizontalDisplayed) * 2); // Whole row

   int scanline_offset = 0x800; // Regardless of the screen format or anything else, this is always the screen offset
   int memory_line_offset = ((m_vertical_scanline_counter % (GetRegister(Register.MaximumRasterAddress) + 1)) * scanline_offset); // This accounts for Amstrad's bizarre wiring of the CRTC

   return bytes_across + bytes_down + memory_line_offset + screen_memory_address + scroll_offset;
}


I'm not sure if anyone can see where I'm going wrong? A typical example of the difference between my emulation and WinApe or Arnold is if I set the DisplayAddressHigh to 44 on those emulators, the screen becomes a mash of lines (out &bc00,12, out &bd00,44). On my emulator, the first couple of lines in each character row are the same, but the rest are rendered out as normal. It is as if memory is not wrapping round correctly or something like that.

If anyone has any ideas, they would be most welcome :) .[/code]
For all the latest Starquake remake news check out my website - www.endangermice.co.uk

andycadley


I haven't followed your code entirely, but I don't see how it is taking into account things like the addresses within a scanline not necessarily being continuous - i.e. when the scroll offset is non-zero there can be a portion of the screen whose address cannot be calculated as simply start_address_of_line + (2 * characters_across)


I suspect it gets a lot easier if you stop thinking about "the screen" and treat the process as purely maths. Calculate the values for MA and RA first, then combine the bits as per: http://cpcwiki.eu/index.php/CRTC

PulkoMandy

The code is a bit hard to follow this way indeed.


What is done in emulators I know is:
1) Implement the CRTC internal counters. The CRTC works by counting things from 0 up to some register value, and when the value equals the register, reset the counter to 0 and start over. This is important, because the register value can be changed while the counter is counting! It can lead to situations like that:



Counter Reg Result


0            7    !=
1            7    !=
2            7    !=
3            7    !=
(here, the z80 writes to the register)
3            2    !=
4            2    !=
5            2    !=
...
255        2    !=
(here, the CRTC register overflows without matching anything)
0            2    !=
1            2    !=
2            2    ==
(here, the counter matches, an event happens: DISPEN goes to 0, or a counter is reset, or...)



2) Once you have this, emulate the computation of MA and RA outputs from the CRTC. They are straightforward from the counter values: RA is from the line counter, MA is from the character line and character column counters
3) Map these MA and RA values to a RAM address according to the CPC wiring (did you see where it is in the CPC schematics?): A0 is generated from the 1MHz clock, A1 is MA0, etc.
4) Read the byte at the generated address and send it to the "Gate Array" (for palette and mode handling)
5) Output the pixels from the gate array into the CTM emulation, don't forget to also feed it the sync signals
6) At first a simple emulation of the CTM is enough: draw pixels from left to right on a line, when there is a HSYNC, move to next line, when there is a VSYNC, move to top of screen
7) Once you have this working, you can start working on a more realistic emulation of the CTM to handle cases where the HSYNC and VSYNC are not at the expected times or not present at all.

endangermice

#15
Yes you're both quite right my code isn't that easy to follow, basically some parts of it I have figured out from the datasheets for the CRTC other parts are taken from various other sites and some from the existing XNACPC emulator I've been working with. It isn't optimised in any way and is pretty cumbersome as it's really just experimental. Thank you both for having a go though, I know there's nothing worse than trying to understand someone else's work!

I remembered late last night when browsing the forum on setting the base address that it can only be set to 4 pages, 0x0000, 0x4000, 0x8000 and 0xC000. Fano wrote in another post how bit 4 & 5 of register 12 select which of the 4 pages is going to act as base address so now I can calculate this very easily - it seems the original code in XNACPC is wrong, I think the reason it makes no sense is becuase it's a bit of a stab in the dark that just happens to produce the correct results for standard games. Bearing this in mind, I'm now calculating the base address by simply doing the following:

int screen_memory_address = ((GetRegister(Register.DisplayStartAddressHigh) & 0x30) >> 4) * 0x4000;

& Mask off bits 4 & 5 (0x30), bit shift to the right by 4 and multiply the result by 0x4000. This works perfectly so now it's time to figure out and fix the rest of the code.

I will now study both your replies, thank you again for all the great detail. I'm tempted to write this all up if I finally get it all working, I think it would be useful to expand on what's currently in the wiki and I'd also like to give something back for all your efforts.

Andy you're right, I need to think in terms of the maths and get that right. Everything should then work. The display aspects have been a distraction I really should be concentrating on the timings and maths - good advice, thanks.

I'll let you know how I get on, thanks again,

Damien
For all the latest Starquake remake news check out my website - www.endangermice.co.uk

PulkoMandy

I don't get this "4 pages" thing. The CRTC can address the full 64K of central ram and start the display almost anywhere in it.


What happens is, 2 bits of the MA address are not wired to anything (as you said, they are "masked"). BUT, these two bits still play an important role. The CRTC still uses them, even if they have no effect. When it reaches the end of a "page", what happens is that these two bits will change.


In the usual setting, your addresses will be like:



00001111
00010000



But, if you have these two bits set to 1 in the initial address:




00111111
01000000



In that case, with the CPC wiring, the "next" page is displayed.

andycadley


Quote from: PulkoMandy on 15:33, 07 October 16
I don't get this "4 pages" thing. The CRTC can address the full 64K of central ram and start the display almost anywhere in it.
Yes, it can, but as a result of the way things are wired up it will wrap within the current page (so a screen starting at &1000 will go to &3fff and then wrap back to &0000) and so people tend to think of it in that way.

endangermice

#18
Right, I think I have it, apart from one final thing...

Quote2) Once you have this, emulate the computation of MA and RA outputs from the CRTC. They are straightforward from the counter values: RA is from the line counter, MA is from the character line and character column counters
I understand that RA value comes from the current line within the character row, which on the CPC could be from 0-7, because only 3 bits are wired up. How is MA calculated from the line and character counters? I've been reading through the documentation and I can't seem to find a description of this. I may just not be able to interpret the information in there.

Also it says that address A0 is fed by the clock. Is this something I need to account for in the emulation too?

Update - I've found it! It was hiding in one of the PDF datasheets http://www.cpcwiki.eu/imgs/1/13/Um6845.umc.pdf page 3-85

MA0 - MA7 = Character Column Counter bits 0-7
MA8 - MA13 = Character Row Counter bits 0-5


I think that looks right.
For all the latest Starquake remake news check out my website - www.endangermice.co.uk

PulkoMandy

Yes, that's it.


Regarding A0, well, what this means is that a "character" for the CRTC is the size of a "mode 1" character, that is, 2 bytes.


How you handle this depends on how precise you want your emulation to be. It probably has some effect when you change a CRTC register at just the right time to make it visible. But otherwise, as long as you manage to get the odd and even bytes to screen in order, it is not very important.

endangermice

#20
So I spent several hours yesterday trying to get this to work, but I'm still not getting the right result. I've generated the CRTC 16 bit address, I have then mapped this to the CPC layout, but the addressing isn't correct.

I've looked into it in detail and when I add in the Memory Base Address (in this case 0xC000), the addresses start generating correctly i.e. the first pixel on each row is:

0xC000
0xC800
0xD000
0xD800 etc.

However when the counter moves to the second row, instead of starting at 0xC050, it's starting at 0xC200. It seems the translation to the CPC addressing is not correct. I'm not sure what I am doing wrong, I have wired it all up exactly according to the docs but it's not generating the addresses correctly.

I've done the calculation by hand and I get the same results so it appears the wiring documentation is missing some vital information, maybe a bitshift or something.

I've been through all the CRTC counter code with a fine tooth comb and that all seems to be creating the correct values, so I'm confident the counters I'm reading in the address creation code are correct it's just the addressing code that is wrong somehow.

I'm working on the basis that the MA CRTC address is generated by taking the first 8 bits of the column address counter (which is the counter that counts horizintally from 0 - 63) and bits 0 - 5 of the row address counter (which is the counter that counts vertically from 0 - 39). Those are then combined to make the 14 bit MA address number (as detailed in http://www.cpcwiki.eu/imgs/1/13/Um6845.umc.pdf page 3-85).

For example, if we are on column 0, row 1, raster row 0 - the column counter is 0 and the row counter is 1. Bits 0-7 of the column counter are therefor 00000000 (the column is 0). Bits 0-5 of the row counter will be 000001 (the row is 1). If I combine these to create the 14bit MA address I get

000001 00000000 or 0x100 in hex

if I then wire this according to the CPC with MA11, MA12 going to A15 and A14 and MA9 - MA0 going to A10 - A1 (I'm ignoring RA in this example since in this case it is 0 so will not have any effect on the generated address) I get:

00000100 00000000 or 0x200 which is the wrong offset, it should be 0x50 (00000000 01010000). I cannot see any way of getting that with the input numbers.

It seems this is the part of the calculation that I am getting wrong. As far as I can tell, I'm performing it correctly, but the result is not correct suggesting I'm missing something.

Hope this makes sense. Any ideas?
For all the latest Starquake remake news check out my website - www.endangermice.co.uk

andycadley


Quote from: endangermice on 16:03, 09 October 16I'm working on the basis that the MA CRTC address is generated by taking the first 8 bits of the column address counter (which is the counter that counts horizintally from 0 - 63) and bits 0 - 5 of the row address counter (which is the counter that counts vertically from 0 - 39). Those are then combined to make the 14 bit MA address number (as detailed in http://www.cpcwiki.eu/imgs/1/13/Um6845.umc.pdf page 3-85).
The CPC operates the CRTC in "straight binary" mode, rather than row/column (indeed other CRTC variants only support that mode) - as indicated by the fact R8 contains 0 under normal circumstances.


In that mode the CRTC counts each displayed "character", so 0 to 39 on the first character row then 40 to 79 on the next. Putting that into CPC format should give you C000h, C050h etc

endangermice

Andy, thank you, that's definitely what I'm missing. One further question, how is the straight binary counter converted into MA? If I'm using rows and columns, it's

MA0 - MA7 = Character Column Counter bits 0-7, MA8 - MA13 = Character Row Counter bits 0-5

However since we're only using a straightforward character counter when the Display is enabled I only have one number. Do I simply populate MA0 - MA7 with this value and ensure MA8 - MA13 are populated with 0's?
For all the latest Starquake remake news check out my website - www.endangermice.co.uk

andycadley

It's just a straight 14-bit counter, you just keep incrementing it and that value is MA.

endangermice

Thanks Andy that's really helpful :).
For all the latest Starquake remake news check out my website - www.endangermice.co.uk

Powered by SMFPacks Menu Editor Mod