X-Git-Url: http://4ch.mooo.com/gitweb/?a=blobdiff_plain;f=16%2Fdos_gfx.cpp;h=b3bd315594e0dc088b309c326588f44dc72c4df5;hb=118896f74475eb26539f1b32923d58f24f0f162f;hp=80e820357f266cdc18af4440fce434a45605ac11;hpb=7c6b87bc6ff8582a7775f7ca315273b7747369a2;p=16.git diff --git a/16/dos_gfx.cpp b/16/dos_gfx.cpp index 80e82035..b3bd3155 100644 --- a/16/dos_gfx.cpp +++ b/16/dos_gfx.cpp @@ -6,20 +6,20 @@ * * Simple graphics library to accompany the article * - * INTRODUCTION TO MODE X. + * INTRODUCTION TO MODE X. * * This library provides the basic functions for initializing and using * unchained (planar) 256-color VGA modes. Currently supported are: * - * - 320x200 - * - 320x240 + * - 320x200 + * - 320x240 * * Functions are provided for: * - * - initializing one of the available modes - * - setting the start address of the VGA refresh data - * - setting active and visible display pages - * - writing and reading a single pixel to/from video memory + * - initializing one of the available modes + * - setting the start address of the VGA refresh data + * - setting active and visible display pages + * - writing and reading a single pixel to/from video memory * * The library is provided as a demonstration only, and is not claimed * to be particularly efficient or suited for any purpose. It has only @@ -45,7 +45,7 @@ #if !defined(__COMPACT__) # if !defined(__LARGE__) # if !defined(__HUGE__) -# error Large data model required! Try compiling with 'bcc -ml lib.c'. +# error Large data model required! Try compiling with 'wcc -0 -ml lib.c'. # endif # endif #endif @@ -57,7 +57,6 @@ //code from old library! /*src\lib\*/ #include "dos_gfx.h" -#include "lib\x\modex.h" int old_mode; //color ‚Ä‚·‚Æ @@ -65,7 +64,7 @@ int gq = LGQ; //‚Ä‚·‚Æ int q = 0; int bakax = 0, bakay = 0; -int xx = rand()&0%320, yy = rand()&0%240, sx = 0, sy = 0; +cord xx = rand()&0%320, yy = rand()&0%240, sx = 0, sy = 0; byte coor; /* @@ -77,10 +76,18 @@ byte coor; /* * Define the port addresses of some VGA registers. */ -#define CRTC_ADDR 0x3d4 /* Base port of the CRT Controller (color) */ +#define CRTC_ADDR 0x3d4 /* Base port of the CRT Controller (color) */ -#define SEQU_ADDR 0x3c4 /* Base port of the Sequencer */ -#define GRAC_ADDR 0x3ce /* Base port of the Graphics Controller */ +#define SEQU_ADDR 0x3c4 /* Base port of the Sequencer */ +#define GRAC_ADDR 0x3ce /* Base port of the Graphics Controller */ +#define STATUS_ADDR 0x3DA + +unsigned char *RowsX[600]; +unsigned char write_plane, read_plane; +unsigned short text_mask[16] = { 0x0002, 0x0102, 0x0202, 0x0302, + 0x0402, 0x0502, 0x0602, 0x0702, + 0x0802, 0x0902, 0x0A02, 0x0B02, + 0x0C02, 0x0D02, 0x0E02, 0x0F02 }; /* @@ -89,7 +96,6 @@ byte coor; */ byte *vga = (byte *) MK_FP(0xA000, 0); -//fontAddr = getFont(); /* * width and height should specify the mode dimensions. widthBytes @@ -106,8 +112,8 @@ unsigned actStart, visStart; /* * set320x200x256_X() - * sets mode 13h, then turns it into an unchained (planar), 4-page - * 320x200x256 mode. + * sets mode 13h, then turns it into an unchained (planar), 4-page + * 320x200x256 mode. */ void set320x200x256_X(void) { @@ -136,7 +142,7 @@ void set320x200x256_X(void) /* Update the global variables to reflect dimensions of this mode. This is needed by most future drawing operations. */ - width = 320; + width = 320; height = 200; /* Each byte addresses four pixels, so the width of a scan line @@ -146,6 +152,45 @@ void set320x200x256_X(void) /* By default we want screen refreshing and drawing operations to be based at offset 0 in the video segment. */ actStart = visStart = 0; + + /* +-------------------- +HORIZONTAL SCROLLING +-------------------- +Horizontal scrolling is essentially the same as vertical scrolling, all +you do is increment or decrement the VGA offset register by 1 instead of +80 as with vertical scrolling. + +However, horizontal scrolling is complicated by two things + + 1. Incrementing the offset register by one actually scrolls by FOUR + pixels (and there are FOUR planes on the VGA, what a coincidence) + + 2. You can't draw the image off the screen and then scroll it on + because of the way the VGA wraps to the next row every 80 bytes + (80 bytes * 4 planes = 320 pixels), if you tried it, you would + actually be drawing to the other side of the screen (which is + entirely visible) + +I'll solve these problems one at a time. + +Firstly, to get the VGA to scroll by only one pixel you use the horizontal +pixel panning (HPP) register. This register resides at + + PORT: 3C0H + INDEX: 13h + +and in real life, you use it like this + +----------------- Pixel Panning --------------- +IN PORT 3DAH (this clears an internal + flip-flop of the VGA) +OUT 13H TO PORT 3C0H +OUT value TO PORT 3C0H (where "value" is the + number of pixels to offset) +----------------------------------------------- +*/ + } /* @@ -168,9 +213,9 @@ void setActiveStart(unsigned offset) void setVisibleStart(unsigned offset) { visStart = offset; - outpw(CRTC_ADDR, 0x0C); /* set high byte */ + outpw(CRTC_ADDR, 0x0C); /* set high byte */ outpw(CRTC_ADDR+1, visStart >> 8); - outpw(CRTC_ADDR, 0x0D); /* set low byte */ + outpw(CRTC_ADDR, 0x0D); /* set low byte */ outpw(CRTC_ADDR+1, visStart & 0xff); } @@ -229,24 +274,168 @@ void set320x240x256_X(void) /* Modify the vertical timing registers to reflect the increased vertical resolution, and to center the image as good as possible: */ - outpw(0x3D4, 0x2C11); /* turn off write protect */ - outpw(0x3D4, 0x0D06); /* vertical total */ - outpw(0x3D4, 0x3E07); /* overflow register */ - outpw(0x3D4, 0xEA10); /* vertical retrace start */ - outpw(0x3D4, 0xAC11); /* vertical retrace end AND wr.prot */ - outpw(0x3D4, 0xDF12); /* vertical display enable end */ - outpw(0x3D4, 0xE715); /* start vertical blanking */ - outpw(0x3D4, 0x0616); /* end vertical blanking */ + outpw(0x3D4, 0x2C11); /* turn off write protect */ + outpw(0x3D4, 0x0D06); /* vertical total */ + outpw(0x3D4, 0x3E07); /* overflow register */ + outpw(0x3D4, 0xEA10); /* vertical retrace start */ + outpw(0x3D4, 0xAC11); /* vertical retrace end AND wr.prot */ + outpw(0x3D4, 0xDF12); /* vertical display enable end */ + outpw(0x3D4, 0xE715); /* start vertical blanking */ + outpw(0x3D4, 0x0616); /* end vertical blanking */ /* Update mode info, so future operations are aware of the resolution */ height = 240; +//*$pragma aux mxSetVirtualScreen "MXSETVIRTUALSCREEN" +//mxSetVirtualScreen(480,360); } /*-----------XXXX-------------*/ + +///////////////////////////////////////////////////////////////////////////// +// // +// WaitRetrace() - This waits until you are in a Verticle Retrace. // +// // +///////////////////////////////////////////////////////////////////////////// +void wait_for_retrace(void) +{ + while (!(inp(STATUS_ADDR) & 0x08)); +} + +///////////////////////////////////////////////////////////////////////////// +// // +// MoveTo() - This moves to position X*4 on a chain 4 screen. // +// Note: As soon as I find documentation, this function // +// will be better documented. - Snowman // +// // +///////////////////////////////////////////////////////////////////////////// +/* +void MoveTo (word X, word Y) { + +// word O = Y*SIZE*2+X; + word O = Y*widthBytes*2+X; + + asm { + mov bx, [O] + mov ah, bh + mov al, 0x0C + + mov dx, 0x3D4 + out dx, ax + + mov ah, bl + mov al, 0x0D + mov dx, 0x3D4 + out dx, ax + } + +;----------------------------------------------------------- +; +; MXPN.ASM - Panning function +; Copyright (c) 1993,1994 by Alessandro Scotti +; +;----------------------------------------------------------- +;WARN PRO +#pragma aux mxPan = \ +"INCLUDE MODEX.DEF" \ + + + +"EXTRN mxWaitDisplay : FAR" \ +"EXTRN mxStartAddress : FAR" \ + +"MX_TEXT SEGMENT USE16 PARA PUBLIC 'CODE' + ASSUME cs:MX_TEXT, ds:NOTHING, es:NOTHING" \ + +"EXTRN mx_BytesPerLine : WORD" \ + +//;----------------------------------------------------------- +//; +//; Moves the screen. +//; +//; Input: +//; X, Y = new X, Y coordinates of view screen +//; Output: +//; none +//; +mxPan PROC FAR + ARG Y:WORD, \ + X:WORD = ARG_SIZE + ASSUME ds:NOTHING + .enter 0 + + mov ax, [Y] + mul [mx_BytesPerLine] + mov dx, [X] + shr dx, 1 + shr dx, 1 + add ax, dx + push ax ; Push the start address + call mxWaitDisplay + call mxStartAddress + + mov dx, 03DAh ; Set the pixel pan register + in al, dx + mov dx, 03C0h + mov al, 33h + out dx, al + mov al, BYTE PTR [X] + and al, 3 + shl al, 1 + out dx, al + + xor ax, ax + .leave ARG_SIZE +mxPan ENDP + +MX_TEXT ENDS +END + + +} + +//Procedure Play; +void Play() +{ + int loop1,loop2; + int xpos,ypos,xdir,ydir; + //int ch; +// for(loop1=1;loop1<=62;loop1++) + //Pal ((char)loop1,(char)loop1,(char)0,(char)(62-loop1)); // { This sets up the pallette for the pic } + + moveto(0,0,Size); // { This moves the view to the top left hand corner } + +// for(loop1=0;loop1<=3;loop1++) +// for(loop2=0;loop2<=5;loop2++) +// Putpic (loop1*160,loop2*66); // { This places the picture all over the + // chain-4 screen } +// getch(); +// ch=0x0; +// xpos=rand (78)+1; +// ypos=rand (198)+1; // { Random start positions for the view } + xpos=0; + ypos=0; + xdir=1; + ydir=1; +// while(1) +// { + WaitRetrace(); // { Take this out and watch the screen go crazy! } + moveto (xpos,ypos,Size); + xpos=xpos+xdir; + ypos=ypos+ydir; + if( (xpos>79) || (xpos<1))xdir=-xdir; + if( (ypos>199) || (ypos<1))ydir=-ydir; // { Hit a boundry, change + // direction! } +// if(_bios_keybrd(_KEYBRD_READY))ch=getch(); +// if(ch==0x71)break; // 'q' +// if(ch==0x1b)break; // 'ESC' +// } +} +*/ /*tile*/ +//king_crimson's code void putColorBox_X(int x, int y, int w, int h, byte color) { outp(0x3c4, 0x02); @@ -268,86 +457,203 @@ void vScroll(int rows) setVisibleStart(visStart + (rows * width)); } -/*OFFSET = 0 -WHILE NOT FINISHED DO - OFFSET = OFFSET + 80 - IF OFFSET >= (200 * 80) THEN OFFSET = 0 - DRAW TO ROW 200 - SET VGA OFFSET = OFFSET - DRAW TO ROW -1 (was row 0 before scroll) -END WHILE*//* -void scrolly(){ - int OFFSET = 0 - WHILE NOT FINISHED DO - OFFSET = OFFSET + 80 - IF OFFSET >= (240 * 80) THEN OFFSET = 0 - RAW TO ROW 240 - SET VGA OFFSET = OFFSET - DRAW TO ROW -1 (was row 0 before scroll) +void scrolly(int bongy) +{ + int boingy=0; + if(bongy<0) + boingy=-1; + else if(bongy>0) + boingy=1; + + for(int ti=0;ti> 2); + outp(0x3D4, Cols); + //setVisibleStart(visStart + (Cols * height)); + setVisibleStart(visStart + (Cols * width)); +} + +/*To implement smooth horizontal scrolling, you would do the following: +-------------- Horizontal Scrolling ------------ +FOR X = 0 TO 319 DO + SET HPP TO ( X MOD 4 ) + SET VGA OFFSET TO ( X/4 ) +END FOR +------------------------------------------------ + +Okay, no problem at all (although I think you might have to fiddle +around with the HPP a bit to get it right...try different values and +see what works :). + +So, the next problem is with drawing the images off the screen where +they aren't visible and then scrolling them on!!! As it turns out, +there's yet ANOTHER register to accomplish this. This one's called the +offset register (no, not the one I was talking about before, that one +was actually the "start address" register) and it's at + + PORT: 3D4H/3D5H + OFFSET: 13H + +and here's how to use it + +-------------- Offset Register --------------- +OUT 13H TO PORT 3D4H +OUT value TO PORT 3D5H +---------------------------------------------- + +Now, what my VGA reference says is that this register holds the number +of bytes (not pixels) difference between the start address of each row. +So, in X-mode it normally contains the value 80 (as we remember, +80 bytes * 4 planes = 320 pixels). This register does not affect the +VISIBLE width of the display, only the difference between addresses on +each row. + +When we scroll horizontally, we need a little bit of extra working space +so we can draw off the edge of the screen. + +Perhaps a little diagram will clarify it. The following picture is of a +standard X-mode addressing scheme with the OFFSET register set to 80. + + ROW OFFSET + 0 0 ======================== + 1 80 [ ] + 2 160 [ ] + .. .. [ VISIBLE ] + [ SCREEN ] + [ ] + [ ] + .. .. [ ] + 199 15920 ======================== + +and the next diagram is of a modified addressing scheme with the OFFSET +register set to 82 (to give us 4 extra pixels on each side of the screen) + +ROW OFFSET +0 0 ------========================------ +1 82 | V [ ] V | +2 164 | I [ ] I | +.. .. | N S [ VISIBLE ] N S | + | O I [ SCREEN ] O I | + | T B [ ] T B | + | L [ ] L | +.. .. | E [ ] E | +199 16318 ------========================------ + +Beautiful!!! + +As with vertical scrolling, however, you still have the problem of when +you reach the bottom of page 4...and it's fixed in the same manner. + +I haven't actually managed to get infinite horizontal scrolling working, +but the method I have just stated will give you a horizontal scrolling +range of over 200 screens!!!! So if you need more (which is extremely +unlikely), figure it out yourself. + + +------------------ +COMBINED SCROLLING +------------------ +To do both horizontal and vertical scrolling, all you have to do is combine +the two methods with a few little extras (it's always the way isn't it). + +You have to start off with the original screen on the current page and the +next page as well. When you scroll horizontally, you have to draw the edge +that's coming in to the screen to BOTH pages (that means you'll be drawing +the incoming edge twice, once for each page). You do this so that when you +have scrolled vertically down through a complete page, you can jump back +to the first page and it will (hopefully) have an identical copy, and you +can then continue scrolling again. + +I'm sorry about this being so confusing but it's a bit difficult to explain. + + */ -//--------------------------------------------------- -// -// Use the bios to get the address of the 8x8 font -// -// You need a font if you are going to draw text. -// - -int far * -getFont() +int loadfontX(char *fname) { - union REGPACK rg; - int seg; - int off; - memset(&rg, 0, sizeof(rg)); - - rg.w.ax = 0x1130; - rg.h.bh = 0x03; - intr(0x10, &rg); - seg = rg.w.es; - off = rg.w.bp; - - - return (int far *)MK_FP(seg, off); + FILE *fp; + + fp = fopen(fname, "rb"); + + if (fp == NULL) { + return 0; + } else { + fread(Xfont, 8, 256, fp); + fclose(fp); + return 1; + } } -void drawChar(int x, int y, int color, byte c) +void putchX(cord x, cord y, char c, byte color) { - int i, j; - int mask; - int far *font = getFont() + (c * 8); - - for (i = 0; i < 8; i++) - { - mask = *font; - for (j = 0; j < 8; j++) - { - if (mask & 0x80) - { - //pixel(x + j, y + i, color); - putPixel_X(x + j, y + i, color); - } - mask <<= 1; - } - font++; - } + int i; + byte *vga_ptr; + byte *font_ptr; + byte temp; + + // 8x8 font + vga_ptr = RowsX[y << 3] + (x << 1) + actStart; + write_plane = -1; + + font_ptr = Xfont + (c << 3); + + i=8; + while (i--) { + temp = *font_ptr++; + outpw(SEQU_ADDR, text_mask[temp & 0x0F]); + *vga_ptr++ = color; + + outpw(SEQU_ADDR, text_mask[temp >> 4]); + *vga_ptr-- = color; + vga_ptr += widthBytes; + } } -void drawText(int x, int y, int color, byte string) +void putstringX(cord x, cord y, char *str, byte color) { - while (string) - { - drawChar(x, y, color, string); - x += 8; - string++; + int i, skip; + byte *vga_ptr; + byte *font_ptr; + byte c, temp; + + // 8x8 font + vga_ptr = RowsX[y << 3] + (x << 1) + actStart; + write_plane = -1; + + skip = 2 - (widthBytes << 3); + + while (c = *str++) { + font_ptr = Xfont + (c << 3); + + i=8; + while (i--) { + temp = *font_ptr++; + outpw(SEQU_ADDR, text_mask[temp & 0x0F]); + *vga_ptr++ = color; + + outpw(SEQU_ADDR, text_mask[temp >> 4]); + *vga_ptr-- = color; + vga_ptr += widthBytes; } + + vga_ptr += skip; + } } ///////////////////////////////////////////////////////////////////////////// -// // -// setvideo() - This function Manages the video modes // -// // +// // +// setvideo() - This function Manages the video modes // +// // ///////////////////////////////////////////////////////////////////////////// void setvideo(/*byte mode, */int vq){ union REGS in, out; @@ -372,7 +678,7 @@ void setvideo(/*byte mode, */int vq){ ///////////////////////////////////////////////////////////////////////////// // // // cls() - This clears the screen to the specified color, on the VGA or on // -// the Virtual screen. // +// the Virtual screen. // // // ///////////////////////////////////////////////////////////////////////////// void cls(byte color, byte *Where){ @@ -391,7 +697,7 @@ int colortest(){ //color ‚Ä‚·‚Æ int colorz(){ if(gq < HGQ){ -//---- cls(gq, vaddr); +//---- cls(gq, vaddr); cls(gq, vga); gq++; }else gq = LGQ; @@ -419,45 +725,45 @@ void ssd(int svq){ /*-----------ding-------------*/ int ding(int q){ - if(yy1){ - xx+=TILEWH; +// xx+=TILEWH; + xx++; } if(!bakay){ - yy-=TILEWH; +// yy-=TILEWH; + yy--; }else if(bakay>1){ - yy+=TILEWH; +// yy+=TILEWH; + yy++; } } } // fixer // if(q!=16){ //if(q!=16) - if(xx<0) xx=width; +// if(xx<(0/*-(TILEWH/2)*/)) xx=(width/*+(TILEWH)*/); if(yy<0) yy=(height*3); - if(xx>width) xx=0; +// if(xx>(width/*+(TILEWH)*/)) xx=(0/*-(TILEWH/2)*/); if(yy>(height*3)) yy=0; // } @@ -541,22 +851,25 @@ int ding(int q){ //printf("%d %d %d %d %d %d\n", xx, yy, tx, ty, TILEWH); // plot the pixel -//---- ppf(xx, yy, coor, vga); - }else if(xx>=0 && xx=0 && yy=0 && xx=0 && yy<(height*3))*/{ +// putColorBox_X(xx, yy, TILEWH, TILEWH, coor); +//++++0000 + putPixel_X(xx, yy, coor); } -//---- if(q==2) ppf(rand()%, rand()%height, 0, vga); - if(q==2) putColorBox_X(rand()%width, rand()%(height*3), TILEWH, TILEWH, 0); +//---- if(q==2) ppf(rand()%, rand()%height, 0, vga); +// if(q==2) putColorBox_X(rand()%width, rand()%(height*3), TILEWH, TILEWH, 0); +//++++0000 + if(q==2) putPixel_X(rand()%width, rand()%(height*3), 0); if(q==16) putPixel_X(rand()%width, rand()%(height*3), 0); if(q==2||q==4||q==16){ bakax = rand()%3; bakay = rand()%3; } gq++; -//if(xx<0||xx>320||yy<0||yy>240) -// printf("%d %d %d %d %d %d\n", xx, yy, coor, bakax, bakay, getPixel_X(xx,yy)); -// printf("%d\n", getPixel_X(xx,yy)); +//if(xx<0||xx>320||yy<0||yy>(height*3)) +// printf("%d %d %d %d %d %d\n", xx, yy, coor, bakax, bakay, getPixel_X(xx,yy)); +// printf("%d\n", getPixel_X(xx,yy)); //0000 -// drawText(0, 0, 15, getPixel_X(xx,yy)); +// drawText(0, 0, 15, getPixel_X(xx,yy)); }else gq = LGQ; return gq; } @@ -578,8 +891,9 @@ void doTest(void) /* This is the way to calculate the number of pages available. */ pages = 65536L/(widthBytes*height); // apparently this takes the A000 address +// if(height==240) pages++; - printf("%d\n", pages); +// printf("%d\n", pages); for (p = 0; p <= pages; ++p) { @@ -593,31 +907,28 @@ void doTest(void) { putPixel_X(x, 0, p+1); if(p!=pages) putPixel_X(x, height-1, p+1); - else putPixel_X(x, 99-1, p+1); + else if(height==240) putPixel_X(x, 99-1, p+1); } for (y = 0; y <= height; ++y) { putPixel_X(0, y, p+1); if(p!=pages) putPixel_X(width-1, y, p+1); - else putPixel_X(width-1, y, p+1); + else if(height==240) putPixel_X(width-1, y, p+1); } - for (x = 0; x < 16; ++x) - for (y = 0; y < 16; ++y) - putPixel_X(x+(p+2)*16, y+(p+2)*16, x + y*16); + for (x = 0; x < TILEWH; ++x) + for (y = 0; y < TILEWH; ++y) + putPixel_X(x+(p+2)*16, y+(p+2)*TILEWH, x + y*TILEWH); //} -// drawText(0, 0, 15, p); - } /* Each pages will now contain a different image. Let the user cycle through all the pages by pressing a key. */ - for (p = 0; p <= pages; ++p) + for (p = 0; p < pages; ++p) { setVisiblePage(p); - //drawText(0, 240, 15, "bakapi"); getch(); } @@ -630,24 +941,30 @@ void doTest(void) int main(void) { int key,d; + //short int temp; // main variables d=1; // switch variable key=4; // default screensaver number -// puts("First, have a look at the 320x200 mode. I will draw some rubbish"); -// puts("on all of the four pages, then let you cycle through them by"); -// puts("hitting a key on each page."); -// puts("Press a key when ready..."); -// getch(); +// puts("First, have a look at the 320x200 mode. I will draw some rubbish"); +// puts("on all of the four pages, then let you cycle through them by"); +// puts("hitting a key on each page."); +// puts("Press a key when ready..."); +// getch(); -// doTest(); +// doTest(); -// puts("Then, check out Mode X, 320x240 with 3 (and a half) pages."); -// puts("Press a key when ready..."); -// getch(); +// puts("Then, check out Mode X, 320x240 with 3 (and a half) pages."); +// puts("Press a key when ready..."); +// getch(); //++++0000 setvideo(1); -//mxInit(); + /*temp = loadfontX("vga8x8.fnt"); + + if (temp) { + putstringX(0, 0, "bakapi!", 2); + } + getch();*/ // screen savers /*while(d!=0){ // on! @@ -668,16 +985,25 @@ int main(void) } //end of screen savers doTest(); +// getch(); while(!kbhit()){ // conditions of screen saver - vScroll(-1); + hScroll(1); +// scrolly(1); +// vScroll(1); +// delay(100); +// Play(); } //++++0000 setvideo(0); + printf("Resolution:\n[%d][%d]\n", width,height); +// setvideo(0); //mxTerm(); -//mxGetVersion(); - puts("Where to next? It's your move! wwww"); - printf("bakapi ver. 1.04.09.02\nis made by sparky4i†ƒÖ…j feel free to use it ^^\nLicence: GPL v2\n"); +//mxGetVersion(); + + printf("[%d]\n", mxGetVersion()); + puts("where to next? It's your move! wwww"); + printf("bakapi ver. 1.04.09.03\nis made by sparky4i†ƒÖ…j feel free to use it ^^\nLicence: GPL v2\n"); return 0; }