Announcement

Collapse

Forum etiquette

Our mission ...

This forum is part of our mission to promote the preservation of vintage computers through education and outreach. (In real life we also run events and have a museum.) We encourage you to join us, participate, share your knowledge, and enjoy.

This forum has been around in this format for over 15 years. These rules and guidelines help us maintain a healthy and active community, and we moderate the forum to keep things on track. Please familiarize yourself with these rules and guidelines.


Remain civil and respectful

There are several hundred people who actively participate here. People come from all different backgrounds and will have different ways of seeing things. You will not agree with everything you read here. Back-and-forth discussions are fine but do not cross the line into rude or disrespectful behavior.

Conduct yourself as you would at any other place where people come together in person to discuss their hobby. If you wouldn't say something to somebody in person, then you probably should not be writing it here.

This should be obvious but, just in case: profanity, threats, slurs against any group (sexual, racial, gender, etc.) will not be tolerated.


Stay close to the original topic being discussed
  • If you are starting a new thread choose a reasonable sub-forum to start your thread. (If you choose incorrectly don't worry, we can fix that.)
  • If you are responding to a thread, stay on topic - the original poster was trying to achieve something. You can always start a new thread instead of potentially "hijacking" an existing thread.



Contribute something meaningful

To put things in engineering terms, we value a high signal to noise ratio. Coming here should not be a waste of time.
  • This is not a chat room. If you are taking less than 30 seconds to make a post then you are probably doing something wrong. A post should be on topic, clear, and contribute something meaningful to the discussion. If people read your posts and feel that their time as been wasted, they will stop reading your posts. Worse yet, they will stop visiting and we'll lose their experience and contributions.
  • Do not bump threads.
  • Do not "necro-post" unless you are following up to a specific person on a specific thread. And even then, that person may have moved on. Just start a new thread for your related topic.
  • Use the Private Message system for posts that are targeted at a specific person.


"PM Sent!" messages (or, how to use the Private Message system)

This forum has a private message feature that we want people to use for messages that are not of general interest to other members.

In short, if you are going to reply to a thread and that reply is targeted to a specific individual and not of interest to anybody else (either now or in the future) then send a private message instead.

Here are some obvious examples of when you should not reply to a thread and use the PM system instead:
  • "PM Sent!": Do not tell the rest of us that you sent a PM ... the forum software will tell the other person that they have a PM waiting.
  • "How much is shipping to ....": This is a very specific and directed question that is not of interest to anybody else.


Why do we have this policy? Sending a "PM Sent!" type message basically wastes everybody else's time by making them having to scroll past a post in a thread that looks to be updated, when the update is not meaningful. And the person you are sending the PM to will be notified by the forum software that they have a message waiting for them. Look up at the top near the right edge where it says 'Notifications' ... if you have a PM waiting, it will tell you there.

Copyright and other legal issues

We are here to discuss vintage computing, so discussing software, books, and other intellectual property that is on-topic is fine. We don't want people using these forums to discuss or enable copyright violations or other things that are against the law; whether you agree with the law or not is irrelevant. Do not use our resources for something that is legally or morally questionable.

Our discussions here generally fall under "fair use." Telling people how to pirate a software title is an example of something that is not allowable here.


Reporting problematic posts

If you see spam, a wildly off-topic post, or something abusive or illegal please report the thread by clicking on the "Report Post" icon. (It looks like an exclamation point in a triangle and it is available under every post.) This send a notification to all of the moderators, so somebody will see it and deal with it.

If you are unsure you may consider sending a private message to a moderator instead.


New user moderation

New users are directly moderated so that we can weed spammers out early. This means that for your first 10 posts you will have some delay before they are seen. We understand this can be disruptive to the flow of conversation and we try to keep up with our new user moderation duties to avoid undue inconvenience. Please do not make duplicate posts, extra posts to bump your post count, or ask the moderators to expedite this process; 10 moderated posts will go by quickly.

New users also have a smaller personal message inbox limit and are rate limited when sending PMs to other users.


Other suggestions
  • Use Google, books, or other definitive sources. There is a lot of information out there.
  • Don't make people guess at what you are trying to say; we are not mind readers. Be clear and concise.
  • Spelling and grammar are not rated, but they do make a post easier to read.
See more
See less

Looking for fast Turbo C 2.01 screen copy routine for CGA graphics

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

    Looking for fast Turbo C 2.01 screen copy routine for CGA graphics

    Are there any tried-and-true (and fast) routines available online for Turbo C 2.01 for copying a full 16k buffer to CGA video RAM? Either native C or ASM is fine. Thanks!

    #2
    Well, IFAIK, there's (almost) nothing online about CGA programming. There's a little of info about programming MCGA/VGA mode 13h but there's a great void about earlier graphic chips.

    Nevertheless, the routines I'm posting here indeed work. You must have into account that they are planned for a buffer that is organized the exact same way the CGA video RAM is: that is, first, 100 even scanlines (8000 bytes) - 192 bytes of empty (invisible) space - 100 odd scanlines (8000 bytes more) - another 192 empty bytes.

    The first one (and fastest), is written for Turbo Assembler (any version). It wouldn't work on MASM without modifications because it uses ARG in order to simplify the stack use for parameters.

    Code:
    .8086
    .model compact
    
    .code
    
    public		_copyBuffer2Video
    
    _copyBuffer2Video proc near
    ARG buffer: dword
    
    	push 	bp
    	mov 	bp,sp
    	push 	ds
    
    	lds 	si,[buffer]
    
    	mov 	ax,0b800h
    	mov 	es,ax
    	xor 	di,di
    
    	mov 	cx,2000h
    	rep movsw
    
    	pop		ds
    	pop		bp
    	
    	ret
    _copyBuffer2Video endp
    
    end
    And here the accompanying C function prototype for calling it:

    Code:
    void copyBuffer2Video (char *buffer);

    I post here also a native C function, but I don't recommend it because is way slower than the assembler one:

    Code:
    #include <mem.h>
    
    copyBuffer2Video_PureC (char *buffer)
    {
    	char far *vbufCGA = (char far *) 0xb8000000;
    
    	memcpy (vbufCGA, buffer, 0x4000);
    }

    If the buffer is organized other way, the code must be modfied. For instance, instead of having all the even scanlines grouped, it could be ordered just as what the display likes, that is, even scanline - odd scanline - next even - next odd and so on. If it's the case, these functions will not work correctly.

    Hope it helps.
    Last edited by carlos12; March 3, 2021, 12:08 PM.

    Comment


      #3
      Perfect, thank you! I'll test that out tonight and let you know the results.

      Comment


        #4
        I spun up a quick project for this:

        https://github.com/discolando/cgatest

        It *almost* works, but as you can see in the screenshot in the repo, the output is a bit garbled. I'm sure it's something simple I'm doing wrong - I'm slowly reacquainting myself with C.

        Comment


          #5
          I think the problem is the array is too big. With such a massive amount of data (for the IBM PC proportions ) is preferable using pointers and allocating memory using malloc.

          I changed

          Code:
          char buffer[16384];
          with

          Code:
          char *buffer;
          added this header:

          Code:
          #include <stdlib.h>
          and this to your main function:

          Code:
          buffer = malloc (0x4000);
          The full modified test.c:

          Code:
          #include <bios.h>
          #include <stdlib.h>
          
          #define Enter_Key 0x0d
          
          void copyBuffer2Video(char *buffer);
          void setGraphicsMode();
          void setTextMode();
          
          char *buffer;
          
          void waitForEnter()
          {
              unsigned char keycode;
              unsigned char ckeycode;
          
              do {
                  keycode  = bioskey(0);
                  ckeycode = keycode & 0xFF;
              } while (ckeycode != Enter_Key);
          }
          
          void main()
          {
              int x;
              setGraphicsMode();
          
              buffer = malloc (0x4000);
          
              for (x = 0; x < 16384; x++) {
                  buffer[x] = 0xAA;
              }
          
              copyBuffer2Video(buffer);
              waitForEnter();
          
              for (x = 0; x < 16384; x++) {
                  buffer[x] = 0x55;
              }
          
              copyBuffer2Video(buffer);
              waitForEnter();
          
              free(buffer);
          
              setTextMode();
          }
          At least on my computer, it works. Just in case, remember to compile the C file also in Compact model.

          Comment


            #6
            I just made another test using the array instead of the pointer+malloc and it also works fine for me.

            There are the commands I used for compiling, also with TC 2.01 and TASM 1.0:

            Code:
            tasm /ml cga
            
            c:\tc\tcc -c -mc test.c
            
            c:\tc\tlink test cga c:\tc\lib\c0c.obj,,,c:\tc\lib\cc.lib
            I have TC 2.01 installed on c:\tc. Change it with the path you have it installed. I'm not using here the DOS path environment variable because I have TC++ on it, not TC 2.01.

            Comment


              #7
              At least on my computer, it works. Just in case, remember to compile the C file also in Compact model.
              That did it. Adding the parameter to TCC to have it compile with the compact model resolved it and everything is working exactly as expected, and fast, too!

              What modifications would I need to make to have it work as expected with other memory models?

              Comment


                #8
                Doesn't TCC have an inline assembly feature (sorry, I'm a MSC user)? It should be possible to include this in your code and automatically handle the model size issue. The code's pretty much a rep movsw.

                I wonder how the assembly version would compare with the memcopy() intrinsic? MSC can inline this function into a movsb+movsw pair.

                Comment


                  #9
                  Originally posted by Chuck(G) View Post
                  Doesn't TCC have an inline assembly feature (sorry, I'm a MSC user)? It should be possible to include this in your code and automatically handle the model size issue. The code's pretty much a rep movsw.

                  I wonder how the assembly version would compare with the memcopy() intrinsic? MSC can inline this function into a movsb+movsw pair.
                  From what I understand, Turbo C 2.01 only supports inline assembly through the use of __emit__. I'll dig through the PDF of the manual to see if that's accurate.

                  Comment


                    #10
                    Originally posted by Chuck(G)
                    Doesn't TCC have an inline assembly feature (sorry, I'm a MSC user)? It should be possible to include this in your code and automatically handle the model size issue. The code's pretty much a rep movsw.
                    Yes, TC has inline assembly, I think since 2.0. But in 2.0 the projects with it cannot be compiled from IDE, and I think it has some limitations. At least it gave some problems to me. From TC++ 1.0 and onwards the inline assembly support is more complete.

                    Originally posted by discolando
                    What modifications would I need to make to have it work as expected with other memory models?
                    First (sorry for stating the obvious), change the header of the asm file to the model you want (I.e= .model small, .model large, etc.).

                    For models tiny, small and medium a little change must be made on the _copyBuffer2Video procedure:

                    Code:
                    _copyBuffer2Video proc near
                    ARG buffer: dword
                      push  bp
                      mov   bp,sp
                     
                      mov   si,offset [buffer]
                      mov   ax,0b800h
                      mov   es,ax
                      xor   di,di
                      mov   cx,2000h
                      rep movsw
                    
                      pop   bp
                      ret
                    _copyBuffer2Video endp
                    As lds si,[buffer] makes an unnecessary operation for these models (that is, loading ds, the data segment), some garbage appears on screen. So here it's simplified loading only the offset as there's only one data segment. We can also remove the push ds and pop ds.

                    Also, on the medium, large and huge model the procedure must be declared far (as there are several code segments):

                    Code:
                    _copyBuffer2Video proc far
                    Remember to compile the C file also for that model, with the according libraries (C0S.obj and CS.LIB for small model and so on).

                    That's the charm (and nightmare) of the memory segmentation that we have learned to love

                    Comment


                      #11
                      Thanks again for the previous replies to this thread.

                      To follow up from my original question, are there any standardized and CGA specific C/ASM routines for drawing a sprite of arbitrary width & height to any location on the screen? I can store the sprite interlaced or not interlaced, and I presume that storing it interlaced would give a performance boost.

                      Comment


                        #12
                        There is PC Tech Journal edition with an article by Michael Abrash called Software Sprites that is more or less describing what you're affter. There was an other thread here where someone linked to this, I saved the link back then but not the thread, so sorry to the original poster, I'm not trying to plagiarise.

                        https://archive.org/details/PC_Tech_.../n128/mode/1up

                        Comment


                          #13
                          Trying sprites on tandy interlaced mode, I also got an idea about how cga sprites can work.

                          First I stored the sprite in two arrays, one for even lines and one for odd. And then, every sprite byte is followed by a mask byte.
                          For this to work, mask bytes have color 4 for transparent parts, and color 0 for the sprite itself (or bits 11 for transparent, bits 00 for data).

                          The way I got something working was like this:

                          Process even lines (mask,data,mask,data...)
                          Change screen address to odd lines
                          Porcess odd lines (mask,data,mask,data...)

                          I still had some code and maybe I will use it again.

                          Code:
                          	mov 		ax,0B800h
                          	mov 		ds,ax
                          	mov		si,s_offset	//ds:si SCREEN
                          	les		di,[spritedata]	//es:di Sprite data array
                          	
                          	//even lines
                          	//process one byte (4 pixels)
                          	mov		al,byte ptr es:[di]	//get sprite mask
                          	inc		di
                          	mov		bl,byte ptr es:[di]	//get sprite data
                          	inc		di
                          	mov		ah,byte ptr ds:[si]	//get screen data, sprite data will be pasted on top
                          	and		ah,al			//screen data = screen data AND mask
                          	or		ah,bl			//masked screen data = masked screen data OR sprite byte
                          	mov		byte ptr ds:[si],ah	//paste processed byte to screen
                          	inc 		si			//go to next byte
                          
                          	//Now you'll process (for example) 4 bytes for a 16x16 sprite and then go to next line
                          	add		si,80-4
                          	//Next line...
                          	//Finally draw 8 even lines and jump to the odd ones
                          	add		si,Odd_Lines address	//= something like 80*100, I can't remember.
                          Now you have to store the original screen data and delete the sprite every frame... or make a sprite with black borders and make it leave a black trail (like boulder dash), or move the sprite only on top of black parts of the screen.
                          Last edited by Mills32; April 2, 2021, 03:56 AM.

                          Comment

                          Working...
                          X