Index: head/usr.bin/doscmd/ems.c =================================================================== --- head/usr.bin/doscmd/ems.c (revision 82324) +++ head/usr.bin/doscmd/ems.c (revision 82325) @@ -1,1692 +1,1692 @@ /*- * Copyright (c) 1997 Helmut Wirth * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice immediately at the beginning of the file, witout modification, * this list of conditions, and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ /* * EMS memory emulation * * To emulate Expanded Memory we use a DOS driver (emsdriv.sys) which * routes calls to int 0x67 to this emulator routine. The main entry point * is ems_entry(..). The emulator needs to be initialized before the first * call. The first step of the initialization is done during program startup * the second part is done during DOS boot, from a call of the DOS driver. * The DOS driver is neccessary because DOS programs look for it to * determine if EMS is available. * * To emulate a configurable amount of EMS memory we use a file created * at startup with the size of the configured EMS memory. This file is * mapped into the EMS window like any DOS memory manager would do, using * mmap calls. * * The emulation follows the LIM EMS 4.0 standard. Not all functions of it * are implemented yet. The "alter page map and jump" and "alter page map * and call" functions are not implemented, because they are rather hard to * do. (It would mean a call to the emulator executes a routine in EMS * memory and returns to the emulator, the emulator switches the page map * and then returns to the DOS program.) LINUX does not emulate this * functions and I think they were very rarely used by DOS applications. * * Credits: To the writers of LINUX dosemu, I looked at their code */ #include #include #include #include #include "doscmd.h" #include "ems.h" /* Will be configurable */ u_long ems_max_size = EMS_MAXSIZE * 1024; u_long ems_frame_addr = EMS_FRAME_ADDR; /* * Method for EMS: Allocate a mapfile with the size of EMS memory * and map the needed part into the page frame */ #define EMS_MAP_PATH "/var/tmp/" /* Use a big file system */ #define EMS_MAP_FILE "doscmd.XXXXXX" static int mapfile_fd = -1; /* Pages are always 16 kB in size. The page frame is 64 kB, there are * 4 positions (0..3) for a page to map in. The pages are numbered from 0 to * the highest 16 kB page in the mapfile, depending on the EMS size */ EMS_mapping_context ems_mapping_context; /* Handle and page management (see ems.h) */ /* The handle array. If the pointer is NULL, the handle is unallocated */ static EMS_handle *ems_handle[EMS_NUM_HANDLES]; static u_long ems_alloc_handles; /* The active handle, if any */ static short active_handle; /* The page array. It is malloced at runtime, depending on the total * allocation size */ static EMS_page *ems_page = NULL; static u_long ems_total_pages; static u_long ems_alloc_pages; static u_long ems_free_pages; /* Local structure used for region copy and move operations */ struct copydesc { #define SRC_EMS 1 #define DST_EMS 2 short copytype; /* Type of source and destination memory */ EMS_addr src_addr; /* Combined pointer for source */ EMS_addr dst_addr; /* Combined pointer for destination */ u_long rest_len; /* Lenght to copy */ }; /* Local prototypes */ static int init_mapfile(void); static void map_page(u_long, u_char, short, int); static EMS_handle *get_new_handle(long); static void context_to_handle(short); static long find_next_free_handle(void); static short lookup_handle(Hname *hp); static void allocate_pages_to_handle(u_short, long); static void allocate_handle(short, long); static void reallocate_pages_to_handle(u_short, long); static void free_handle(short); static void free_pages_of_handle(short); static void restore_context(EMS_mapping_context *); static void save_context_to_dos(EMScontext *); static int check_saved_context(EMScontext *); static void *get_valid_pointer(u_short, u_short, u_long); static u_long move_ems_to_conv(short, u_short, u_short, u_long, u_long); static u_long move_conv_to_ems(u_long, u_short, u_short, u_short, u_long); static u_long move_ems_to_ems(u_short, u_short, u_short, u_short, u_short, u_short, u_long); /* * EMS initialization routine: Return 1, if successful, return 0 if * init problem or EMS disabled */ int ems_init() { unsigned i; if (ems_max_size == 0) return 0; if (init_mapfile() == 0) return 0; /* Sanity */ bzero((void *)(&ems_handle[0]), sizeof(ems_handle)); ems_total_pages = ems_max_size / EMS_PAGESIZE; ems_alloc_pages = 0; ems_free_pages = ems_total_pages; ems_alloc_handles = 0; active_handle = 0; /* Malloc the page array */ ems_page = (EMS_page *)malloc(sizeof(EMS_page) * ems_total_pages); if (ems_page == NULL) { debug(D_ALWAYS, "Could not malloc page array, EMS disabled\n"); ems_frame_addr = 0; ems_max_size = 0; ems_total_pages = 0; return 0; } for (i = 0; i < ems_total_pages; i++) { ems_page[i].handle = 0; ems_page[i].status = EMS_FREE; } debug(D_EMS, "EMS: Emulation init OK.\n"); return 1; } /* Main entry point */ void ems_entry(regcontext_t *REGS) { /* * If EMS is not enabled, the DOS ems.exe module should not have * been loaded. If it is loaded anyway, report software malfunction */ if (ems_max_size == 0) { R_AH = EMS_SW_MALFUNC; debug(D_EMS, "EMS emulation not enabled\n"); return; } switch (R_AH) { case GET_MANAGER_STATUS: debug(D_EMS, "EMS: Get manager status\n"); R_AH = EMS_SUCCESS; break; case GET_PAGE_FRAME_SEGMENT: debug(D_EMS, "EMS: Get page frame segment\n"); R_BX = ems_frame_addr >> 4; R_AH = EMS_SUCCESS; break; case GET_PAGE_COUNTS: R_BX = ems_total_pages - ems_alloc_pages; R_DX = ems_total_pages; debug(D_EMS, "EMS: Get page count: Returned total=%d, free=%d\n", R_DX, R_BX); R_AH = EMS_SUCCESS; break; case GET_HANDLE_AND_ALLOCATE: { u_short npages; short handle; npages = R_BX; debug(D_EMS, "EMS: Get handle and allocate %d pages: ", npages); /* Enough handles? */ if ((handle = find_next_free_handle()) < 0) { debug(D_EMS,"Return error:No handles\n"); R_AH = EMS_OUT_OF_HANDLES; break; } /* Enough memory for this request ? */ if (npages > ems_free_pages) { debug(D_EMS,"Return error:Request too big\n"); R_AH = EMS_OUT_OF_LOG; break; } if (npages > ems_total_pages) { debug(D_EMS,"Return error:Request too big\n"); R_AH = EMS_OUT_OF_PHYS; break; } /* Not allowed to allocate zero pages with this function */ if (npages == 0) { debug(D_EMS,"Return error:Cannot allocate 0 pages\n"); R_AH = EMS_ZERO_PAGES; break; } /* Allocate the handle */ allocate_handle(handle, npages); /* Allocate the pages */ allocate_pages_to_handle(handle, npages); R_DX = handle; R_AH = EMS_SUCCESS; debug(D_EMS,"Return success:Handle = %d\n", handle); break; } case MAP_UNMAP: { u_char position; u_short hpagenum, spagenum; short handle; debug(D_EMS, "EMS: Map/Unmap handle=%d, pos=%d, pagenum=%d ", R_DX, R_AL, R_BX); handle = R_DX; position = R_AL; if (position > 3) { debug(D_EMS, "invalid position\n"); R_AH = EMS_ILL_PHYS; break; } hpagenum = R_BX; /* This succeeds without a valid handle ! */ if (hpagenum == 0xffff) { /* Unmap only */ map_page(0, position, handle, 1); debug(D_EMS, "(unmap only) success\n"); R_AH = EMS_SUCCESS; break; } if (handle > 255 || handle == 0 || ems_handle[handle] == NULL) { R_AH = EMS_INV_HANDLE; debug(D_EMS, "invalid handle\n"); break; } if (hpagenum >= ems_handle[handle]->npages) { R_AH = EMS_LOGPAGE_TOOBIG; debug(D_EMS, "invalid pagenumber\n"); break; } spagenum = ems_handle[handle]->pagenum[hpagenum]; map_page(spagenum, position, handle, 0); debug(D_EMS, "success\n"); R_AH = EMS_SUCCESS; break; } case DEALLOCATE_HANDLE: { short handle; /* Handle valid ? */ handle = R_DX; debug(D_EMS, "EMS: Deallocate handle %d\n", handle); if (handle > 255 || ems_handle[handle] == NULL) { R_AH = EMS_INV_HANDLE; break; } /* Mapping context saved ? */ if (ems_handle[handle]->mcontext != NULL) { R_AH = EMS_SAVED_MAP; break; } free_pages_of_handle(handle); free_handle(handle); R_AH = EMS_SUCCESS; break; } case GET_EMM_VERSION: debug(D_EMS, "EMS: Get version\n"); R_AL = EMS_VERSION; R_AH = EMS_SUCCESS; break; case SAVE_PAGE_MAP: { short handle; debug(D_EMS, "EMS: Save page map\n"); handle = R_DX; if (handle > 255 || handle == 0 || ems_handle[handle] == NULL) { R_AH = EMS_INV_HANDLE; break; } if (ems_handle[handle]->mcontext != NULL) { /* There is already a context saved */ if (memcmp((void *)ems_handle[handle]->mcontext, (void *)&ems_mapping_context, sizeof(EMS_mapping_context)) == 0) R_AH = EMS_ALREADY_SAVED; else R_AH = EMS_NO_ROOM_TO_SAVE; break; } context_to_handle(handle); R_AH = EMS_SUCCESS; break; } case RESTORE_PAGE_MAP: { short handle; debug(D_EMS, "EMS: Restore page map\n"); handle = R_DX; if (handle > 255 || handle == 0 || ems_handle[handle] == NULL) { R_AH = EMS_INV_HANDLE; break; } if (ems_handle[handle]->mcontext == NULL) { R_AH = EMS_NO_SAVED_CONTEXT; break; } restore_context(ems_handle[handle]->mcontext); free((void *)ems_handle[handle]->mcontext); ems_handle[handle]->mcontext = NULL; R_AH = EMS_SUCCESS; break; } case RESERVED_1: case RESERVED_2: debug(D_ALWAYS, "Reserved function called: %02x\n", R_AH); R_AH = EMS_FUNC_NOSUP; break; case GET_HANDLE_COUNT: debug(D_EMS, "EMS: Get handle count\n"); R_BX = ems_alloc_handles + 1; R_AH = EMS_SUCCESS; break; case GET_PAGES_OWNED: { short handle; debug(D_EMS, "EMS: Get pages owned\n"); /* Handle valid ? */ handle = R_DX; if (handle > 255 || ems_handle[handle] == NULL) { R_AH = EMS_INV_HANDLE; break; } if (handle == 0) R_BX = 0; else R_BX = ems_handle[handle]->npages; R_AH = EMS_SUCCESS; break; } case GET_PAGES_FOR_ALL: { EMShandlepage *ehp; unsigned safecount; int i; debug(D_EMS, "EMS: Get pages for all\n"); /* Get the address passed from DOS app */ ehp = (EMShandlepage *)get_valid_pointer(R_ES, R_DI, sizeof(EMShandlepage) * ems_alloc_handles); if (ehp == NULL) { R_AH = EMS_SW_MALFUNC; break; } R_BX = ems_alloc_handles; safecount = 0; for (i = 0; i < 255; i++) { if (ems_handle[i] != NULL) { if (safecount > (ems_alloc_handles+1)) fatal("EMS: ems_alloc_handles is wrong, " "cannot continue\n"); ehp->handle = i; ehp->npages = ems_handle[i]->npages; ehp++; safecount++; } } R_AH = EMS_SUCCESS; break; } case PAGE_MAP: /* This function is a nuisance. It was invented to save time and * memory, but in our case it is useless. We have to support it * but we use the same save memory as for the page map function. * It uses only 20 bytes anyway. We store/restore the entire mapping */ case PAGE_MAP_PARTIAL: { int subfunction; EMScontext *src, *dest; debug(D_EMS, "EMS: Page map "); subfunction = R_AL; if (R_AH == PAGE_MAP_PARTIAL) { debug(D_EMS, "partial "); /* Page map partial has slightly different subfunctions * GET_SET does not exist and is GET_SIZE in this case */ if (subfunction == GET_SET) subfunction = GET_SIZE; } switch (subfunction) { case GET: { debug(D_EMS, "get\n"); /* Get the address passed from DOS app */ dest = (EMScontext *)get_valid_pointer(R_ES, R_DI, sizeof(EMScontext)); if (dest == NULL) { R_AH = EMS_SW_MALFUNC; break; } save_context_to_dos(dest); R_AH = EMS_SUCCESS; break; } case SET: { debug(D_EMS, "set\n"); src = (EMScontext *)get_valid_pointer(R_DS, R_SI, sizeof(EMScontext)); if (src == NULL) { R_AH = EMS_SW_MALFUNC; break; } if (check_saved_context(src) == 0) { R_AH = EMS_SAVED_CONTEXT_BAD; break; } restore_context(&src->ems_saved_context); R_AH = EMS_SUCCESS; break; } case GET_SET: { debug(D_EMS, "get/set\n"); dest = (EMScontext *)get_valid_pointer(R_ES, R_DI, sizeof(EMScontext)); if (dest == NULL) { R_AH = EMS_SW_MALFUNC; break; } save_context_to_dos(dest); src = (EMScontext *)get_valid_pointer(R_DS, R_SI, sizeof(EMScontext)); if (src == NULL) { R_AH = EMS_SW_MALFUNC; break; } if (check_saved_context(src) == 0) { R_AH = EMS_SAVED_CONTEXT_BAD; break; } restore_context(&src->ems_saved_context); R_AH = EMS_SUCCESS; break; } case GET_SIZE: debug(D_EMS, "get size\n"); R_AL = (sizeof(EMScontext) + 1) & 0xfe; R_AH = EMS_SUCCESS; break; default: debug(D_EMS, "invalid subfunction\n"); R_AH = EMS_INVALID_SUB; break; } break; } case MAP_UNMAP_MULTI_HANDLE: { u_char position; u_short hpagenum, spagenum; short handle; EMSmapunmap *mp; int n_entry, i; debug(D_EMS, "EMS: Map/Unmap multiple "); if ((n_entry = R_CX) > 3) { R_AH = EMS_ILL_PHYS; } /* This is valid according to the LIM EMS 4.0 spec */ if (n_entry == 0) { R_AH = EMS_SUCCESS; break; } handle = R_DX; if (handle > 255 || handle == 0 || ems_handle[handle] == NULL) { R_AH = EMS_INV_HANDLE; break; } mp = (EMSmapunmap *)get_valid_pointer(R_DS, R_SI, sizeof(EMSmapunmap) * n_entry); if (mp == NULL) { R_AH = EMS_SW_MALFUNC; break; } R_AH = EMS_SUCCESS; /* Walk through the table and map/unmap */ for (i = 0; i < n_entry; i++) { hpagenum = mp->log; /* Method is in R_AL */ if (R_AL == 0) { debug(D_EMS, "phys page method\n"); if (mp->phys <= 3) { position = mp->phys; } else { R_AH = EMS_ILL_PHYS; break; } } else if (R_AL == 1) { /* Compute position from segment address */ u_short p_seg; debug(D_EMS, "segment method\n"); p_seg = mp->phys; p_seg -= ems_frame_addr; p_seg /= EMS_PAGESIZE; if (p_seg <= 3) { position = p_seg; } else { R_AH = EMS_ILL_PHYS; break; } } else { debug(D_EMS, "invalid subfunction\n"); R_AH = EMS_INVALID_SUB; break; } mp++; if (hpagenum == 0xffff) { /* Unmap only */ map_page(0, position, handle, 1); continue; } if (hpagenum >= ems_handle[handle]->npages) { R_AH = EMS_LOGPAGE_TOOBIG; break; } spagenum = ems_handle[handle]->pagenum[hpagenum]; map_page(spagenum, position, handle, 0); } break; } case REALLOC_PAGES: { short handle; u_long newpages; debug(D_EMS, "EMS: Realloc pages "); handle = R_DX; if (handle > 255 || handle == 0 || ems_handle[handle] == NULL) { R_AH = EMS_INV_HANDLE; debug(D_EMS, "invalid handle\n"); break; } newpages = R_BX; debug(D_EMS, "changed from %ld to %ld pages\n", ems_handle[handle]->npages, newpages); /* Case 1: Realloc to zero pages */ if (newpages == 0) { free_pages_of_handle(handle); R_AH = EMS_SUCCESS; break; } /* Case 2: New allocation is equal to allocated number */ if (newpages == ems_handle[handle]->npages) { R_AH = EMS_SUCCESS; break; } /* Case 3: Reallocate to bigger and smaller sizes */ if (newpages > ems_handle[handle]->npages) { if (newpages > ems_free_pages) { R_AH = EMS_OUT_OF_LOG; break; } if (newpages > ems_total_pages) { R_AH = EMS_OUT_OF_PHYS; break; } } reallocate_pages_to_handle(handle, newpages); R_AH = EMS_SUCCESS; break; } /* We do not support nonvolatile pages */ case HANDLE_ATTRIBUTES: debug(D_EMS, "Handle attributes called\n"); switch (R_AL) { case GET: case SET: R_AH = EMS_FEAT_NOSUP; break; case HANDLE_CAPABILITY: R_AL = 0; /* Volatile only */ R_AH = EMS_SUCCESS; break; default: R_AH = EMS_FUNC_NOSUP; break; } break; case HANDLE_NAME: { short handle; Hname *hp; handle = R_DX; if (handle > 255 || handle == 0 || ems_handle[handle] == NULL) { R_AH = EMS_INV_HANDLE; debug(D_EMS, "invalid handle\n"); break; } switch (R_AL) { case GET: if ((hp = (Hname *)get_valid_pointer(R_ES, R_DI, 8)) == NULL) { R_AH = EMS_SW_MALFUNC; break; } *hp = ems_handle[handle]->hname; R_AH = EMS_SUCCESS; break; case SET: if ((hp = (Hname *)get_valid_pointer(R_DS, R_SI, 8)) == NULL) { R_AH = EMS_SW_MALFUNC; break; } /* If the handle name is not 0, it may not exist */ if ((hp->ul_hn[0] | hp->ul_hn[1]) != 0) { if (lookup_handle(hp) == 0) { ems_handle[handle]->hname = *hp; R_AH = EMS_SUCCESS; } else { R_AH = EMS_NAME_EXISTS; break; } } else { /* Name is deleted (set to zeros) */ ems_handle[handle]->hname = *hp; R_AH = EMS_SUCCESS; } break; default: R_AH = EMS_FUNC_NOSUP; break; } break; } case HANDLE_DIRECTORY: { int i; EMShandledir *hdp; Hname *hp; short handle; switch(R_AL) { case GET: hdp = (EMShandledir *)get_valid_pointer(R_ES, R_DI, sizeof(EMShandledir) * ems_alloc_handles); if (hdp == NULL) { R_AH = EMS_SW_MALFUNC; break; } for (i = 0; i < EMS_NUM_HANDLES; i++) { if (ems_handle[i] != NULL) { hdp->log = i; hdp->name = ems_handle[i]->hname; } } R_AH = EMS_SUCCESS; break; case HANDLE_SEARCH: hp = (Hname *)get_valid_pointer(R_DS, R_SI, 8); if (hp == NULL) { R_AH = EMS_SW_MALFUNC; break; } /* Cannot search for NULL handle name */ if ((hp->ul_hn[0] | hp->ul_hn[1]) != 0) { R_AH = EMS_NAME_EXISTS; break; } if ((handle = lookup_handle(hp)) == 0) { R_AH = EMS_HNAME_NOT_FOUND; } else { R_DX = handle; R_AH = EMS_SUCCESS; } break; case GET_TOTAL_HANDLES: R_AH = EMS_SUCCESS; R_BX = EMS_NUM_HANDLES; /* Includes OS handle */ break; default: R_AH = EMS_FUNC_NOSUP; break; } break; } /* I do not know if we need this. LINUX emulation leaves it out * so I leave it out too for now. */ case ALTER_PAGEMAP_JUMP: debug(D_ALWAYS, "Alter pagemap and jump used!\n"); R_AH = EMS_FUNC_NOSUP; break; case ALTER_PAGEMAP_CALL: debug(D_ALWAYS, "Alter pagemap and call used!\n"); R_AH = EMS_FUNC_NOSUP; break; case MOVE_MEMORY_REGION: { EMSmovemem *emvp; u_long src_addr, dst_addr; u_short src_handle, dst_handle; if (R_AL == EXCHANGE) debug(D_EMS, "EMS: Exchange memory region "); else debug(D_EMS, "EMS: Move memory region "); emvp = (EMSmovemem *)get_valid_pointer(R_DS, R_SI, sizeof(EMSmovemem)); if (emvp == NULL) { debug(D_EMS, "Invalid structure pointer\n"); R_AH = EMS_SW_MALFUNC; break; } /* Zero length is not an error */ if (emvp->length == 0) { debug(D_EMS, "Zero length\n"); R_AH = EMS_SUCCESS; break; } /* Some checks */ if (emvp->src_type == EMS_MOVE_CONV) { /* Conventional memory source */ src_addr = MAKEPTR(emvp->src_seg, emvp->src_offset); /* May not exceed conventional memory */ if ((src_addr + emvp->length) > 640 * 1024) { R_AH = EMS_SW_MALFUNC; break; } } else { /* Check the handle */ src_handle = emvp->src_handle; if (src_handle > 255 || src_handle == 0 || ems_handle[src_handle] == NULL) { R_AH = EMS_INV_HANDLE; debug(D_EMS, "invalid source handle\n"); break; } /* Offset may not exceed page size */ if (emvp->src_offset >= (16 * 1024)) { R_AH = EMS_PAGEOFFSET; debug(D_EMS, "source page offset too big\n"); break; } } if (emvp->dst_type == EMS_MOVE_CONV) { /* Conventional memory source */ dst_addr = MAKEPTR(emvp->dst_seg, emvp->dst_offset); /* May not exceed conventional memory */ if ((dst_addr + emvp->length) > 640 * 1024) { R_AH = EMS_SW_MALFUNC; break; } } else { /* Check the handle */ dst_handle = emvp->dst_handle; if (dst_handle > 255 || dst_handle == 0 || ems_handle[dst_handle] == NULL) { R_AH = EMS_INV_HANDLE; debug(D_EMS, "invalid destination handle\n"); break; } /* Offset may not exceed page size */ if (emvp->dst_offset >= (16 * 1024)) { R_AH = EMS_PAGEOFFSET; debug(D_EMS, "destination page offset too big\n"); break; } } if (R_AL == MOVE) { /* If it is conventional memory only, do it */ if (emvp->src_type == EMS_MOVE_CONV && emvp->dst_type == EMS_MOVE_CONV) { memmove((void *)dst_addr, (void *)src_addr, (size_t) emvp->length); debug(D_EMS, "conventional to conventional memory done\n"); R_AH = EMS_SUCCESS; break; } if (emvp->src_type == EMS_MOVE_EMS && emvp->dst_type == EMS_MOVE_CONV) R_AH = move_ems_to_conv(src_handle, emvp->src_seg, emvp->src_offset, dst_addr, emvp->length); else if (emvp->src_type == EMS_MOVE_CONV && emvp->dst_type == EMS_MOVE_EMS) R_AH = move_conv_to_ems(src_addr, dst_handle, emvp->dst_seg, emvp->dst_offset, emvp->length); else R_AH = move_ems_to_ems(src_handle, emvp->src_seg, emvp->src_offset, dst_handle, emvp->dst_seg, emvp->dst_offset, emvp->length); debug(D_EMS, " done\n"); break; } else { /* exchange memory region */ /* We need a scratch area for the exchange */ void *buffer; if ((buffer = malloc(emvp->length)) == NULL) fatal("EMS: Could not malloc scratch area for exchange"); /* If it is conventional memory only, do it */ if (emvp->src_type == EMS_MOVE_CONV && emvp->dst_type == EMS_MOVE_CONV) { /* destination -> buffer */ memmove(buffer, (void *)dst_addr, (size_t) emvp->length); /* Source -> destination */ memmove((void *)dst_addr, (void *)src_addr, (size_t) emvp->length); /* Buffer -> source */ memmove((void *)src_addr, buffer, (size_t) emvp->length); free(buffer); debug(D_EMS, "conventional to conventional memory done\n"); R_AH = EMS_SUCCESS; break; } /* Exchange EMS with conventional */ if (emvp->src_type == EMS_MOVE_EMS && emvp->dst_type == EMS_MOVE_CONV) { /* Destination -> buffer */ memmove(buffer, (void *)dst_addr, (size_t) emvp->length); /* Source -> destination */ R_AH = move_ems_to_conv(src_handle, emvp->src_seg, emvp->src_offset, dst_addr, emvp->length); if (R_AH != EMS_SUCCESS) { free(buffer); break; } /* Buffer -> source */ R_AH = move_conv_to_ems((u_long)buffer, src_handle, emvp->src_seg, emvp->src_offset, emvp->length); /* Exchange conventional with EMS */ } else if (emvp->src_type == EMS_MOVE_CONV && emvp->dst_type == EMS_MOVE_EMS) { /* Destination -> buffer */ R_AH = move_ems_to_conv(dst_handle, emvp->dst_seg, emvp->dst_offset, (u_long)buffer, emvp->length); if (R_AH != EMS_SUCCESS) { free(buffer); break; } /* Source -> destination */ R_AH = move_conv_to_ems((u_long)buffer, dst_handle, emvp->dst_seg, emvp->dst_offset, emvp->length); /* Buffer -> source */ memmove(buffer, (void *)src_addr, (size_t) emvp->length); /* Exchange EMS with EMS */ } else { /* Destination -> buffer */ R_AH = move_ems_to_conv(dst_handle, emvp->dst_seg, emvp->dst_offset, (u_long)buffer, emvp->length); if (R_AH != EMS_SUCCESS) { free(buffer); break; } /* Source -> destination */ R_AH = move_ems_to_ems(src_handle, emvp->src_seg, emvp->src_offset, dst_handle, emvp->dst_seg, emvp->dst_offset, emvp->length); if (R_AH != EMS_SUCCESS) { free(buffer); break; } /* Buffer -> source */ R_AH = move_conv_to_ems((u_long)buffer, src_handle, emvp->src_seg, emvp->src_offset, emvp->length); } free(buffer); } debug(D_EMS, " done\n"); break; } case GET_MAPPABLE_PHYS_ADDR: { switch (R_AL) { case GET_ARRAY: { EMSaddrarray *eadp; int i; u_short seg; eadp = (EMSaddrarray *)get_valid_pointer(R_ES, R_DI, sizeof(EMSaddrarray) * 4); if (eadp == NULL) { R_AH = EMS_SW_MALFUNC; break; } for (i = 0, seg = (ems_frame_addr >> 4); i < 4; i++) { eadp->segm = seg; eadp->phys = i; eadp++; seg += 1024; } R_AH = EMS_SUCCESS; break; } case GET_ARRAY_ENTRIES: /* There are always 4 positions, 4*16kB = 64kB */ R_CX = 4; R_AH = EMS_SUCCESS; break; default: R_AH = EMS_FUNC_NOSUP; break; } break; } /* This is an OS function in the LIM EMS 4.0 standard: It is * usable only by an OS and its use can be disabled for all other * programs. I think we do not need to support it. It is not * implemented and it reports "disabled" to any caller. */ case GET_HW_CONFIGURATION: R_AH = EMS_FUNCTION_DISABLED; break; /* This function is a little different, it was defined with * LIM EMS 4.0: It is allowed to allocate zero pages and raw * page size (i.e. page size != 16kB) is supported. We have * only 16kB pages, so the second difference does not matter. */ case ALLOCATE_PAGES: { u_short npages; short handle; npages = R_BX; debug(D_EMS, "EMS: Get handle and allocate %d pages: ", npages); /* Enough handles? */ if ((handle = find_next_free_handle()) < 0) { debug(D_EMS,"Return error:No handles\n"); R_AH = EMS_OUT_OF_HANDLES; break; } /* Enough memory for this request ? */ if (npages > ems_free_pages) { debug(D_EMS,"Return error:Request too big\n"); R_AH = EMS_OUT_OF_LOG; break; } if (npages > ems_total_pages) { debug(D_EMS,"Return error:Request too big\n"); R_AH = EMS_OUT_OF_PHYS; break; } /* Allocate the handle */ allocate_handle(handle, npages); /* Allocate the pages */ allocate_pages_to_handle(handle, npages); R_DX = handle; R_AH = EMS_SUCCESS; debug(D_EMS,"Return success:Handle = %d\n", handle); break; } /* This is an OS function in the LIM EMS 4.0 standard: It is * usable only by an OS and its use can be disabled for all other * programs. I think we do not need to support it. It is not * implemented and it reports "disabled" to any caller. */ case ALTERNATE_MAP_REGISTER: R_AH = EMS_FUNCTION_DISABLED; break; /* We cannot support that ! */ case PREPARE_WARMBOOT: R_AH = EMS_FUNC_NOSUP; break; case OS_FUNCTION_SET: R_AH = EMS_FUNCTION_DISABLED; break; default: debug(D_ALWAYS, "EMS: Unknown function called: %02x\n", R_AH); R_AH = EMS_FUNC_NOSUP; break; } } /* Initialize the EMS memory: Return 1 on success, 0 on failure */ static int init_mapfile() { char path[256]; int mfd; /* Sanity */ if (ems_max_size == 0) return 0; strcpy(path, EMS_MAP_PATH); strcat(path, EMS_MAP_FILE); mfd = mkstemp(path); if (mfd < 0) { debug(D_ALWAYS, "Could not create EMS mapfile, "); goto fail; } unlink(path); mapfile_fd = squirrel_fd(mfd); if (lseek(mapfile_fd, (off_t)(ems_max_size - 1), 0) < 0) { debug(D_ALWAYS, "Could not seek into EMS mapfile, "); goto fail; } if (write(mapfile_fd, "", 1) < 0) { debug(D_ALWAYS, "Could not write to EMS mapfile, "); goto fail; } /* Unmap the entire page frame */ if (munmap((caddr_t)ems_frame_addr, 64 * 1024) < 0) { debug(D_ALWAYS, "Could not unmap EMS page frame, "); goto fail; } /* DOS programs will access the page frame without allocating * pages first. Microsoft diagnose MSD.EXE does this, for example * We need to have memory here to avoid segmentation violation */ if (mmap((caddr_t)ems_frame_addr, 64 * 1024, PROT_EXEC | PROT_READ | PROT_WRITE, - MAP_ANON | MAP_FIXED | MAP_INHERIT | MAP_SHARED, + MAP_ANON | MAP_FIXED | MAP_SHARED, -1, 0) == MAP_FAILED) { debug(D_ALWAYS, "Could not map EMS page frame, "); goto fail; } bzero((void *)&ems_mapping_context, sizeof(EMS_mapping_context)); return (1); fail: debug(D_ALWAYS, "EMS disabled\n"); ems_max_size = 0; ems_frame_addr = 0; return (0); } /* Map/Unmap pages into one of four positions in the frame segment */ static void map_page(u_long pagenum, u_char position, short handle, int unmaponly) { caddr_t map_addr; size_t len; off_t file_offs; if (position > 3) fatal("EMS: Internal error: Mapping position\n"); map_addr = (caddr_t)(ems_frame_addr + (1024 * 16 * (u_long)position)); len = 1024 * 16; file_offs = (off_t)(pagenum * 16 * 1024); if (ems_mapping_context.pos_mapped[position]) { if (munmap(map_addr, len) < 0) { fatal("EMS unmapping error: %s\nCannot recover\n", strerror(errno)); } ems_page[ems_mapping_context.pos_pagenum[position]].status &= ~EMS_MAPPED; ems_mapping_context.pos_mapped[position] = 0; ems_mapping_context.handle[position] = 0; } if (unmaponly) { /* DOS programs will access the page frame without allocating * pages first. Microsoft diagnose MSD.EXE does this, for example * We need to have memory here to avoid segmentation violation */ if (mmap((caddr_t)ems_frame_addr, 64 * 1024, PROT_EXEC | PROT_READ | PROT_WRITE, - MAP_ANON | MAP_FIXED | MAP_INHERIT | MAP_SHARED, + MAP_ANON | MAP_FIXED | MAP_SHARED, -1, 0) == MAP_FAILED) fatal("Could not map EMS page frame during unmap only\n"); return; } if (mmap(map_addr, len, PROT_EXEC | PROT_READ | PROT_WRITE, - MAP_FILE | MAP_FIXED | MAP_INHERIT | MAP_SHARED, + MAP_FILE | MAP_FIXED | MAP_SHARED, mapfile_fd, file_offs) == MAP_FAILED) { fatal("EMS mapping error: %s\nCannot recover\n", strerror(errno)); } ems_mapping_context.pos_mapped[position] = 1; ems_mapping_context.pos_pagenum[position] = pagenum; ems_mapping_context.handle[position] = handle; ems_page[pagenum].status |= EMS_MAPPED; } /* Get a pointer from VM86 app, check it and return it. This returns NULL * if the pointer is not valid. We can check only for very limited * criteria: The pointer and the area defined by size may not point to * memory over 1MB and it may not may to addresses under 1kB, because there * is the VM86 interrupt table. */ static void *get_valid_pointer(u_short seg, u_short offs, u_long size) { u_long addr; addr = MAKEPTR(seg, offs); /* Check bounds */ if ((addr + size) >= (1024 * 1024) || addr < 1024) return NULL; else return (void *)addr; } /* Malloc a new handle */ static EMS_handle *get_new_handle(long npages) { EMS_handle *ehp; size_t dynsize = sizeof(EMS_handle) + sizeof(short) * npages; if ((ehp = calloc(1, dynsize)) == NULL) fatal("Cannot malloc EMS handle, cannot continue\n"); return ehp; } /* Allocate a mapping context to a handle */ static void context_to_handle(short handle) { EMS_mapping_context *emc; if (ems_handle[handle] == NULL) fatal("EMS context_to_handle called with invalid handle\n"); if ((emc = calloc(1, sizeof(EMS_mapping_context))) == NULL) fatal("EMS Cannot malloc mapping context, cannot continue\n"); ems_handle[handle]->mcontext = emc; memmove((void *)emc, (void *)&ems_mapping_context, sizeof(EMS_mapping_context)); } /* Find the next free handle, returns -1 if there are no more handles */ static long find_next_free_handle() { int i; if (ems_alloc_handles >= 255) return (-1); /* handle 0 is OS handle */ for (i = 1; i < EMS_NUM_HANDLES; i++) { if (ems_handle[i] == NULL) return (i); } fatal("EMS handle count garbled, should not happen\n"); /* quiet 'gcc -Wall' */ return (-1); } /* Look for a named handle, returns 0 if not found, else handle */ static short lookup_handle(Hname *hp) { int i; for (i = 1; i < EMS_NUM_HANDLES; i++) { if (ems_handle[i] != NULL) { if (hp->ul_hn[0] == ems_handle[i]->hname.ul_hn[0] && hp->ul_hn[1] == ems_handle[i]->hname.ul_hn[1]) return (i); } } return (0); } /* Malloc a new handle struct and put into array at index handle */ static void allocate_handle(short handle, long npages) { if (ems_handle[handle] != NULL) fatal("EMS allocate_handle, handle was not free\n"); ems_handle[handle] = get_new_handle(npages); ems_alloc_handles++; } /* Free a handle, return its memory. Call this *after* freeing the * allocated pages ! */ static void free_handle(short handle) { if (ems_handle[handle] == NULL) fatal("EMS free_handle, handle was free\n"); if (ems_handle[handle]->mcontext != NULL) free((void *)ems_handle[handle]->mcontext); free((void *)ems_handle[handle]); ems_handle[handle] = NULL; ems_alloc_handles--; } /* Allocates npages to handle. Call this routine only after you have * ensured there are enough free pages *and* the new handle is in place * in the handle array ! */ static void allocate_pages_to_handle(u_short handle, long npages) { unsigned syspagenum; int pages_to_alloc = npages; int allocpagenum = 0; /* sanity */ if (handle > 255 || ems_handle[handle] == NULL) fatal("EMS allocate_pages_to_handle called with invalid handle\n"); ems_handle[handle]->npages = npages; for (syspagenum = 0; syspagenum < ems_total_pages; syspagenum++) { if (ems_page[syspagenum].status == EMS_FREE) { ems_page[syspagenum].handle = handle; ems_page[syspagenum].status = EMS_ALLOCED; ems_handle[handle]->pagenum[allocpagenum] = syspagenum; allocpagenum++; pages_to_alloc--; if (pages_to_alloc == 0) break; } } if (pages_to_alloc > 0) fatal("EMS allocate_pages_to_handle found not enough free pages\n"); ems_alloc_pages += npages; ems_free_pages -= npages; } /* Reallocates npages to handle. Call this routine only after you have * ensured there are enough free pages *and* the new handle is in place * in the handle array ! */ static void reallocate_pages_to_handle(u_short handle, long npages) { unsigned allocpagenum; unsigned syspagenum; int pages_to_alloc; long delta; size_t dynsize; EMS_handle *emp; /* sanity */ if (handle > 255 || ems_handle[handle] == NULL) fatal("EMS allocate_pages_to_handle called with invalid handle\n"); delta = npages - ems_handle[handle]->npages; if (delta > 0) { /* Grow array size and allocation */ emp = ems_handle[handle]; dynsize = sizeof(EMS_handle) + sizeof(short) * npages; /* First step: Make room in the handle pagenum array */ if ((emp = (EMS_handle *)realloc((void *)emp, dynsize)) == NULL) fatal("Cannot malloc EMS handle, cannot continue\n"); ems_handle[handle] = emp; /* Second step: Add pages to the handle */ pages_to_alloc = delta; allocpagenum = ems_handle[handle]->npages; ems_handle[handle]->npages = npages; for (syspagenum = 0; syspagenum < ems_total_pages; syspagenum++) { if (ems_page[syspagenum].status == EMS_FREE) { ems_page[syspagenum].handle = handle; ems_page[syspagenum].status = EMS_ALLOCED; ems_handle[handle]->pagenum[allocpagenum] = syspagenum; allocpagenum++; pages_to_alloc--; if (pages_to_alloc == 0) break; } } if (pages_to_alloc > 0) fatal("EMS allocate_pages_to_handle found not enough free pages\n"); } else { /* Shrink array size and allocation */ /* First step: Deallocate all pages from new size to old size */ for (allocpagenum = npages; allocpagenum < ems_handle[handle]->npages; allocpagenum++) { syspagenum = ems_handle[handle]->pagenum[allocpagenum]; /* sanity */ if (syspagenum > ems_total_pages) fatal("EMS free_pages_of_handle found invalid page number\n"); if (!(ems_page[syspagenum].status & EMS_ALLOCED)) fatal("EMS free_pages_of_handle tried to free page already free\n"); ems_page[syspagenum].handle = 0; ems_page[syspagenum].status = EMS_FREE; } /* Second step: Shrink the dynamic array of the handle */ dynsize = sizeof(EMS_handle) + sizeof(short) * npages; emp = ems_handle[handle]; if ((emp = (EMS_handle *)realloc((void *)emp, dynsize)) == NULL) fatal("Cannot realloc EMS handle, cannot continue\n"); ems_handle[handle] = emp; ems_handle[handle]->npages = npages; } ems_alloc_pages += delta; ems_free_pages -= delta; } /* Free all pages belonging to a handle, handle must be valid */ static void free_pages_of_handle(short handle) { int allocpagenum; unsigned syspagenum; int npages; /* sanity */ if (handle > 255 || ems_handle[handle] == NULL) fatal("EMS free_pages_of_handle called with invalid handle\n"); if ((npages = ems_handle[handle]->npages) == 0) return; for (allocpagenum = 0; allocpagenum < npages; allocpagenum++) { syspagenum = ems_handle[handle]->pagenum[allocpagenum]; /* sanity */ if (syspagenum > ems_total_pages) fatal("EMS free_pages_of_handle found invalid page number\n"); if (!(ems_page[syspagenum].status & EMS_ALLOCED)) fatal("EMS free_pages_of_handle tried to free page already free\n"); ems_page[syspagenum].handle = 0; ems_page[syspagenum].status = EMS_FREE; } ems_alloc_pages -= npages; ems_free_pages += npages; } /* Restore a saved mapping context, overwrites current mapping context */ static void restore_context(EMS_mapping_context *emc) { int i; for (i = 0; i < 4; i++) { ems_mapping_context.handle[i] = emc->handle[i]; if (emc->pos_mapped[i] != 0 && ems_mapping_context.pos_pagenum[i] != emc->pos_pagenum[i]) { map_page(emc->pos_pagenum[i], (u_char) i, emc->handle[i], 0); } else { ems_mapping_context.pos_mapped[i] = 0; } } } /* Prepare a special context save block for DOS and save it to * VM86 memory */ static void save_context_to_dos(EMScontext *emp) { int i, end; EMScontext context; u_short *sp; u_short sum; context.ems_saved_context = ems_mapping_context; context.magic = EMS_SAVEMAGIC; context.checksum = 0; sp = (u_short *)&context; end = sizeof(EMScontext) / sizeof(short); /* Generate checksum */ for (i = 0, sum = 0; i < end; i++) { sum += *sp++; sum &= 0xffff; } context.checksum = 0x10000L - sum; /* Save it to VM86 memory */ *emp = context; } /* Check a context returned from VM86 app for validity, return 0, if * not valid, else return 1 */ static int check_saved_context(EMScontext *emp) { int i, end; u_short *sp; u_short sum; if (emp->magic != EMS_SAVEMAGIC) return 0; sp = (u_short *)emp; end = sizeof(EMScontext) / sizeof(short); /* Generate checksum */ for (i = 0, sum = 0; i < end; i++) { sum += *sp++; sum &= 0xffff; } if (sum != 0) return 0; else return 1; } /* Helper routine for the move routines below: Check if length bytes * can be moved from/to handle pages (i.e are there enough pages) */ static int check_alloc_pages(u_short handle, u_short firstpage, u_short offset, u_long length) { u_long nbytes; if (firstpage > ems_handle[handle]->npages) return (0); nbytes = (ems_handle[handle]->npages - firstpage) * EMS_PAGESIZE - offset; return (ems_handle[handle]->npages >= nbytes); } /* Copy a block of memory up to the next 16kB boundary in the source * to the destination in upward direction (i.e. with ascending addresses) * XXX Could be an inline function. */ static void copy_block_up(struct copydesc *cdp) { size_t size; void *srcp; void *dstp; /* If source or both memory types are EMS, source determines the * block lenght, else destination determines the block lenght */ if (cdp->copytype & SRC_EMS) size = EMS_PAGESIZE - cdp->EMS_OFFS(src_addr); else size = EMS_PAGESIZE - cdp->EMS_OFFS(dst_addr); if (size > cdp->rest_len) size = cdp->rest_len; /* If src is EMS memory, it is mapped into position 0 */ if (cdp->copytype & SRC_EMS) srcp = (void *)(ems_frame_addr + cdp->EMS_OFFS(src_addr)); else srcp = (void *)(cdp->EMS_PTR(src_addr)); /* If dest is EMS memory, it is mapped into position 1,2 */ if (cdp->copytype & DST_EMS) dstp = (void *)(ems_frame_addr + EMS_PAGESIZE + cdp->EMS_OFFS(dst_addr)); else dstp = (void *)(cdp->EMS_PTR(dst_addr)); /* Move this block */ memmove(dstp, srcp, size); /* Update the copy descriptor: This updates the address of both * conventional and EMS memory */ cdp->EMS_PTR(src_addr) += size; cdp->EMS_PTR(dst_addr) += size; cdp->rest_len -= size; } /* Move EMS memory starting with handle page src_seg and offset src_offset * to conventional memory dst_addr for length bytes * dst_addr is checked, handle is valid */ static u_long move_ems_to_conv(short src_handle, u_short src_seg, u_short src_offset, u_long dst_addr, u_long length) { EMS_mapping_context ems_saved_context; EMS_handle *ehp; int pageindx = src_seg; struct copydesc cd; if (check_alloc_pages(src_handle, src_seg, src_offset, length) == 0) return EMS_MOVE_OVERFLOW; ehp = ems_handle[src_handle]; /* Prepare the move: Save the mapping context */ ems_saved_context = ems_mapping_context; /* Setup the copy descriptor struct */ cd.copytype = SRC_EMS; cd.EMS_PAGE(src_addr) = ehp->pagenum[pageindx]; cd.EMS_OFFS(src_addr) = src_offset; cd.EMS_PTR(dst_addr) = dst_addr; cd.rest_len = length; do { /* Map for the first block copy, source is mapped to position zero */ map_page(cd.EMS_PAGE(src_addr), 0, src_handle, 0); copy_block_up(&cd); } while(cd.rest_len > 0); /* Restore the original mapping */ restore_context(&ems_saved_context); return EMS_SUCCESS; } /* Move conventional memory starting with src_addr * to EMS memory starting with handle page src_seg and offset src_offset * for length bytes * dst_addr is checked, handle is valid */ static u_long move_conv_to_ems(u_long src_addr, u_short dst_handle, u_short dst_seg, u_short dst_offset, u_long length) { EMS_mapping_context ems_saved_context; EMS_handle *ehp; int pageindx = dst_seg; struct copydesc cd; if (check_alloc_pages(dst_handle, dst_seg, dst_offset, length) == 0) return EMS_MOVE_OVERFLOW; ehp = ems_handle[dst_handle]; /* Prepare the move: Save the mapping context */ ems_saved_context = ems_mapping_context; /* Setup the copy descriptor struct */ cd.copytype = DST_EMS; cd.EMS_PAGE(dst_addr) = ehp->pagenum[pageindx]; cd.EMS_OFFS(dst_addr) = dst_offset; cd.EMS_PTR(src_addr) = src_addr; cd.rest_len = length; do { map_page(cd.EMS_PAGE(dst_addr), 1, dst_handle, 0); copy_block_up(&cd); } while(cd.rest_len > 0); /* Restore the original mapping */ restore_context(&ems_saved_context); return EMS_SUCCESS; } static u_long move_ems_to_ems(u_short src_handle, u_short src_seg, u_short src_offset, u_short dst_handle, u_short dst_seg, u_short dst_offset, u_long length) { EMS_mapping_context ems_saved_context; EMS_handle *src_hp, *dst_hp; struct copydesc cd; if (check_alloc_pages(src_handle, src_seg, src_offset, length) == 0) return EMS_MOVE_OVERFLOW; if (check_alloc_pages(dst_handle, dst_seg, dst_offset, length) == 0) return EMS_MOVE_OVERFLOW; src_hp = ems_handle[src_handle]; dst_hp = ems_handle[dst_handle]; /* Prepare the move: Save the mapping context */ ems_saved_context = ems_mapping_context; /* Setup the copy descriptor struct */ cd.copytype = SRC_EMS | DST_EMS; cd.EMS_PAGE(src_addr) = src_hp->pagenum[src_seg]; cd.EMS_OFFS(src_addr) = src_offset; cd.EMS_PAGE(dst_addr) = dst_hp->pagenum[dst_seg]; cd.EMS_OFFS(dst_addr) = dst_offset; cd.rest_len = length; /* Copy */ do { map_page(cd.EMS_PAGE(src_addr), 0, src_handle, 0); map_page(cd.EMS_PAGE(dst_addr), 1, dst_handle, 0); /* If there are more pages, map the next destination page to * position 2. This removes a compare between source and dest * offsets. */ if (cd.EMS_PAGE(dst_addr) < dst_hp->npages) map_page((cd.EMS_PAGE(dst_addr) + 1), 2, dst_handle, 0); copy_block_up(&cd); } while(cd.rest_len > 0); /* Restore the original mapping */ restore_context(&ems_saved_context); return EMS_SUCCESS; } Index: head/usr.bin/doscmd/tty.c =================================================================== --- head/usr.bin/doscmd/tty.c (revision 82324) +++ head/usr.bin/doscmd/tty.c (revision 82325) @@ -1,2218 +1,2218 @@ /* * Copyright (c) 1992, 1993, 1996 * Berkeley Software Design, Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Berkeley Software * Design, Inc. * * THIS SOFTWARE IS PROVIDED BY Berkeley Software Design, Inc. ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL Berkeley Software Design, Inc. BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * BSDI tty.c,v 2.4 1996/04/08 22:03:27 prb Exp * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef __FreeBSD__ # include # if __FreeBSD_version >= 500014 # include # else # include # endif #else # ifdef __NetBSD__ # include "machine/pccons.h" # else /* BSD/OS */ # include "/sys/i386/isa/pcconsioctl.h" # endif #endif #ifndef NO_X #include #include #include #endif #include "doscmd.h" #include "AsyncIO.h" #include "font8x8.h" #include "font8x14.h" #include "font8x16.h" #include "mouse.h" #include "trap.h" #include "tty.h" #include "video.h" #ifndef NO_X static int show = 1; #endif static int blink = 1; int flipdelete = 0; /* Flip meaning of delete and backspace */ static u_short break_code = 0x00; static u_short scan_code = 0x00; int height; int width; int vattr; const char *xfont = 0; #ifndef NO_X Display *dpy; Window win; XFontStruct *font; XImage *xi = 0; Visual *visual; unsigned int depth; unsigned long black; unsigned long white; int FW, FH, FD; GC gc; GC cgc; int xfd; /* LUT for the vram -> XImage conversion */ u_int8_t lut[4][256][8]; /* X pixel values for the RGB triples */ unsigned long pixels[16]; #endif typedef struct TextLine { u_short *data; u_char max_length; /* Not used, but here for future use */ u_char changed:1; } TextLine; TextLine *lines = NULL; int kbd_fd = -1; int kbd_read = 0; static struct termios tty_cook, tty_raw; #define row (CursRow0) #define col (CursCol0) /* Local functions */ static void _kbd_event(void *, regcontext_t *); static void Failure(void *); static void SetVREGCur(void); static void debug_event(void *, regcontext_t *); static unsigned char inb_port60(int); static int inrange(int, int, int); static void kbd_event(int); static u_short read_raw_kbd(int, u_short *); static void setgc(u_short); static void video_async_event(void *, regcontext_t *); #ifndef NO_X static void dac2rgb(XColor *, int); static void prepare_lut(void); static void putchar_graphics(int, int, int); static void tty_rwrite_graphics(int, int, int); static int video_event(XEvent *ev); static void video_update_graphics(void); static void video_update_text(void); static void vram2ximage(void); #endif #define PEEKSZ 16 #define K_NEXT *(u_short *)0x41a #define K_FREE *(u_short *)0x41c #define K_BUFSTARTP *(u_short *)0x480 #define K_BUFENDP *(u_short *)0x482 #define K_BUFSTART ((u_short *)(0x400 + *(u_short *)0x480)) #define K_BUFEND ((u_short *)(0x400 + *(u_short *)0x482)) #define K_BUF(i) *((u_short *)((u_char *)0x400 + (i))) #define K1_STATUS BIOSDATA[0x17] #define K1_RSHIFT 0x01 #define K1_LSHIFT 0x02 #define K1_SHIFT 0x03 #define K1_CTRL 0x04 #define K1_ALT 0x08 #define K1_SLOCK 0x10 /* Active */ #define K1_NLOCK 0x20 /* Active */ #define K1_CLOCK 0x40 /* Active */ #define K1_INSERT 0x80 /* Active */ #define K2_STATUS BIOSDATA[0x18] #define K2_LCTRL 0x01 #define K2_LALT 0x02 #define K2_SYSREQ 0x04 #define K2_PAUSE 0x08 #define K2_SLOCK 0x10 /* Actually held down */ #define K2_NLOCK 0x20 /* Actually held down */ #define K2_CLOCK 0x40 /* Actually held down */ #define K2_INSERT 0x80 /* Actually held down */ #define K3_STATUS BIOSDATA[0x96] #define K3_E1 0x01 /* Last code read was e1 */ #define K3_E2 0x02 /* Last code read was e2 */ #define K3_RCTRL 0x04 #define K3_RALT 0x08 #define K3_ENHANCED 0x10 #define K3_FORCENLOCK 0x20 #define K3_TWOBYTE 0x40 /* last code was first of 2 */ #define K3_READID 0x80 /* read ID in progress */ #define K4_STATUS BIOSDATA[0x97] #define K4_SLOCK_LED 0x01 #define K4_NLOCK_LED 0x02 #define K4_CLOCK_LED 0x04 #define K4_ACK 0x10 /* ACK recieved from keyboard */ #define K4_RESEND 0x20 /* RESEND recieved from keyboard */ #define K4_LED 0x40 /* LED update in progress */ #define K4_ERROR 0x80 static void Failure(void *arg) { fprintf(stderr, "X Connection shutdown\n"); quit(1); } static void SetVREGCur() { int cp = row * width + col; VGA_CRTC[CRTC_CurLocHi] = cp >> 8; VGA_CRTC[CRTC_CurLocLo] = cp & 0xff; } static void console_denit(void *arg) { int fd = *(int *)arg; #ifdef __FreeBSD__ if (ioctl(fd, KDSKBMODE, K_XLATE)) perror("KDSKBMODE/K_XLATE"); #else # ifdef __NetBSD__ if (ioctl(fd, CONSOLE_X_MODE_OFF, 0)) perror("CONSOLE_X_MODE_OFF"); # else /* BSD/OS */ if (ioctl(fd, PCCONIOCCOOK, 0)) perror("PCCONIOCCOOK"); # endif #endif if (tcsetattr(fd, TCSANOW, &tty_cook)) perror("tcsetattr"); } void _kbd_event(void *pfd, regcontext_t *REGS) { int fd = *(int *)pfd; printf("_kbd_event: fd=%d\n", fd); kbd_read = 1; } void console_init() { int fd; caddr_t addr; if ((fd = open("/dev/vga", 2)) < 0) { perror("/dev/vga"); quit(1); } addr = mmap((caddr_t)0xA0000, 5 * 64 * 1024, PROT_EXEC | PROT_READ | PROT_WRITE, - MAP_FILE | MAP_FIXED | MAP_INHERIT | MAP_SHARED, + MAP_FILE | MAP_FIXED | MAP_SHARED, fd, 0); if (addr != (caddr_t)0xA0000) { perror("mmap"); quit(1); } #if 0 addr = mmap((caddr_t)0x100000 - 0x1000, 0x1000, PROT_EXEC | PROT_READ | PROT_WRITE, - MAP_FILE | MAP_FIXED | MAP_INHERIT | MAP_SHARED, + MAP_FILE | MAP_FIXED | MAP_SHARED, fd, 0); if (addr != (caddr_t)(0x100000 - 0x1000)) { perror("mmap"); quit(1); } #endif if ((fd = open(_PATH_CONSOLE, 2)) < 0) { perror(_PATH_CONSOLE); quit(1); } fd = squirrel_fd(fd); kbd_fd = fd; #ifdef __FreeBSD__ if (ioctl(fd, KDSKBMODE, K_RAW)) { perror("KDSKBMODE/K_RAW"); quit(1); } #else # ifdef __NetBSD__ if (ioctl(fd, CONSOLE_X_MODE_ON, 0)) { perror("CONSOLE_X_MODE_ON"); quit(1); } # else /* BSD/OS */ if (ioctl(fd, PCCONIOCRAW, 0)) { perror("PCCONIOCRAW"); quit(1); } # endif #endif call_on_quit(console_denit, &kbd_fd); if (fcntl(fd, F_SETFL, O_NDELAY|O_ASYNC) < 0) { perror("fcntl"); quit(1); } if (tcgetattr(fd, &tty_cook)) { perror("tcgetattr"); quit(1); } tty_raw = tty_cook; cfmakeraw(&tty_raw); if (tcsetattr(fd, TCSANOW, &tty_raw)) { perror("tcsetattr"); quit(1); } #if 0 _RegisterIO(0, debug_event, 0, Failure); _RegisterIO(fd, kbd_event, fd, Failure); #endif _RegisterIO(fd, _kbd_event, &fd, Failure); } void video_setborder(int color) { #ifndef NO_X _BlockIO(); XSetWindowBackground(dpy, win, pixels[color & 0xf]); _UnblockIO(); #endif } void video_blink(int mode) { blink = mode; } static void setgc(u_short attr) { #ifndef NO_X XGCValues v; if (blink && !show && (attr & 0x8000)) v.foreground = pixels[(attr >> 12) & 0x07]; else v.foreground = pixels[(attr >> 8) & 0x0f]; v.background = pixels[(attr >> 12) & (blink ? 0x07 : 0x0f)]; XChangeGC(dpy, gc, GCForeground|GCBackground, &v); #endif } void video_update(regcontext_t *REGS) { #ifndef NO_X static int icnt = 3; if (kbd_read) kbd_event(kbd_fd); if (--icnt == 0) { icnt = 3; lpt_poll(); /* Handle timeout on lpt code */ /* quick and dirty */ if (VGA_ATC[ATC_ModeCtrl] & 1) video_update_graphics(); else video_update_text(); } #endif } #ifndef NO_X static void video_update_graphics() { vram2ximage(); XPutImage(dpy, win, DefaultGC(dpy, DefaultScreen(dpy)), xi, 0, 0, 0, 0, width, height); XFlush(dpy); return; } static void video_update_text() { static int or = -1; static int oc = -1; static char buf[256]; int r, c; int attr = vmem[0] & 0xff00; XGCValues v; if (xmode) { wakeup_poll(); /* Wake up anyone waiting on kbd poll */ show ^= 1; setgc(attr); for (r = 0; r < height; ++r) { int cc = 0; if (!lines[r].changed) { if ((r == or || r == row) && (or != row || oc != col)) lines[r].changed = 1; else { for (c = 0; c < width; ++c) { if (lines[r].data[c] != vmem[r * width + c]) { lines[r].changed = 1; break; } if (blink && lines[r].data[c] & 0x8000) { lines[r].changed = 1; break; } } } } if (!lines[r].changed) continue; reset_poll(); lines[r].changed = 0; memcpy(lines[r].data, &vmem[r * width], sizeof(u_short) * width); for (c = 0; c < width; ++c) { int cv = vmem[r * width + c]; if ((cv & 0xff00) != attr) { if (cc < c) XDrawImageString(dpy, win, gc, 2 + cc * FW, 2 + (r + 1) * FH, buf + cc, c - cc); cc = c; attr = cv & 0xff00; setgc(attr); } buf[c] = (cv & 0xff) ? cv & 0xff : ' '; } if (cc < c) { XDrawImageString(dpy, win, gc, 2 + cc * FW, 2 + (r + 1) * FH, buf + cc, c - cc); } } or = row; oc = col; if (CursStart <= CursEnd && CursEnd <= FH && show && row < height && col < width) { attr = vmem[row * width + col] & 0xff00; v.foreground = pixels[(attr >> 8) & 0x0f] ^ pixels[(attr >> 12) & (blink ? 0x07 : 0x0f)]; if (v.foreground) { v.function = GXxor; } else { v.foreground = pixels[7]; v.function = GXcopy; } XChangeGC(dpy, cgc, GCForeground | GCFunction, &v); XFillRectangle(dpy, win, cgc, 2 + col * FW, 2 + row * FH + CursStart + FD, FW, CursEnd + 1 - CursStart); } if (mouse_status.installed && mouse_status.show) { c = mouse_status.x / mouse_status.hmickey; r = mouse_status.y / mouse_status.vmickey; lines[r].changed = 1; attr = vmem[r * width + c] & 0xff00; v.foreground = pixels[(attr >> 8) & 0x0f] ^ pixels[(attr >> 12) & 0x0f]; if (v.foreground) { v.function = GXxor; } else { v.foreground = pixels[7]; v.function = GXcopy; } XChangeGC(dpy, cgc, GCForeground | GCFunction, &v); XFillRectangle(dpy, win, cgc, 2 + c * FW, 2 + r * FH + 2, FW, FH); } XFlush(dpy); } } /* Convert the contents of the video RAM into an XImage. Bugs: - The function is way too slow. - It only works for the 16 color modes. - It only works on 15/16-bit TrueColor visuals. */ static void vram2ximage() { int i, x, y, yoffset; u_int16_t *image = (u_int16_t *)xi->data; yoffset = 0; for (y = 0; y < height; y++) { yoffset += width / 8; for (x = 0; x < width; x += 8) { int offset = yoffset + x / 8; for (i = 0; i < 8; i++) { int color = lut[0][vplane0[offset]][i] | lut[1][vplane1[offset]][i] | lut[2][vplane2[offset]][i] | lut[3][vplane3[offset]][i]; *image++ = (u_int16_t)pixels[color]; } } } return; } #endif static u_short Ascii2Scan[] = { 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0x000e, 0x000f, 0xffff, 0xffff, 0xffff, 0x001c, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0x0001, 0xffff, 0xffff, 0xffff, 0xffff, 0x0039, 0x0102, 0x0128, 0x0104, 0x0105, 0x0106, 0x0108, 0x0028, 0x010a, 0x010b, 0x0109, 0x010d, 0x0033, 0x000c, 0x0034, 0x0035, 0x000b, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x0127, 0x0027, 0x0133, 0x000d, 0x0134, 0x0135, 0x0103, 0x011e, 0x0130, 0x012e, 0x0120, 0x0112, 0x0121, 0x0122, 0x0123, 0x0117, 0x0124, 0x0125, 0x0126, 0x0132, 0x0131, 0x0118, 0x0119, 0x0110, 0x0113, 0x011f, 0x0114, 0x0116, 0x012f, 0x0111, 0x012d, 0x0115, 0x012c, 0x001a, 0x002b, 0x001b, 0x0107, 0x010c, 0x0029, 0x001e, 0x0030, 0x002e, 0x0020, 0x0012, 0x0021, 0x0022, 0x0023, 0x0017, 0x0024, 0x0025, 0x0026, 0x0032, 0x0031, 0x0018, 0x0019, 0x0010, 0x0013, 0x001f, 0x0014, 0x0016, 0x002f, 0x0011, 0x002d, 0x0015, 0x002c, 0x011a, 0x012b, 0x011b, 0x0129, 0xffff, }; struct { u_short base; u_short shift; u_short ctrl; u_short alt; } ScanCodes[] = { { 0xffff, 0xffff, 0xffff, 0xffff }, /* key 0 */ { 0x011b, 0x011b, 0x011b, 0xffff }, /* key 1 - Escape key */ { 0x0231, 0x0221, 0xffff, 0x7800 }, /* key 2 - '1' */ { 0x0332, 0x0340, 0x0300, 0x7900 }, /* key 3 - '2' - special handling */ { 0x0433, 0x0423, 0xffff, 0x7a00 }, /* key 4 - '3' */ { 0x0534, 0x0524, 0xffff, 0x7b00 }, /* key 5 - '4' */ { 0x0635, 0x0625, 0xffff, 0x7c00 }, /* key 6 - '5' */ { 0x0736, 0x075e, 0x071e, 0x7d00 }, /* key 7 - '6' */ { 0x0837, 0x0826, 0xffff, 0x7e00 }, /* key 8 - '7' */ { 0x0938, 0x092a, 0xffff, 0x7f00 }, /* key 9 - '8' */ { 0x0a39, 0x0a28, 0xffff, 0x8000 }, /* key 10 - '9' */ { 0x0b30, 0x0b29, 0xffff, 0x8100 }, /* key 11 - '0' */ { 0x0c2d, 0x0c5f, 0x0c1f, 0x8200 }, /* key 12 - '-' */ { 0x0d3d, 0x0d2b, 0xffff, 0x8300 }, /* key 13 - '=' */ { 0x0e08, 0x0e08, 0x0e7f, 0xffff }, /* key 14 - backspace */ { 0x0f09, 0x0f00, 0xffff, 0xffff }, /* key 15 - tab */ { 0x1071, 0x1051, 0x1011, 0x1000 }, /* key 16 - 'Q' */ { 0x1177, 0x1157, 0x1117, 0x1100 }, /* key 17 - 'W' */ { 0x1265, 0x1245, 0x1205, 0x1200 }, /* key 18 - 'E' */ { 0x1372, 0x1352, 0x1312, 0x1300 }, /* key 19 - 'R' */ { 0x1474, 0x1454, 0x1414, 0x1400 }, /* key 20 - 'T' */ { 0x1579, 0x1559, 0x1519, 0x1500 }, /* key 21 - 'Y' */ { 0x1675, 0x1655, 0x1615, 0x1600 }, /* key 22 - 'U' */ { 0x1769, 0x1749, 0x1709, 0x1700 }, /* key 23 - 'I' */ { 0x186f, 0x184f, 0x180f, 0x1800 }, /* key 24 - 'O' */ { 0x1970, 0x1950, 0x1910, 0x1900 }, /* key 25 - 'P' */ { 0x1a5b, 0x1a7b, 0x1a1b, 0xffff }, /* key 26 - '[' */ { 0x1b5d, 0x1b7d, 0x1b1d, 0xffff }, /* key 27 - ']' */ { 0x1c0d, 0x1c0d, 0x1c0a, 0xffff }, /* key 28 - CR */ { 0xffff, 0xffff, 0xffff, 0xffff }, /* key 29 - control */ { 0x1e61, 0x1e41, 0x1e01, 0x1e00 }, /* key 30 - 'A' */ { 0x1f73, 0x1f53, 0x1f13, 0x1f00 }, /* key 31 - 'S' */ { 0x2064, 0x2044, 0x2004, 0x2000 }, /* key 32 - 'D' */ { 0x2166, 0x2146, 0x2106, 0x2100 }, /* key 33 - 'F' */ { 0x2267, 0x2247, 0x2207, 0x2200 }, /* key 34 - 'G' */ { 0x2368, 0x2348, 0x2308, 0x2300 }, /* key 35 - 'H' */ { 0x246a, 0x244a, 0x240a, 0x2400 }, /* key 36 - 'J' */ { 0x256b, 0x254b, 0x250b, 0x2500 }, /* key 37 - 'K' */ { 0x266c, 0x264c, 0x260c, 0x2600 }, /* key 38 - 'L' */ { 0x273b, 0x273a, 0xffff, 0xffff }, /* key 39 - ';' */ { 0x2827, 0x2822, 0xffff, 0xffff }, /* key 40 - ''' */ { 0x2960, 0x297e, 0xffff, 0xffff }, /* key 41 - '`' */ { 0xffff, 0xffff, 0xffff, 0xffff }, /* key 42 - left shift */ { 0x2b5c, 0x2b7c, 0x2b1c, 0xffff }, /* key 43 - '' */ { 0x2c7a, 0x2c5a, 0x2c1a, 0x2c00 }, /* key 44 - 'Z' */ { 0x2d78, 0x2d58, 0x2d18, 0x2d00 }, /* key 45 - 'X' */ { 0x2e63, 0x2e43, 0x2e03, 0x2e00 }, /* key 46 - 'C' */ { 0x2f76, 0x2f56, 0x2f16, 0x2f00 }, /* key 47 - 'V' */ { 0x3062, 0x3042, 0x3002, 0x3000 }, /* key 48 - 'B' */ { 0x316e, 0x314e, 0x310e, 0x3100 }, /* key 49 - 'N' */ { 0x326d, 0x324d, 0x320d, 0x3200 }, /* key 50 - 'M' */ { 0x332c, 0x333c, 0xffff, 0xffff }, /* key 51 - ',' */ { 0x342e, 0x343e, 0xffff, 0xffff }, /* key 52 - '.' */ { 0x352f, 0x353f, 0xffff, 0xffff }, /* key 53 - '/' */ { 0xffff, 0xffff, 0xffff, 0xffff }, /* key 54 - right shift - */ { 0x372a, 0xffff, 0x3772, 0xffff }, /* key 55 - prt-scr - */ { 0xffff, 0xffff, 0xffff, 0xffff }, /* key 56 - Alt - */ { 0x3920, 0x3920, 0x3920, 0x3920 }, /* key 57 - space bar */ { 0xffff, 0xffff, 0xffff, 0xffff }, /* key 58 - caps-lock - */ { 0x3b00, 0x5400, 0x5e00, 0x6800 }, /* key 59 - F1 */ { 0x3c00, 0x5500, 0x5f00, 0x6900 }, /* key 60 - F2 */ { 0x3d00, 0x5600, 0x6000, 0x6a00 }, /* key 61 - F3 */ { 0x3e00, 0x5700, 0x6100, 0x6b00 }, /* key 62 - F4 */ { 0x3f00, 0x5800, 0x6200, 0x6c00 }, /* key 63 - F5 */ { 0x4000, 0x5900, 0x6300, 0x6d00 }, /* key 64 - F6 */ { 0x4100, 0x5a00, 0x6400, 0x6e00 }, /* key 65 - F7 */ { 0x4200, 0x5b00, 0x6500, 0x6f00 }, /* key 66 - F8 */ { 0x4300, 0x5c00, 0x6600, 0x7000 }, /* key 67 - F9 */ { 0x4400, 0x5d00, 0x6700, 0x7100 }, /* key 68 - F10 */ { 0xffff, 0xffff, 0xffff, 0xffff }, /* key 69 - num-lock - */ { 0xffff, 0xffff, 0xffff, 0xffff }, /* key 70 - scroll-lock - */ { 0x4700, 0x4737, 0x7700, 0xffff }, /* key 71 - home */ { 0x4800, 0x4838, 0xffff, 0xffff }, /* key 72 - cursor up */ { 0x4900, 0x4939, 0x8400, 0xffff }, /* key 73 - page up */ { 0x4a2d, 0x4a2d, 0xffff, 0xffff }, /* key 74 - minus sign */ { 0x4b00, 0x4b34, 0x7300, 0xffff }, /* key 75 - cursor left */ { 0xffff, 0x4c35, 0xffff, 0xffff }, /* key 76 - center key */ { 0x4d00, 0x4d36, 0x7400, 0xffff }, /* key 77 - cursor right */ { 0x4e2b, 0x4e2b, 0xffff, 0xffff }, /* key 78 - plus sign */ { 0x4f00, 0x4f31, 0x7500, 0xffff }, /* key 79 - end */ { 0x5000, 0x5032, 0xffff, 0xffff }, /* key 80 - cursor down */ { 0x5100, 0x5133, 0x7600, 0xffff }, /* key 81 - page down */ { 0x5200, 0x5230, 0xffff, 0xffff }, /* key 82 - insert */ { 0x5300, 0x532e, 0xffff, 0xffff }, /* key 83 - delete */ { 0xffff, 0xffff, 0xffff, 0xffff }, /* key 84 - sys key */ { 0xffff, 0xffff, 0xffff, 0xffff }, /* key 85 */ { 0xffff, 0xffff, 0xffff, 0xffff }, /* key 86 */ { 0x8500, 0x5787, 0x8900, 0x8b00 }, /* key 87 - F11 */ { 0x8600, 0x5888, 0x8a00, 0x8c00 }, /* key 88 - F12 */ }; void debug_event(void *pfd, regcontext_t *REGS) { static char ibuf[1024]; static int icnt = 0; static u_short ds = 0; static u_short di = 0; static u_short cnt = 16 * 8; char *ep; int r; r = read(STDIN_FILENO, ibuf + icnt, sizeof(ibuf) - icnt); if (r <= 0) return; icnt += r; ibuf[icnt] = 0; while ((ep = strchr(ibuf, '\n')) != 0) { int ac; char *_av[16]; char **av; *ep++ = 0; ac = ParseBuffer(ibuf, av = _av, 16); if (ac > 0) { if (!strcasecmp(av[0], "dump")) { if (ac > 1) { char *c; if ((c = strchr(av[1], ':')) != 0) { ds = strtol(av[1], 0, 16); di = strtol(c+1, 0, 16); } else di = strtol(av[1], 0, 16); } if (ac > 2) cnt = strtol(av[2], 0, 0); cnt = (cnt + 0xf) & ~0xf; if (cnt == 0) cnt = 0x10; di &= ~0xf; for (r = 0; r < cnt; r += 0x10, di = (di + 0x10) & 0xffff) { int i; u_char *ap = (u_char *)(((u_long)ds << 4) + di); printf("%04x:%04x:", ds, di); for (i = 0; i < 8; ++i) printf(" %02x", ap[i]); printf(" "); for (i = 8; i < 16; ++i) printf(" %02x", ap[i]); printf(": "); for (i = 0; i < 8; ++i) printf("%c",(ap[i] < ' ' || ap[i] > '~') ? '.' : ap[i]); printf(" "); for (i = 8; i < 16; ++i) printf("%c",(ap[i] < ' ' || ap[i] > '~') ? '.' : ap[i]); printf("\n"); } } else if (!strcasecmp(av[0], "dis")) { u_char *ap = (u_char *)(((u_long)ds << 4) + di); if (ac > 1) { char *c; if ((c = strchr(av[1], ':')) != 0) { ds = strtol(av[1], 0, 16); di = strtol(c+1, 0, 16); } else di = strtol(av[1], 0, 16); } if (ac > 2) cnt = strtol(av[2], 0, 0); for (r = 0; r < cnt; ++r) { char buf[16]; int c = i386dis(ds, di, ap, buf, 0); printf("%04x:%04x %s\n", ds, di, buf); di += c; ap += c; } } else if (!strcasecmp(av[0], "regs")) { dump_regs(REGS); } else if (!strcasecmp(av[0], "force")) { char *p = av[1]; while ((p = *++av) != 0) { while (*p) { if (*p >= ' ' && *p <= '~') KbdWrite(ScanCodes[Ascii2Scan[(int)*p] & 0xff].base); ++p; } } KbdWrite(ScanCodes[28].base); } else if (!strcasecmp(av[0], "bell")) { #ifndef NO_X XBell(dpy, 0); XFlush(dpy); #endif } else { fprintf(stderr, "%s: unknown command\n", av[0]); } } if (ep < ibuf + icnt) { char *f = ep; char *t = ibuf; icnt -= ep - ibuf; while (icnt--) *t++ = *f++; } else icnt = 0; ibuf[icnt] = 0; } } unsigned char inb_port60(int port) { int r = break_code; break_code = 0; scan_code = 0xffff; return(r); } void kbd_event(int fd) { kbd_read = 0; printf("kbd_event: fd=%d\n", fd); if ((break_code = read_raw_kbd(fd, &scan_code)) != 0xffff) hardint(0x09); } void int09(REGISTERS) { if (raw_kbd) { if (scan_code != 0xffff) { KbdWrite(scan_code); break_code = 0; scan_code = 0xffff; #if 0 kbd_event(kbd_fd, sc); #endif } } } u_short read_raw_kbd(int fd, u_short *code) { unsigned char c; *code = 0xffff; if (read(fd, &c, 1) == 1) { if (c == 0xe0) { K3_STATUS |= K3_TWOBYTE; return(c); } switch (c) { case 29: /* Control */ K1_STATUS |= K1_CTRL; if (K3_STATUS & K3_TWOBYTE) K3_STATUS |= K3_RCTRL; else K2_STATUS |= K2_LCTRL; break; case 29 | 0x80: /* Control */ K1_STATUS &= ~K1_CTRL; if (K3_STATUS & K3_TWOBYTE) K3_STATUS &= ~K3_RCTRL; else K2_STATUS &= ~K2_LCTRL; break; case 42: /* left shift */ K1_STATUS |= K1_LSHIFT; break; case 42 | 0x80: /* left shift */ K1_STATUS &= ~K1_LSHIFT; break; case 54: /* right shift */ K1_STATUS |= K1_RSHIFT; break; case 54 | 0x80: /* right shift */ K1_STATUS &= ~K1_RSHIFT; break; case 56: /* Alt */ K1_STATUS |= K1_ALT; if (K3_STATUS & K3_TWOBYTE) K3_STATUS |= K3_RALT; else K2_STATUS |= K2_LALT; break; case 56 | 0x80: /* Alt */ K1_STATUS &= ~K1_ALT; if (K3_STATUS & K3_TWOBYTE) K3_STATUS &= ~K3_RALT; else K2_STATUS &= ~K2_LALT; break; case 58: /* caps-lock */ K1_STATUS ^= K1_CLOCK; if (K1_STATUS & K1_CLOCK) K4_STATUS |= K4_CLOCK_LED; else K4_STATUS &= ~K4_CLOCK_LED; K2_STATUS |= K2_CLOCK; break; case 58 | 0x80: /* caps-lock */ K2_STATUS &= ~K2_CLOCK; break; case 69: /* num-lock */ K1_STATUS ^= K1_NLOCK; if (K1_STATUS & K1_NLOCK) K4_STATUS |= K4_NLOCK_LED; else K4_STATUS &= ~K4_NLOCK_LED; K2_STATUS |= K2_NLOCK; break; case 69 | 0x80: /* num-lock */ K2_STATUS &= ~K2_NLOCK; break; case 70: /* scroll-lock */ K1_STATUS ^= K1_SLOCK; if (K1_STATUS & K1_SLOCK) K4_STATUS |= K4_SLOCK_LED; else K4_STATUS &= ~K4_SLOCK_LED; K2_STATUS |= K2_SLOCK; break; case 70 | 0x80: /* scroll-lock */ K2_STATUS &= ~K2_SLOCK; break; case 82: /* insert */ K1_STATUS ^= K1_INSERT; K2_STATUS |= K2_INSERT; break; case 82 | 0x80: /* insert */ K2_STATUS &= ~K2_INSERT; break; } #if 0 /*XXXXX*/ if ((K4_STATUS & 0x07) != oldled) { oldled = K4_STATUS & 0x07; ioctl (fd, PCCONIOCSETLED, &oldled); } #endif if (c == 83 && (K1_STATUS & (K1_ALT|K1_CTRL)) == (K1_ALT|K1_CTRL)) quit(0); if (c < 89) { u_short scode; if (K1_STATUS & K1_ALT) { scode = ScanCodes[c].alt; } else if (K1_STATUS & K1_CTRL) { scode = ScanCodes[c].ctrl; } else if (K1_STATUS & K1_SHIFT) { scode = ScanCodes[c].shift; } else { scode = ScanCodes[c].base; if (K1_STATUS & K1_CLOCK) { if (islower(scode & 0xff)) { scode = (scode & 0xff00) | toupper(scode & 0xff); } } if ((K1_STATUS & K1_NLOCK) && (K3_STATUS & K3_TWOBYTE) == 0) { switch (c) { case 71: /* home */ case 72: /* cursor up */ case 73: /* page up */ case 75: /* cursor left */ case 76: /* center key */ case 77: /* cursor right */ case 79: /* end */ case 80: /* cursor down */ case 81: /* page down */ case 82: /* insert */ case 83: /* delete */ scode = ScanCodes[c].shift; break; } } } *code = scode; } K3_STATUS &= ~K3_TWOBYTE; if ((K1_STATUS&(K1_ALT|K1_CTRL)) == (K1_ALT|K1_CTRL)) { switch (c) { case 0x13: /* R */ kill(getpid(), SIGALRM); /* force redraw */ printf("FORCED REDRAW\n"); return(0xffff); case 0x14: /* T */ tmode ^= 1; if (!tmode) resettrace((regcontext_t *)&saved_sigframe-> sf_uc.uc_mcontext); return(0xffff); case 0x53: /* DEL */ quit(0); } } return(c); } else { return(0xffff); } } void video_async_event(void *pfd, regcontext_t *REGS) { #ifndef NO_X int int9 = 0; int fd = *(int *)pfd; for (;;) { int x; fd_set fdset; XEvent ev; static struct timeval tv; /* * Handle any events just sitting around... */ XFlush(dpy); while (QLength(dpy) > 0) { XNextEvent(dpy, &ev); int9 |= video_event(&ev); } FD_ZERO(&fdset); FD_SET(fd, &fdset); x = select(FD_SETSIZE, &fdset, 0, 0, &tv); switch (x) { case -1: /* * Errno might be wrong, so we just select again. * This could cause a problem is something really * was wrong with select.... */ perror("select"); return; case 0: XFlush(dpy); if (int9) hardint(0x09); return; default: if (FD_ISSET(fd, &fdset)) { do { XNextEvent(dpy, &ev); int9 |= video_event(&ev); } while (QLength(dpy)); } break; } } #endif } #ifndef NO_X static int video_event(XEvent *ev) { switch (ev->type) { case MotionNotify: { XMotionEvent *me = (XMotionEvent *)ev; me->x -= 2; me->y -= 2; mouse_status.x = (me->x < mouse_status.range.x) ? mouse_status.range.x : (me->x > mouse_status.range.w) ? mouse_status.range.w : me->x; mouse_status.y = (me->y < mouse_status.range.y) ? mouse_status.range.y : (me->y > mouse_status.range.h) ? mouse_status.range.h : me->y; break; } case ButtonRelease: { XButtonEvent *be = (XButtonEvent *)ev; be->x -= 2; be->y -= 2; if (be->button < 3) mouse_status.ups[be->button]++; mouse_status.x = (be->x < mouse_status.range.x) ? mouse_status.range.x : (be->x > mouse_status.range.w) ? mouse_status.range.w : be->x; mouse_status.y = (be->y < mouse_status.range.y) ? mouse_status.range.y : (be->y > mouse_status.range.h) ? mouse_status.range.h : be->y; break; } case ButtonPress: { XButtonEvent *be = (XButtonEvent *)ev; be->x -= 2; be->y -= 2; if (be->button < 3) mouse_status.downs[be->button]++; mouse_status.x = (be->x < mouse_status.range.x) ? mouse_status.range.x : (be->x > mouse_status.range.w) ? mouse_status.range.w : be->x; mouse_status.y = (be->y < mouse_status.range.y) ? mouse_status.range.y : (be->y > mouse_status.range.h) ? mouse_status.range.h : be->y; if ((K1_STATUS & (K1_ALT|K1_CTRL)) == (K1_ALT|K1_CTRL)) { quit(0); } break; } case NoExpose: break; case GraphicsExpose: case Expose: { int r; for (r = 0; r < height; ++r) lines[r].changed = 1; break; } case KeyRelease: { static char buf[128]; KeySym ks; break_code |= 0x80; if (!(ev->xkey.state & ShiftMask)) { K1_STATUS &= ~K1_LSHIFT; K1_STATUS &= ~K1_RSHIFT; } if (!(ev->xkey.state & ControlMask)) { K1_STATUS &= ~K1_CTRL; K2_STATUS &= ~K2_LCTRL; K3_STATUS &= ~K3_RCTRL; } if (!(ev->xkey.state & Mod1Mask)) { K1_STATUS &= ~K1_ALT; K2_STATUS &= ~K2_LALT; K3_STATUS &= ~K3_RALT; } if (!(ev->xkey.state & LockMask)) { K2_STATUS &= ~K2_CLOCK; } XLookupString((XKeyEvent *)ev, buf, sizeof(buf), &ks, 0); switch (ks) { case XK_Shift_L: K1_STATUS &= ~K1_LSHIFT; break; case XK_Shift_R: K1_STATUS &= ~K1_RSHIFT; break; case XK_Control_L: K1_STATUS &= ~K1_CTRL; K2_STATUS &= ~K2_LCTRL; break; case XK_Control_R: K1_STATUS &= ~K1_CTRL; K3_STATUS &= ~K3_RCTRL; break; case XK_Alt_L: K1_STATUS &= ~K1_ALT; K2_STATUS &= ~K2_LALT; break; case XK_Alt_R: K1_STATUS &= ~K1_ALT; K3_STATUS &= ~K3_RALT; break; case XK_Scroll_Lock: K2_STATUS &= ~K2_SLOCK; break; case XK_Num_Lock: K2_STATUS &= ~K2_NLOCK; break; case XK_Caps_Lock: K2_STATUS &= ~K2_CLOCK; break; case XK_Insert: K2_STATUS &= ~K2_INSERT; break; } return(1); } case KeyPress: { static char buf[128]; KeySym ks; int n; int nlock = 0; u_short scan = 0xffff; if (!(ev->xkey.state & ShiftMask)) { K1_STATUS &= ~K1_LSHIFT; K1_STATUS &= ~K1_RSHIFT; } if (!(ev->xkey.state & ControlMask)) { K1_STATUS &= ~K1_CTRL; K2_STATUS &= ~K2_LCTRL; K3_STATUS &= ~K3_RCTRL; } if (!(ev->xkey.state & Mod1Mask)) { K1_STATUS &= ~K1_ALT; K2_STATUS &= ~K2_LALT; K3_STATUS &= ~K3_RALT; } if (!(ev->xkey.state & LockMask)) { K2_STATUS &= ~K2_CLOCK; } n = XLookupString((XKeyEvent *)ev, buf, sizeof(buf), &ks, 0); switch (ks) { case XK_Shift_L: K1_STATUS |= K1_LSHIFT; break; case XK_Shift_R: K1_STATUS |= K1_RSHIFT; break; case XK_Control_L: K1_STATUS |= K1_CTRL; K2_STATUS |= K2_LCTRL; break; case XK_Control_R: K1_STATUS |= K1_CTRL; K3_STATUS |= K3_RCTRL; break; case XK_Alt_L: K1_STATUS |= K1_ALT; K2_STATUS |= K2_LALT; break; case XK_Alt_R: K1_STATUS |= K1_ALT; K3_STATUS |= K3_RALT; break; case XK_Scroll_Lock: K1_STATUS ^= K1_SLOCK; K2_STATUS |= K2_SLOCK; break; case XK_Num_Lock: K1_STATUS ^= K1_NLOCK; K2_STATUS |= K2_NLOCK; break; case XK_Caps_Lock: K1_STATUS ^= K1_CLOCK; K2_STATUS |= K2_CLOCK; break; case XK_Insert: case XK_KP_Insert: K1_STATUS ^= K1_INSERT; K2_STATUS |= K2_INSERT; scan = 82; goto docode; case XK_Escape: scan = 1; goto docode; case XK_Tab: case XK_ISO_Left_Tab: scan = 15; goto docode; case XK_Return: case XK_KP_Enter: scan = 28; goto docode; case XK_Print: scan = 55; goto docode; case XK_F1: case XK_F2: case XK_F3: case XK_F4: case XK_F5: case XK_F6: case XK_F7: case XK_F8: case XK_F9: case XK_F10: scan = ks - XK_F1 + 59; goto docode; case XK_KP_7: nlock = 1; case XK_Home: case XK_KP_Home: scan = 71; goto docode; case XK_KP_8: nlock = 1; case XK_Up: case XK_KP_Up: scan = 72; goto docode; case XK_KP_9: nlock = 1; case XK_Prior: case XK_KP_Prior: scan = 73; goto docode; case XK_KP_Subtract: scan = 74; goto docode; case XK_KP_4: nlock = 1; case XK_Left: case XK_KP_Left: scan = 75; goto docode; case XK_KP_5: nlock = 1; case XK_Begin: case XK_KP_Begin: scan = 76; goto docode; case XK_KP_6: nlock = 1; case XK_Right: case XK_KP_Right: scan = 77; goto docode; case XK_KP_Add: scan = 78; goto docode; case XK_KP_1: nlock = 1; case XK_End: case XK_KP_End: scan = 79; goto docode; case XK_KP_2: nlock = 1; case XK_Down: case XK_KP_Down: scan = 80; goto docode; case XK_KP_3: nlock = 1; case XK_Next: case XK_KP_Next: scan = 81; goto docode; case XK_KP_0: nlock = 1; /* case XK_Insert: This is above */ scan = 82; goto docode; case XK_KP_Decimal: nlock = 1; scan = 83; goto docode; case XK_Delete: case XK_KP_Delete: scan = flipdelete ? 14 : 83; goto docode; case XK_BackSpace: scan = flipdelete ? 83 : 14; goto docode; case XK_F11: scan = 87; goto docode; case XK_F12: scan = 88; goto docode; case XK_KP_Divide: scan = Ascii2Scan['/']; goto docode; case XK_KP_Multiply: scan = Ascii2Scan['*']; goto docode; default: if ((K1_STATUS&(K1_ALT|K1_CTRL)) == (K1_ALT|K1_CTRL)) { if (ks == 'T' || ks == 't') { tmode ^= 1; if (!tmode) resettrace((regcontext_t *)&saved_sigframe-> sf_uc.uc_mcontext); break; } if (ks == 'R' || ks == 'r') { kill(getpid(), SIGALRM); /* redraw */ break; } } if (ks < ' ' || ks > '~') break; scan = Ascii2Scan[ks]; docode: if (nlock) scan |= 0x100; if ((scan & ~0x100) > 88) { scan = 0xffff; break; } if ((K1_STATUS & K1_SHIFT) || (scan & 0x100)) { scan = ScanCodes[scan & 0xff].shift; } else if (K1_STATUS & K1_CTRL) { scan = ScanCodes[scan & 0xff].ctrl; } else if (K1_STATUS & K1_ALT) { scan = ScanCodes[scan & 0xff].alt; } else scan = ScanCodes[scan & 0xff].base; break; } if (scan != 0xffff) { break_code = scan >> 8; KbdWrite(scan); } return(1); } default: break; } return(0); } #endif void tty_move(int r, int c) { row = r; col = c; SetVREGCur(); } void tty_report(int *r, int *c) { *r = row; *c = col; } void tty_flush() { K_NEXT = K_FREE = K_BUFSTARTP; } void tty_index(int scroll) { int i; if (row > (height - 1)) row = 0; else if (++row >= height) { row = height - 1; if (scroll) { memcpy(vmem, &vmem[width], 2 * width * (height - 1)); for (i = 0; i < width; ++i) vmem[(height - 1) * width + i] = vattr | ' '; } } SetVREGCur(); } void tty_write(int c, int attr) { if (attr == TTYF_REDIRECT) { if (redirect1) { write(1, &c, 1); return; } attr = -1; } if (capture_fd >= 0) { char cc = c; write(capture_fd, &cc, 1); } c &= 0xff; switch (c) { case 0x07: if (xmode) { #ifndef NO_X XBell(dpy, 0); #endif } else write(1, "\007", 1); break; case 0x08: if (row > (height - 1) || col > width) break; if (col > 0) --col; vmem[row * width + col] &= 0xff00; break; case '\t': if (row > (height - 1)) row = 0; col = (col + 8) & ~0x07; if (col > width) { col = 0; tty_index(1); } break; case '\r': col = 0; break; case '\n': tty_index(1); break; default: if (col >= width) { col = 0; tty_index(1); } if (row > (height - 1)) row = 0; if (attr >= 0) vmem[row * width + col] = attr & 0xff00; else vmem[row * width + col] &= 0xff00; vmem[row * width + col++] |= c; break; } SetVREGCur(); } void tty_rwrite(int n, int c, int attr) { u_char srow, scol; c &= 0xff; #ifndef NO_X if (VGA_ATC[ATC_ModeCtrl] & 1) { tty_rwrite_graphics(n, c, attr); return; } #endif srow = row; scol = col; while (n--) { if (col >= width) { col = 0; tty_index(0); } if (row > (height - 1)) row = 0; if (attr >= 0) vmem[row * width + col] = attr & 0xff00; else vmem[row * width + col] &= 0xff00; vmem[row * width + col++] |= c; } row = srow; col = scol; SetVREGCur(); } #ifndef NO_X /* Write a character in graphics mode. Note that the text is put at *text* coordinates. */ static void tty_rwrite_graphics(int n, int c, int attr) { u_int8_t srow, scol; int ht = height / CharHeight; int wd = width / 8; srow = row; scol = col; while (n--) { if (col >= wd) { col = 0; /* tty_index(0); *//* scroll up if last line is filled */ } if (row > (ht - 1)) row = 0; putchar_graphics(row * wd * CharHeight + col, c, attr); col++; } row = srow; col = scol; SetVREGCur(); return; } /* Put the character together from its pixel representation in 'font8xXX[]' and write it to 'vram'. The attribute byte gives the desired color; if bit 7 is set, the pixels are XOR'd with the underlying color(s). XXX This must be updated for the 256 color modes. */ static void putchar_graphics(int xy, int c, int attr) { int i, j; u_int8_t cline; u_int8_t *cpos; /* get char position in the pixel representation */ cpos = (u_int8_t *)(0xC3000 + c * CharHeight); for (i = 0; i < CharHeight; i++) { cline = cpos[i]; for (j = 0; j < 4; j++) { if (attr & 0x8000) { /* XOR */ if (attr & (0x0100 << j)) vram[xy + i * width / 8 + j * 0x10000] ^= cline; } else { /* replace */ if (attr & (0x0100 << j)) vram[xy + i * width / 8 + j * 0x10000] &= ~cline; else vram[xy + i * width / 8 + j * 0x10000] |= cline; } } } return; } #endif void tty_pause() { sigset_t set; sigprocmask(0, 0, &set); sigdelset(&set, SIGIO); sigdelset(&set, SIGALRM); sigsuspend(&set); } static int nextchar = 0; int tty_read(REGISTERS, int flag) { int r; if ((r = nextchar) != 0) { nextchar = 0; return(r & 0xff); } if ((flag & TTYF_REDIRECT) && redirect0) { char c; if (read(STDIN_FILENO, &c, 1) != 1) return(-1); if (c == '\n') c = '\r'; return(c); } if (KbdEmpty()) { if (flag & TTYF_BLOCK) { while (KbdEmpty()) tty_pause(); } else { return(-1); } } r = KbdRead(); if ((r & 0xff) == 0) nextchar = r >> 8; r &= 0xff; if (flag & TTYF_CTRL) { if (r == 3) { /* * XXX - Not quite sure where we should return, maybe not * all the way to the user, but... */ if (ivec[0x23] && (ivec[0x23] >> 16) != 0xF000) { fake_int(REGS, 0x23); R_EIP = R_EIP - 2; return(-2); } } } if (flag & TTYF_ECHO) { if ((flag & TTYF_ECHONL) && (r == '\n' || r == '\r')) { tty_write('\r', -1); tty_write('\n', -1); } else tty_write(r, -1); } return(r & 0xff); } int tty_peek(REGISTERS, int flag) { int c; if (c == nextchar) return(nextchar & 0xff); if (KbdEmpty()) { if (flag & TTYF_POLL) { sleep_poll(); if (KbdEmpty()) return(0); } else if (flag & TTYF_BLOCK) { while (KbdEmpty()) tty_pause(); } else return(0); } c = KbdPeek(); if ((c & 0xff) == 3) { /* * XXX - Not quite sure where we should return, maybe not * all the way to the user, but... */ if (ivec[0x23] && (ivec[0x23] >> 16) != 0xF000) { fake_int(REGS, 0x23); R_EIP = R_EIP - 2; return(-2); } } return(0xff); } int tty_state() { return(K1_STATUS); } int tty_estate() { int state = 0; if (K2_STATUS & K2_SYSREQ) state |= 0x80; if (K2_STATUS & K2_CLOCK) state |= 0x40; if (K2_STATUS & K2_NLOCK) state |= 0x20; if (K2_STATUS & K2_SLOCK) state |= 0x10; if (K3_STATUS & K3_RALT) state |= 0x08; if (K3_STATUS & K3_RCTRL) state |= 0x04; if (K2_STATUS & K2_LALT) state |= 0x02; if (K2_STATUS & K2_LCTRL) state |= 0x01; return(state); } static int inrange(int a, int n, int x) { return(a < n ? n : a > x ? x : a); } void tty_scroll(int sr, int sc, int er, int ec, int n, int attr) { int i, j; sr = inrange(sr, 0, height); er = inrange(er, 0, height); sc = inrange(sc, 0, width); ec = inrange(ec, 0, width); if (sr > er || sc > ec) return; ++er; ++ec; attr &= 0xff00; attr |= ' '; if (n > 0 && n < er - sr) { for (j = sr; j < er - n; ) { memcpy(&vmem[j * width + sc], &vmem[(j + n) * width + sc], sizeof(vmem[0]) * (ec - sc)); ++j; } } else n = er - sr; for (j = er - n; j < er; ) { for (i = sc; i < ec; ++i) vmem[j * width + i] = attr; ++j; } } void tty_rscroll(int sr, int sc, int er, int ec, int n, int attr) { int i, j; sr = inrange(sr, 0, height); er = inrange(er, 0, height); sc = inrange(sc, 0, width); ec = inrange(ec, 0, width); if (sr > er || sc > ec) return; ++er; ++ec; attr &= 0xff00; attr |= ' '; if (n > 0 && n < er - sr) { for (j = er; j > sr + n; ) { --j; memcpy(&vmem[j * width + sc], &vmem[(j - n) * width + sc], sizeof(vmem[0]) * (ec - sc)); } } else n = er - sr; for (j = sr + n; j > sr; ) { --j; for (i = sc; i < ec; ++i) vmem[j * width + i] = attr; } } int tty_char(int r, int c) { if (r == -1) r = row; if (c == -1) c = col; r = inrange(r, 0, height); c = inrange(c, 0, width); return(vmem[r * width + c]); } int KbdEmpty() { return(K_NEXT == K_FREE); } void KbdWrite(u_short code) { int kf; kf = K_FREE + 2; if (kf == K_BUFENDP) kf = K_BUFSTARTP; if (kf == K_NEXT) { #ifndef NO_X XBell(dpy, 0); #endif return; } K_BUF(K_FREE) = code; K_FREE = kf; } u_short KbdRead() { int kf = K_NEXT; K_NEXT = K_NEXT + 2; if (K_NEXT == K_BUFENDP) K_NEXT = K_BUFSTARTP; return(K_BUF(kf)); } u_short KbdPeek() { return(K_BUF(K_NEXT)); } void kbd_init() { u_long vec; define_input_port_handler(0x60, inb_port60); K_BUFSTARTP = 0x1e; /* Start of keyboard buffer */ K_BUFENDP = 0x3e; /* End of keyboard buffer */ K_NEXT = K_FREE = K_BUFSTARTP; vec = insert_hardint_trampoline(); ivec[0x09] = vec; register_callback(vec, int09, "int 09"); return; } void kbd_bios_init() { BIOSDATA[0x96] = 0x10; /* MF II kbd, 101 keys */ K1_STATUS = 0; K2_STATUS = 0; K3_STATUS = 0; K4_STATUS = 0; } #ifndef NO_X /* Calculate 16 bit RGB values for X from the 6 bit DAC values and the palette. This works for 16 and 256 color modes, although we don't really support the latter yet. */ static void dac2rgb(XColor *color, int i) { int n, m; /* 256 colors are easy; just take the RGB values from the DAC and shift left. For the pedants: multiplication with 65535./63. and rounding makes a difference of less than two percent. */ if (VGA_ATC[ATC_ModeCtrl] & 0x40) { color->red = dac_rgb[i].red << 10; color->green = dac_rgb[i].green << 10; color->blue = dac_rgb[i].blue << 10; return; } /* For the 16 color modes, check bit 7 of the Mode Control register in the ATC. If set, we take bits 0-3 of the Color Select register and bits 0-3 of the palette register 'i' to build the index into the DAC table; otherwise, bits 2 and 3 of the CS reg and bits 0-5 of the palette register are used. Note that the entries in 'palette[]' are supposed to be already masked to 6 bits. */ if (VGA_ATC[ATC_ModeCtrl] & 0x80) { n = VGA_ATC[ATC_ColorSelect] & 0x0f; m = palette[i] & 0x0f; } else { n = VGA_ATC[ATC_ColorSelect] & 0x0c; m = palette[i]; } color->red = dac_rgb[16*n + m].red << 10; color->green = dac_rgb[16*n + m].green << 10; color->blue = dac_rgb[16*n + m].blue << 10; } #endif /* Get a connection to the X server and create the window. */ void init_window() { #ifndef NO_X XGCValues gcv; int i; { /* * Arg... I can no longer change X's fd out from under it. * Open up all the available fd's, leave 3 behind for X * to play with, open X and then release all the other fds */ int nfds = sysconf(_SC_OPEN_MAX); int *fds = malloc(sizeof(int) * nfds); i = 0; if (fds) for (i = 0; i < nfds && (i == 0 || fds[i-1] < 63); ++i) if ((fds[i] = open(_PATH_DEVNULL, 0)) < 0) break; /* * Leave 3 fds behind for X to play with */ if (i > 0) close(fds[--i]); if (i > 0) close(fds[--i]); if (i > 0) close(fds[--i]); dpy = XOpenDisplay(NULL); while (i > 0) close(fds[--i]); } if (dpy == NULL) err(1, "Could not open display ``%s''\n", XDisplayName(NULL)); xfd = ConnectionNumber(dpy); _RegisterIO(xfd, video_async_event, &xfd, Failure); if (debug_flags & D_DEBUGIN) _RegisterIO(0, debug_event, 0, Failure); _BlockIO(); /* Create window, but defer setting a size and GC. */ win = XCreateSimpleWindow(dpy, DefaultRootWindow(dpy), 0, 0, 1, 1, 2, black, black); gcv.foreground = white; gcv.background = black; gc = XCreateGC(dpy, win, GCForeground | GCBackground, &gcv); gcv.foreground = 1; gcv.background = 0; gcv.function = GXxor; cgc = XCreateGC(dpy, win, GCForeground|GCBackground|GCFunction, &gcv); if (raw_kbd) { XSelectInput(dpy, win, ExposureMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask ); } else { XSelectInput(dpy, win, KeyReleaseMask | KeyPressMask | ExposureMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask ); } XStoreName(dpy, win, "DOS"); _UnblockIO(); /* Get the default visual and depth for later use. */ depth = DefaultDepth(dpy, DefaultScreen(dpy)); visual = DefaultVisual(dpy, DefaultScreen(dpy)); prepare_lut(); #if 0 /* While we are developing the graphics code ... */ call_on_quit(write_vram, NULL); #endif #endif } void load_font() { #ifndef NO_X XGCValues gcv; if (!xfont) xfont = FONTVGA; font = XLoadQueryFont(dpy, xfont); if (font == NULL) font = XLoadQueryFont(dpy, FONTVGA); if (font == NULL) err(1, "Could not open font ``%s''\n", xfont); gcv.font = font->fid; XChangeGC(dpy, gc, GCFont, &gcv); FW = font->max_bounds.width; FH = font->max_bounds.ascent + font->max_bounds.descent; FD = font->max_bounds.descent; /* Put the pixel representation at c000:3000. */ switch (CharHeight) { case 8: memcpy((void *)0xc3000, font8x8, sizeof(font8x8)); break; case 14: memcpy((void *)0xc3000, font8x14, sizeof(font8x14)); break; case 16: memcpy((void *)0xc3000, font8x16, sizeof(font8x16)); break; default: err(1, "load_font: CharHeight = %d?", CharHeight); } return; #endif } /* Get a new, or resize an old XImage as canvas for the graphics display. */ void get_ximage() { #ifndef NO_X if (xi != NULL) XFree(xi); xi = XCreateImage(dpy, visual, depth, ZPixmap, 0, NULL, width, height, 32, 0); if (xi == NULL) err(1, "Could not get ximage"); xi->data = malloc(width * height * depth / 8); if (xi->data == NULL) { XDestroyImage(xi); err(1, "Could not get memory for ximage data"); } return; #endif } /* Get memory for the text line buffer. */ void get_lines() { int i; if (lines == NULL) { lines = (TextLine *)malloc(sizeof(TextLine) * height); if (lines == NULL) err(1, "Could not allocate data structure for text lines\n"); for (i = 0; i < height; ++i) { lines[i].max_length = width; lines[i].data = (u_short *)malloc(width * sizeof(u_short)); if (lines[i].data == NULL) err(1, "Could not allocate data structure for text lines\n"); lines[i].changed = 1; } } else { lines = (TextLine *)realloc(lines, sizeof(TextLine) * height); if (lines == NULL) err(1, "Could not allocate data structure for text lines\n"); for (i = 0; i < height; ++i) { lines[i].max_length = width; lines[i].data = (u_short *)realloc(lines[i].data, width * sizeof(u_short)); if (lines[i].data == NULL) err(1, "Could not allocate data structure for text lines\n"); lines[i].changed = 1; } } } #ifndef NO_X /* Prepare the LUT for the VRAM -> XImage conversion. */ static void prepare_lut() { int i, j, k; for (i = 0; i < 4; i++) { for (j = 0; j < 256; j++) { for (k = 0; k < 8; k++) { lut[i][j][7 - k] = ((j & (1 << k)) ? (1 << i) : 0); } } } return; } #endif /* Resize the window, using information from 'vga_status[]'. This function is called after a mode change. */ void resize_window() { #ifndef NO_X XSizeHints *sh; vmode_t vmode; sh = XAllocSizeHints(); if (sh == NULL) err(1, "Could not get XSizeHints structure"); #endif width = DpyCols; height = DpyRows + 1; #ifndef NO_X vmode = vmodelist[find_vmode(VideoMode)]; if (vmode.type == TEXT) { sh->base_width = FW * width + 4; sh->base_height = FH * height + 4; sh->base_width += 4; sh->base_height += 4; } else { width *= 8; height *= CharHeight; sh->base_width = width; sh->base_height = height; } sh->min_width = sh->max_width = sh->base_width; sh->min_height = sh->max_height = sh->base_height; sh->flags = USSize | PMinSize | PMaxSize | PSize; debug(D_VIDEO, "VGA: Set window size %dx%d\n", sh->base_width, sh->base_height); XSetWMNormalHints(dpy, win, sh); XResizeWindow(dpy, win, sh->base_width, sh->base_height); XMapWindow(dpy, win); XFlush(dpy); XFree(sh); return; #endif } /* Calculate 'pixels[]' from the current DAC table and palette. To do: do not use 'pixels[]', use an array of 'XColor's which we can allocate and free on demand. Install a private colormap if necessary. */ void update_pixels() { #ifndef NO_X int i; /* We support only 16 colors for now. */ for (i = 0; i < 16; i++) { XColor color; dac2rgb(&color, i); if (XAllocColor(dpy, DefaultColormap(dpy, DefaultScreen(dpy)), &color)) { pixels[i] = color.pixel; } else if (i < 7) pixels[i] = BlackPixel(dpy, DefaultScreen(dpy)); else pixels[i] = WhitePixel(dpy, DefaultScreen(dpy)); } #endif } void write_vram(void *arg) { int fd; if ((fd = open("vram", O_WRONLY | O_CREAT, 0644)) == -1) err(1, "Can't open vram file"); (void)write(fd, (void *)vram, 256 * 1024); return; } Index: head/usr.bin/doscmd/video.c =================================================================== --- head/usr.bin/doscmd/video.c (revision 82324) +++ head/usr.bin/doscmd/video.c (revision 82325) @@ -1,737 +1,737 @@ /* * Copyright (c) 2001 The FreeBSD Project, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY The FreeBSD Project, Inc. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL The FreeBSD Project, Inc. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include #include #include #include "doscmd.h" #include "AsyncIO.h" #include "tty.h" #include "video.h" #include "vparams.h" /* * Global variables */ /* VGA registers */ u_int8_t VGA_CRTC[CRTC_Size]; u_int8_t VGA_ATC[ATC_Size]; u_int8_t VGA_TSC[TSC_Size]; u_int8_t VGA_GDC[GDC_Size]; /* VGA status information */ u_int8_t vga_status[64]; /* Table of supported video modes. */ vmode_t vmodelist[] = { {0x00, 0x17, TEXT, 16, 8, 2, 0xb8000, FONT8x16}, {0x01, 0x17, TEXT, 16, 8, 2, 0xb8000, FONT8x16}, {0x02, 0x18, TEXT, 16, 8, 2, 0xb8000, FONT8x16}, {0x03, 0x18, TEXT, 16, 8, 2, 0xb8000, FONT8x16}, {0x04, 0x04, GRAPHICS, 4, 1, 0, 0xb8000, FONT8x8}, {0x05, 0x05, GRAPHICS, 4, 1, 0, 0xb8000, FONT8x8}, {0x06, 0x06, GRAPHICS, 2, 1, 0, 0xb8000, FONT8x8}, {0x07, 0x19, TEXT, 1, 8, 2, 0xb0000, FONT8x16}, {0x08, 0x08, NOMODE, 0, 0, 0, 0, 0}, {0x09, 0x09, NOMODE, 0, 0, 0, 0, 0}, {0x0a, 0x0a, NOMODE, 0, 0, 0, 0, 0}, {0x0b, 0x0b, NOMODE, 0, 0, 0, 0, 0}, {0x0c, 0x0c, NOMODE, 0, 0, 0, 0, 0}, {0x0d, 0x0d, GRAPHICS, 16, 8, 0, 0xa0000, FONT8x8}, {0x0e, 0x0e, GRAPHICS, 16, 4, 0, 0xa0000, FONT8x8}, {0x0f, 0x11, GRAPHICS, 1, 2, 1, 0xa0000, FONT8x14}, {0x10, 0x12, GRAPHICS, 16, 2, 1, 0xa0000, FONT8x14}, {0x11, 0x1a, GRAPHICS, 2, 1, 3, 0xa0000, FONT8x16}, {0x12, 0x1b, GRAPHICS, 16, 1, 3, 0xa0000, FONT8x16}, /* {0x13, 0x1c, GRAPHICS, 256, 1, 0, 0xa0000, FONT8x8}, */ }; #define NUMMODES (sizeof(vmodelist) / sizeof(vmode_t)) /* * Local functions */ static void init_vga(void); static u_int8_t video_inb(int); static void video_outb(int, u_int8_t); /* * Local types and variables */ /* Save Table and assorted variables */ struct VideoSaveTable { u_short video_parameter_table[2]; u_short parameter_dynamic_save_area[2]; /* Not used */ u_short alphanumeric_character_set_override[2]; /* Not used */ u_short graphics_character_set_override[2]; /* Not used */ u_short secondary_save_table[2]; /* Not used */ u_short mbz[4]; }; struct SecondaryVideoSaveTable { u_short length; u_short display_combination_code_table[2]; u_short alphanumeric_character_set_override[2]; /* Not used */ u_short user_palette_profile_table[2]; /* Not used */ u_short mbz[6]; }; struct VideoSaveTable *vsp; struct SecondaryVideoSaveTable *svsp; /* * Read and write the VGA port */ /* Save the selected index register */ static u_int8_t crtc_index, atc_index, tsc_index, gdc_index; /* Toggle between index and data on port ATC_WritePort */ static u_int8_t set_atc_index = 1; static u_int8_t video_inb(int port) { switch(port) { case CRTC_DataPortColor: return VGA_CRTC[crtc_index]; case CRTC_IndexPortColor: return crtc_index; case ATC_ReadPort: return VGA_ATC[atc_index]; case TSC_DataPort: return VGA_TSC[tsc_index]; case TSC_IndexPort: return tsc_index; case GDC_DataPort: return VGA_GDC[gdc_index]; case GDC_IndexPort: return gdc_index; case VGA_InputStatus1Port: set_atc_index = 1; return VGA_InputStatus1; default: return 0; } } static void video_outb(int port, u_int8_t value) { /* XXX */ #define row (CursRow0) #define col (CursCol0) int cp; switch (port) { case CRTC_IndexPortColor: crtc_index = value; break; case CRTC_DataPortColor: VGA_CRTC[crtc_index] = value; switch (crtc_index) { case CRTC_CurLocHi: /* Update cursor position in BIOS */ cp = row * DpyCols + col; cp &= 0xff; cp |= value << 8; row = cp / DpyCols; col = cp % DpyCols; break; case CRTC_CurLocLo: /* Update cursor position in BIOS */ cp = row * DpyCols + col; cp &= 0xff00; cp |= value; row = cp / DpyCols; col = cp % DpyCols; break; default: debug(D_VIDEO, "VGA: outb 0x%04x, 0x%02x at index 0x%02x\n", port, value, crtc_index); break; } case CRTC_IndexPortMono: /* Not used */ break; case CRTC_DataPortMono: /* Not used */ break; case ATC_WritePort: if (set_atc_index) atc_index = value; else { VGA_ATC[atc_index] = value; switch (atc_index) { default: debug(D_VIDEO, "VGA: outb 0x%04x, 0x%02x at index 0x%02x\n", port, value, crtc_index); break; } } set_atc_index = 1 - set_atc_index; break; case TSC_IndexPort: tsc_index = value; break; case TSC_DataPort: VGA_TSC[tsc_index] = value; switch (tsc_index) { default: debug(D_VIDEO, "VGA: outb 0x%04x, 0x%02x at index 0x%02x\n", port, value, crtc_index); break; } break; case GDC_IndexPort: gdc_index = value; break; case GDC_DataPort: VGA_GDC[gdc_index] = value; #if 0 switch (gdc_index) { default: debug(D_VIDEO, "VGA: outb 0x%04x, 0x%02x at index 0x%02x\n", port, value, crtc_index); break; } #endif break; default: debug(D_ALWAYS, "VGA: Unknown port 0x%4x\n", port); break; } return; #undef row #undef col } void video_init() { /* If we are running under X, get a connection to the X server and create an empty window of size (1, 1). It makes a couple of init functions a lot easier. */ if (xmode) { init_window(); /* Set VGA emulator to a sane state */ init_vga(); /* Initialize mode 3 (text, 80x25, 16 colors) */ init_mode(3); } /* Define all known I/O port handlers */ if (!raw_kbd) { define_input_port_handler(CRTC_IndexPortColor, video_inb); define_input_port_handler(CRTC_DataPortColor, video_inb); define_input_port_handler(ATC_ReadPort, video_inb); define_input_port_handler(TSC_IndexPort, video_inb); define_input_port_handler(TSC_DataPort, video_inb); define_input_port_handler(GDC_IndexPort, video_inb); define_input_port_handler(GDC_DataPort, video_inb); define_output_port_handler(CRTC_IndexPortColor, video_outb); define_output_port_handler(CRTC_DataPortColor, video_outb); define_output_port_handler(ATC_WritePort, video_outb); define_output_port_handler(TSC_IndexPort, video_outb); define_output_port_handler(TSC_DataPort, video_outb); define_output_port_handler(GDC_IndexPort, video_outb); define_output_port_handler(GDC_DataPort, video_outb); } redirect0 = isatty(0) == 0 || !xmode ; redirect1 = isatty(1) == 0 || !xmode ; redirect2 = isatty(2) == 0 || !xmode ; return; } void video_bios_init() { u_char *p; u_long vec; if (raw_kbd) return; /* * Put the Video Save Table Pointer @ C000:0000 * Put the Secondary Video Save Table Pointer @ C000:0020 * Put the Display Combination Code table @ C000:0040 * Put the Video Parameter table @ C000:1000 - C000:2FFF */ BIOS_SaveTablePointer = 0xC0000000; vsp = (struct VideoSaveTable *)0xC0000L; memset(vsp, 0, sizeof(struct VideoSaveTable)); svsp = (struct SecondaryVideoSaveTable *)0xC0020L; vsp->video_parameter_table[0] = 0x1000; vsp->video_parameter_table[1] = 0xC000; vsp->secondary_save_table[0] = 0x0020; vsp->secondary_save_table[1] = 0xC000; svsp->display_combination_code_table[0] = 0x0040; svsp->display_combination_code_table[1] = 0xC000; p = (u_char *)0xC0040; *p++ = 2; /* Only support 2 combinations currently */ *p++ = 1; /* Version # */ *p++ = 8; /* We won't use more than type 8 */ *p++ = 0; /* Reserved */ *p++ = 0; *p++ = 0; /* No Display No Display */ *p++ = 0; *p++ = 8; /* No Display VGA Color */ memcpy((void *)0xC1000, videoparams, sizeof(videoparams)); ivec[0x1d] = 0xC0001000L; /* Video Parameter Table */ ivec[0x42] = ivec[0x10]; /* Copy of video interrupt */ /* Put the current font at C000:3000; the pixels are copied in 'tty.c:load_font()'. */ ivec[0x1f] = 0xC0003000L; ivec[0x43] = 0xC0003000L; BIOSDATA[0x8a] = 1; /* Index into DCC table */ vec = insert_softint_trampoline(); ivec[0x10] = vec; register_callback(vec, int10, "int 10"); } /* Initialize the VGA emulator XXX This is not nearly finished right now. */ static void init_vga(void) { int i; /* Zero-fill 'dac_rgb' on allocation; the default (EGA) table has only 64 entries. */ dac_rgb = (struct dac_colors *)calloc(256, sizeof(struct dac_colors)); if (dac_rgb == NULL) err(1, "Get memory for dac_rgb"); /* Copy the default DAC table to a working copy we can trash. */ for (i = 0; i < 64; i++) dac_rgb[i] = dac_default64[i]; /* Structure copy */ /* Point 'palette[]' to the Attribute Controller space. We will only use the first 16 slots. */ palette = VGA_ATC; /* Get memory for the video RAM and adjust the plane pointers. */ vram = calloc(256 * 1024, 1); /* XXX */ if (vram == NULL) warn("Could not get video memory; graphics modes not available."); /* XXX There is probably a more efficient memory layout... */ vplane0 = vram; vplane1 = vram + 0x10000; vplane2 = vram + 0x20000; vplane3 = vram + 0x30000; } /* * Initialize the requested video mode. */ /* Indices into the video parameter table. We will use that array to initialize the registers on startup and when the video mode changes. */ #define CRTC_Ofs 10 #define ATC_Ofs 35 #define TSC_Ofs 5 #define GDC_Ofs 55 #define MiscOutput_Ofs 9 void init_mode(int mode) { vmode_t vmode; int idx; /* Index into vmode */ int pidx; /* Index into videoparams */ debug(D_VIDEO, "VGA: Set video mode to 0x%02x\n", mode); idx = find_vmode(mode & 0x7f); if (idx == -1 || vmodelist[idx].type == NOMODE) err(1, "Mode 0x%02x is not supported", mode); vmode = vmodelist[idx]; pidx = vmode.paramindex; /* Preset VGA registers. */ memcpy(VGA_CRTC, (u_int8_t *)&videoparams[pidx][CRTC_Ofs], sizeof(VGA_CRTC)); memcpy(VGA_ATC, (u_int8_t *)&videoparams[pidx][ATC_Ofs], sizeof(VGA_ATC)); /* Warning: the video parameter table does not contain the Sequencer's Reset register. Its default value is 0x03.*/ VGA_TSC[TSC_Reset] = 0x03; memcpy(VGA_TSC + 1, (u_int8_t *)&videoparams[pidx][TSC_Ofs], sizeof(VGA_TSC) - 1); memcpy(VGA_GDC, (u_int8_t *)&videoparams[pidx][GDC_Ofs], sizeof(VGA_GDC)); VGA_MiscOutput = videoparams[pidx][MiscOutput_Ofs]; /* Paranoia */ if ((VGA_ATC[ATC_ModeCtrl] & 1) == 1 && vmode.type == TEXT) err(1, "Text mode requested, but ATC switched to graphics mode!"); if ((VGA_ATC[ATC_ModeCtrl] & 1) == 0 && vmode.type == GRAPHICS) err(1, "Graphics mode requested, but ATC switched to text mode!"); VideoMode = mode & 0x7f; DpyCols = (u_int16_t)videoparams[pidx][0]; DpyPageSize = *(u_int16_t *)&videoparams[pidx][3]; ActivePageOfs = 0; CursCol0 = 0; CursRow0 = 0; CursCol1 = 0; CursRow1 = 0; CursCol2 = 0; CursRow2 = 0; CursCol3 = 0; CursRow3 = 0; CursCol4 = 0; CursRow4 = 0; CursCol5 = 0; CursRow5 = 0; CursCol6 = 0; CursRow6 = 0; CursCol7 = 0; CursRow7 = 0; CursStart = VGA_CRTC[CRTC_CursStart]; CursEnd = VGA_CRTC[CRTC_CursEnd]; ActivePage = 0; DpyRows = videoparams[pidx][1]; CharHeight = videoparams[pidx][2]; CRTCPort = vmode.numcolors > 1 ? CRTC_IndexPortColor : CRTC_IndexPortMono; NumColors = vmode.numcolors; NumPages = vmode.numpages; VertResolution = vmode.vrescode; vmem = (u_int16_t *)vmode.vmemaddr; /* Copy VGA related BIOS variables from 'vga_status'. */ memcpy(&BIOS_VideoMode, &VideoMode, 33); BIOS_DpyRows = DpyRows; BIOS_CharHeight = CharHeight; _BlockIO(); /* Load 'pixels[]' from default DAC values. */ update_pixels(); /* Update font. */ xfont = vmode.fontname; load_font(); /* Resize window if necessary. */ resize_window(); _UnblockIO(); /* Mmap video memory for the graphics modes. Write access to 0xa0000 - 0xaffff will generate a T_PAGEFAULT trap in VM86 mode (aside: why not a SIGSEGV?), which is handled in 'trap.c:sigbus()'. */ if (vmode.type == GRAPHICS) { vmem = mmap((void *)0xa0000, 64 * 1024, PROT_NONE, - MAP_ANON | MAP_FIXED | MAP_INHERIT | MAP_SHARED, -1, 0); + MAP_ANON | MAP_FIXED | MAP_SHARED, -1, 0); if (vmem == NULL) fatal("Could not mmap() video memory"); /* Create an XImage to display the graphics screen. */ get_ximage(); } else { int i; get_lines(); if (mode & 0x80) return; /* Initialize video memory with black background, white foreground */ vattr = 0x0700; for (i = 0; i < DpyPageSize / 2; ++i) vmem[i] = vattr; } return; } /* Find the requested mode in the 'vmodelist' table. This function returns the index into this table; we will also use the index for accessing the 'videoparams' array. */ int find_vmode(int mode) { unsigned i; for (i = 0; i < NUMMODES; i++) if (vmodelist[i].modenumber == mode) return i; return -1; } /* Handle access to the graphics memory. Simply changing the protection for the memory is not enough, unfortunately. It would only work for the 256 color modes, where a memory byte contains the color value of one pixel. The 16 color modes (and 4 color modes) make use of four bit planes which overlay the first 64K of video memory. The bits are distributed into these bit planes according to the GDC state, so we will have to emulate the CPU instructions (see 'cpu.c:emu_instr()'). Handling the 256 color modes will be a bit easier, once we support those at all. */ int vmem_pageflt(struct sigframe *sf) { regcontext_t *REGS = (regcontext_t *)(&sf->sf_uc.uc_mcontext); /* The ATC's Mode Control register tells us whether 4 or 8 color bits are used */ if (VGA_ATC[ATC_ModeCtrl] & (1 << 6)) { /* 256 colors, allow writes; the protection will be set back to PROT_READ at the next display update */ mprotect(vmem, 64 * 1024, PROT_READ | PROT_WRITE); return 0; } /* There's no need to change the protection in the 16 color modes, we will write to 'vram'. Just emulate the next instruction. */ return emu_instr(REGS); } /* We need to keep track of the latches' contents.*/ static u_int8_t latch0, latch1, latch2, latch3; /* Read a byte from the video memory. 'vga_read()' is called from 'cpu.c:read_byte()' and will emulate the VGA read modes. */ u_int8_t vga_read(u_int32_t addr) { u_int32_t dst; /* 'addr' lies between 0xa0000 and 0xaffff. */ dst = addr - 0xa0000; /* Fill latches. */ latch0 = vplane0[dst]; latch1 = vplane1[dst]; latch2 = vplane2[dst]; latch3 = vplane3[dst]; /* Select read mode. */ if ((VGA_GDC[GDC_Mode] & 0x80) == 0) /* Read Mode 0; return the byte from the selected bit plane. */ return vram[dst + (VGA_GDC[GDC_ReadMapSelect] & 3) * 0x10000]; /* Read Mode 1 */ debug(D_ALWAYS, "VGA: Read Mode 1 not implemented\n"); return 0; } /* Write a byte to the video memory. 'vga_write()' is called from 'cpu.c:write_word()' and will emulate the VGA write modes. Not all four modes are implemented yet, nor are the addressing modes (odd/even, chain4). (NB: I think the latter will have to be done in 'tty_graphics_update()'). */ void vga_write(u_int32_t addr, u_int8_t val) { u_int32_t dst; u_int8_t c0, c1, c2, c3; u_int8_t m0, m1, m2, m3; u_int8_t mask; #if 0 unsigned i; debug(D_VIDEO, "VGA: Write 0x%02x to 0x%x\n", val, addr); debug(D_VIDEO, " GDC: "); for (i = 0; i < sizeof(VGA_GDC); i++) debug(D_VIDEO, "%02x ", VGA_GDC[i]); debug(D_VIDEO, "\n"); debug(D_VIDEO, " TSC: "); for (i = 0; i < sizeof(VGA_TSC); i++) debug(D_VIDEO, "%02x ", VGA_TSC[i]); debug(D_VIDEO, "\n"); #endif /* 'addr' lies between 0xa0000 and 0xaffff. */ dst = addr - 0xa0000; c0 = latch0; c1 = latch1; c2 = latch2; c3 = latch3; /* Select write mode. */ switch (VGA_GDC[GDC_Mode] & 3) { case 0: mask = VGA_GDC[GDC_BitMask]; if (VGA_GDC[GDC_DataRotate] & 7) debug(D_ALWAYS, "VGA: Data Rotate != 0\n"); /* Select function. */ switch (VGA_GDC[GDC_DataRotate] & 0x18) { case 0x00: /* replace */ m0 = VGA_GDC[GDC_SetReset] & 1 ? mask : 0x00; m1 = VGA_GDC[GDC_SetReset] & 2 ? mask : 0x00; m2 = VGA_GDC[GDC_SetReset] & 4 ? mask : 0x00; m3 = VGA_GDC[GDC_SetReset] & 8 ? mask : 0x00; c0 = VGA_GDC[GDC_EnableSetReset] & 1 ? c0 & ~mask : val & ~mask; c1 = VGA_GDC[GDC_EnableSetReset] & 2 ? c1 & ~mask : val & ~mask; c2 = VGA_GDC[GDC_EnableSetReset] & 4 ? c2 & ~mask : val & ~mask; c3 = VGA_GDC[GDC_EnableSetReset] & 8 ? c3 & ~mask : val & ~mask; c0 |= m0; c1 |= m1; c2 |= m2; c3 |= m3; break; case 0x08: /* AND */ m0 = VGA_GDC[GDC_SetReset] & 1 ? 0xff : ~mask; m1 = VGA_GDC[GDC_SetReset] & 2 ? 0xff : ~mask; m2 = VGA_GDC[GDC_SetReset] & 4 ? 0xff : ~mask; m3 = VGA_GDC[GDC_SetReset] & 8 ? 0xff : ~mask; c0 = VGA_GDC[GDC_EnableSetReset] & 1 ? c0 & m0 : val & m0; c1 = VGA_GDC[GDC_EnableSetReset] & 2 ? c1 & m1 : val & m1; c2 = VGA_GDC[GDC_EnableSetReset] & 4 ? c2 & m2 : val & m2; c3 = VGA_GDC[GDC_EnableSetReset] & 8 ? c3 & m3 : val & m3; break; case 0x10: /* OR */ m0 = VGA_GDC[GDC_SetReset] & 1 ? mask : 0x00; m1 = VGA_GDC[GDC_SetReset] & 2 ? mask : 0x00; m2 = VGA_GDC[GDC_SetReset] & 4 ? mask : 0x00; m3 = VGA_GDC[GDC_SetReset] & 8 ? mask : 0x00; c0 = VGA_GDC[GDC_EnableSetReset] & 1 ? c0 | m0 : val | m0; c1 = VGA_GDC[GDC_EnableSetReset] & 2 ? c1 | m1 : val | m1; c2 = VGA_GDC[GDC_EnableSetReset] & 4 ? c2 | m2 : val | m2; c3 = VGA_GDC[GDC_EnableSetReset] & 8 ? c3 | m3 : val | m3; break; case 0x18: /* XOR */ m0 = VGA_GDC[GDC_SetReset] & 1 ? mask : 0x00; m1 = VGA_GDC[GDC_SetReset] & 2 ? mask : 0x00; m2 = VGA_GDC[GDC_SetReset] & 4 ? mask : 0x00; m3 = VGA_GDC[GDC_SetReset] & 8 ? mask : 0x00; c0 = VGA_GDC[GDC_EnableSetReset] & 1 ? c0 ^ m0 : val ^ m0; c1 = VGA_GDC[GDC_EnableSetReset] & 2 ? c1 ^ m1 : val ^ m1; c2 = VGA_GDC[GDC_EnableSetReset] & 4 ? c2 ^ m2 : val ^ m2; c3 = VGA_GDC[GDC_EnableSetReset] & 8 ? c3 ^ m3 : val ^ m3; break; } break; case 1: /* Just copy the latches' content to the desired destination address. */ break; case 2: mask = VGA_GDC[GDC_BitMask]; /* select function */ switch (VGA_GDC[GDC_DataRotate] & 0x18) { case 0x00: /* replace */ m0 = (val & 1 ? 0xff : 0x00) & mask; m1 = (val & 2 ? 0xff : 0x00) & mask; m2 = (val & 4 ? 0xff : 0x00) & mask; m3 = (val & 8 ? 0xff : 0x00) & mask; c0 &= ~mask; c1 &= ~mask; c2 &= ~mask; c3 &= ~mask; c0 |= m0; c1 |= m1; c2 |= m2; c3 |= m3; break; case 0x08: /* AND */ m0 = (val & 1 ? 0xff : 0x00) | ~mask; m1 = (val & 2 ? 0xff : 0x00) | ~mask; m2 = (val & 4 ? 0xff : 0x00) | ~mask; m3 = (val & 8 ? 0xff : 0x00) | ~mask; c0 &= m0; c1 &= m1; c2 &= m2; c3 &= m3; break; case 0x10: /* OR */ m0 = (val & 1 ? 0xff : 0x00) & mask; m1 = (val & 2 ? 0xff : 0x00) & mask; m2 = (val & 4 ? 0xff : 0x00) & mask; m3 = (val & 8 ? 0xff : 0x00) & mask; c0 |= m0; c1 |= m1; c2 |= m2; c3 |= m3; break; case 0x18: /* XOR */ m0 = (val & 1 ? 0xff : 0x00) & mask; m1 = (val & 2 ? 0xff : 0x00) & mask; m2 = (val & 4 ? 0xff : 0x00) & mask; m3 = (val & 8 ? 0xff : 0x00) & mask; c0 ^= m0; c1 ^= m1; c2 ^= m2; c3 ^= m3; break; } break; case 3: /* not yet */ debug(D_ALWAYS, "VGA: Write Mode 3 not implemented\n"); break; } /* Write back changed byte, depending on Map Mask register. */ if (VGA_TSC[TSC_MapMask] & 1) vplane0[dst] = c0; if (VGA_TSC[TSC_MapMask] & 2) vplane1[dst] = c1; if (VGA_TSC[TSC_MapMask] & 4) vplane2[dst] = c2; if (VGA_TSC[TSC_MapMask] & 8) vplane3[dst] = c3; return; } Index: head/usr.bin/doscmd/xms.c =================================================================== --- head/usr.bin/doscmd/xms.c (revision 82324) +++ head/usr.bin/doscmd/xms.c (revision 82325) @@ -1,1103 +1,1103 @@ /*- * Copyright (c) 1997 Helmut Wirth * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice immediately at the beginning of the file, witout modification, * this list of conditions, and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ /* * XMS memory manmagement * * To emulate DOS extended memory (EMM) we use an implementation of * HIMEM.SYS driver capabitlities, according to the XMS 3.0 Spec. * The actual memory allocated via XMS calls from DOS is allocated * via malloc by the emulator. Maximum memory allocation is configureable. * * Credits to: * The original author of this file, some parts are still here * Linux dosemu programmers. I looked into their code. */ #include #include #include #include #include "doscmd.h" #include "xms.h" /* Extended memory handle management */ static XMS_handle xms_hand[NUM_HANDLES]; int num_free_handle = NUM_HANDLES; /* This is planned to be selectable from .doscmdrc */ u_long xms_maxsize = DEFAULT_EMM_SIZE; static u_long xms_free_mem; static u_long xms_used_mem; static u_char vec_grabbed; /* Address entry for zero size allocated handles */ #define XMS_NULL_ALLOC 0xffffffff /* High memory area (HMA) management */ static u_char HMA_allocated = 0; static short HMA_a20 = -1; static int HMA_fd_off, HMA_fd_on; /* high memory mapfiles */ static char memfile[] = "/tmp/doscmd.XXXXXX"; /* Upper memory block (UMB) management */ UMB_block *UMB_freelist = NULL; UMB_block *UMB_alloclist = NULL; /* Calls to emulator */ u_long xms_vector; static u_char xms_trampoline[] = { 0xeb, /* JMP 5 */ 0x03, 0x90, /* NOP */ 0x90, /* NOP */ 0x90, /* NOP */ 0xf4, /* HLT */ 0xcb, /* RETF */ }; /* Local prototypes */ static void add_block(UMB_block **listp, UMB_block *blk); static UMB_block *create_block(u_long addr, u_long size); static void disable_a20(void); static void enable_a20(void); static int get_free_handle(void); static void merge_blocks(void); static void xms_entry(regcontext_t *REGS); /* Init the entire module */ void xms_init(void) { /* Initialize handle table: xms_handle.addr == 0 means free */ bzero((void *)xms_hand, sizeof(XMS_handle) * NUM_HANDLES); xms_free_mem = xms_maxsize; xms_used_mem = 0; vec_grabbed = 0; HMA_allocated = 0; /* Initialize UMB blocks */ /* 0xD0000 to 0xDffff */ add_block(&UMB_freelist, create_block(0xd0000, 64*1024)); /*XXX check for EMS emulation, when it is done! */ /* 0xE0000 to 0xEffff */ /* This is used as window for EMS, will be configurable ! */ /* add_block(&UMB_freelist, create_block(0xe0000, 64*1024)); */ merge_blocks(); xms_vector = insert_generic_trampoline( sizeof(xms_trampoline), xms_trampoline); register_callback(xms_vector + 5, xms_entry, "xms"); } /* * UMB management routines: UMBs normally lie between 0xd0000 and * 0xefff0 in VM86 memory space and are accessible for all DOS applictions. * We could enable more space, but with the emulator we do not * need many drivers, so I think 2 * 64kB will suffice. If EMS emulation * exists, a 64kB segment (0xe0000 - 0xeffff for example) is needed for * the EMS mapping, in this case we have 64kB UMB space. This is more than * many PCs are able to do. * This emulation does only the management for the memory, the memory * is present and read/write/excutable for VM86 applications. */ /* Add a block to a list, maintain ascending start address order */ static void add_block(UMB_block **listp, UMB_block *blk) { UMB_block *bp, *obp; /* No blocks there, attach the new block to the head */ if (*listp == NULL) { *listp = blk; blk->next = NULL; } else { /* Insert at the start */ bp = obp = *listp; if (blk->addr < bp->addr) { blk->next = *listp; *listp = blk; return; } /* Not at the start, insert into the list */ for (; bp != NULL; bp = bp->next) { if (blk->addr > bp->addr) { obp = bp; continue; } else { obp->next = blk; blk->next = bp; return; } } /* Append to the end of the list */ obp->next = blk; blk->next = NULL; } return; } /* Find a block with address addr in the alloc list */ static UMB_block * find_allocated_block(u_long addr) { UMB_block *bp; if (UMB_alloclist == NULL) return NULL; for (bp = UMB_alloclist; bp != NULL; bp = bp->next) if (bp->addr == addr) return bp; return NULL; } /* Remove a block blk from a list, the block must exist on the list */ static void remove_block(UMB_block **listp, UMB_block *blk) { UMB_block *bp; if (*listp == NULL) goto faterr; if (*listp == blk) { *listp = (*listp)->next; return; } bp = *listp; do { if (bp->next == blk) { bp->next = bp->next->next; return; } bp = bp->next; } while(bp != NULL); faterr: fatal("XMS: UMB remove_block did not find block\n"); } /* Try to merge neighbouring blocks in the free list */ static void merge_blocks() { UMB_block *bp; u_long endaddr; if (UMB_freelist == NULL) return; bp = UMB_freelist; do { endaddr = bp->addr + bp->size; if (bp->next != NULL && endaddr == bp->next->addr) { /* Merge the current and the next block */ UMB_block *mergebp = bp->next; bp->size += mergebp->size; bp->next = mergebp->next; free(mergebp); } else { /* Goto next block */ bp = bp->next; } } while (bp != NULL); } /* Try to find a free block of size exactly siz */ static UMB_block * find_exact_block(u_long siz) { UMB_block *bp; if (UMB_freelist == NULL) return NULL; for (bp = UMB_freelist; bp != NULL; bp = bp->next) if (bp->size == siz) return bp; return NULL; } /* Try to find a block with a size bigger than requested. If there is * no such block, return the block with the biggest size. If there is * no free block at all, return NULL */ static UMB_block * find_block(u_long siz) { UMB_block *bp; UMB_block *biggest = NULL; if (UMB_freelist == NULL) return NULL; for (bp = UMB_freelist; bp != NULL; bp = bp->next) { if (bp->size > siz) return bp; if (biggest == NULL) { biggest = bp; continue; } if (biggest->size < bp->size) biggest = bp; } return biggest; } /* Create a block structure, memory is allocated. The structure lives * until the block is merged into another block, then it is freed */ static UMB_block * create_block(u_long addr, u_long size) { UMB_block *blk; if ((blk = malloc(sizeof(UMB_block))) == NULL) fatal ("XMS: Cannot allocate UMB structure\n"); blk->addr = addr; blk->size = size; blk->next = NULL; return blk; } /* * initHMA(): The first 64kB of memory are mapped from 1MB (0x100000) * again to emulate the address wrap around of the 808x. The HMA area * is a cheap trick, usable only with 80386 and higher. The 80[345..]86 * does not have this address wrap around. If more than 1MB is installed * the processor can address more than 1MB: load 0xFFFF to the segment * register and using the full offset of 0xffff the resulting highest * address is (0xffff << 4) + 0xffff = 0x10ffef. Nearly 64kB are accessible * from real or VM86 mode. The mmap calls emulate the address wrap by * mapping the lowest 64kB the the first 64kB after the 1MB limit. * In hardware this is achieved by setting and resetting the a20 bit, * an ugly compatibility hack: The hardware simlpy clamps the address 20 * line of the processor to low and hence the wrap around is forced. * This is switchable via the BIOS or via HIMEM.SYS and therefore the * first 64kB over 1MB can be enabled or disabled at will by software. * DOS uses this trick to load itself high, if the memory is present. * We emulate this behaviour by mapping and unmapping the HMA area. * (Linux has implemented this feature using shared memory (SHM) calls.) * * This routine is called from doscmd.c at startup. A20 is disabled after * startup. */ void initHMA() { int mfd; /* * We need two files, one for the wrap around mapping and one * for the HMA contents */ mfd = mkstemp(memfile); if (mfd < 0) { fprintf(stderr, "memfile: %s\n", strerror(errno)); fprintf(stderr, "High memory will not be mapped\n"); /* We need this for XMS services. If it fails, turn HMA off */ HMA_a20 = -1; return; } unlink(memfile); HMA_fd_off = squirrel_fd(mfd); lseek(HMA_fd_off, 64 * 1024 - 1, 0); write(HMA_fd_off, "", 1); mfd = mkstemp(memfile); if (mfd < 0) { fprintf(stderr, "memfile: %s\n", strerror(errno)); fprintf(stderr, "High memory will not be mapped\n"); /* We need this for XMS services. If it fails, turn HMA off */ HMA_a20 = -1; return; } unlink(memfile); HMA_fd_on = squirrel_fd(mfd); lseek(HMA_fd_on, 64 * 1024 - 1, 0); write(HMA_fd_on, "", 1); if (mmap((caddr_t)0x000000, 0x100000, PROT_EXEC | PROT_READ | PROT_WRITE, - MAP_ANON | MAP_FIXED | MAP_INHERIT | MAP_SHARED, + MAP_ANON | MAP_FIXED | MAP_SHARED, -1, 0) == MAP_FAILED) { perror("Error mapping HMA, HMA disabled: "); HMA_a20 = -1; close(HMA_fd_off); close(HMA_fd_on); return; } if (mmap((caddr_t)0x000000, 64 * 1024, PROT_EXEC | PROT_READ | PROT_WRITE, - MAP_FILE | MAP_FIXED | MAP_INHERIT | MAP_SHARED, + MAP_FILE | MAP_FIXED | MAP_SHARED, HMA_fd_off, 0) == MAP_FAILED) { perror("Error mapping HMA, HMA disabled: "); HMA_a20 = -1; close(HMA_fd_off); close(HMA_fd_on); return; } if (mmap((caddr_t)0x100000, 64 * 1024, PROT_EXEC | PROT_READ | PROT_WRITE, - MAP_FILE | MAP_FIXED | MAP_INHERIT | MAP_SHARED, + MAP_FILE | MAP_FIXED | MAP_SHARED, HMA_fd_off, 0) == MAP_FAILED) { perror("Error mapping HMA, HMA disabled: "); HMA_a20 = -1; close(HMA_fd_off); close(HMA_fd_on); return; } HMA_a20 = 0; } /* Enable the a20 "address line" by unmapping the 64kB over 1MB */ static void enable_a20() { if (HMA_a20 < 0) return; /* Unmap the wrap around portion (fd = HMA_fd_off) */ /* XXX Not sure about this: Should I unmap first, then map new or * does it suffice to map new "over' the existing mapping ? Both * works (define to #if 0 next line and some lines below to try. */ #if 1 if (munmap((caddr_t)0x100000, 64 * 1024) < 0) { fatal("HMA unmapping error: %s\nCannot recover\n", strerror(errno)); } #endif /* Map memory for the HMA with fd = HMA_fd_on */ if (mmap((caddr_t)0x100000, 64 * 1024, PROT_EXEC | PROT_READ | PROT_WRITE, - MAP_FILE | MAP_FIXED | MAP_INHERIT | MAP_SHARED, + MAP_FILE | MAP_FIXED | MAP_SHARED, HMA_fd_on, 0) == MAP_FAILED) { fatal("HMA mapping error: %s\nCannot recover\n", strerror(errno)); } } /* Disable the a20 "address line" by mapping the 64kB over 1MB again */ static void disable_a20() { if (HMA_a20 < 0) return; #if 1 /* Unmap the HMA (fd = HMA_fd_on) */ if (munmap((caddr_t)0x100000, 64 * 1024) < 0) { fatal("HMA unmapping error: %s\nCannot recover\n", strerror(errno)); } #endif /* Remap the wrap around area */ if (mmap((caddr_t)0x100000, 64 * 1024, PROT_EXEC | PROT_READ | PROT_WRITE, - MAP_FILE | MAP_FIXED | MAP_INHERIT | MAP_SHARED, + MAP_FILE | MAP_FIXED | MAP_SHARED, HMA_fd_off, 0) == MAP_FAILED) { fatal("HMA mapping error: %s\nCannot recover\n", strerror(errno)); } } /* * This handles calls to int15 function 88: BIOS extended memory * request. XMS spec says: "In order to maintain compatibility with existing * device drivers, DOS XMS drivers must not hook INT 15h until the first * non-Version Number call to the control function is made." */ void get_raw_extmemory_info(regcontext_t *REGS) { if (vec_grabbed) R_AX = 0x0; else R_AX = xms_maxsize / 1024; return; } /* Handle management routine: Find next free handle */ static int get_free_handle() { int i; /* Linear search, there are only a few handles */ for (i = 0; i < NUM_HANDLES; i++) { if (xms_hand[i].addr == 0) return i + 1; } return 0; } /* Installation check */ int int2f_43(regcontext_t *REGS) { switch (R_AL) { case 0x00: /* installation check */ R_AL = 0x80; break; case 0x10: /* get handler address */ PUTVEC(R_ES, R_BX, xms_vector); break; default: return (0); } return (1); } /* Main call entry point for the XMS handler from DOS */ static void xms_entry(regcontext_t *REGS) { if (R_AH != 0) vec_grabbed = 1; /* If the HMA feature is disabled these calls are "not managed" */ if (HMA_a20 < 0) { if (R_AH == XMS_ALLOCATE_HIGH_MEMORY || R_AH == XMS_FREE_HIGH_MEMORY || R_AH == XMS_GLOBAL_ENABLE_A20 || R_AH == XMS_GLOBAL_DISABLE_A20 || R_AH == XMS_LOCAL_ENABLE_A20 || R_AH == XMS_LOCAL_DISABLE_A20 || R_AH == XMS_QUERY_A20) { R_AX = 0x0; R_BL = XMS_HMA_NOT_MANAGED; return; } } switch (R_AH) { case XMS_GET_VERSION: debug(D_XMS, "XMS: Get Version\n"); R_AX = XMS_VERSION; /* 3.0 */ R_BX = XMS_REVISION; /* internal revision 0 */ R_DX = (HMA_a20 < 0) ? 0x0000 : 0x0001; break; /* * XXX Not exact! Spec says compare size to a HMAMIN parameter and * refuse HMA, if space is too small. With MSDOS 5.0 and higher DOS * itself uses the HMA (DOS=HIGH), so I think we can safely ignore * that. */ case XMS_ALLOCATE_HIGH_MEMORY: debug(D_XMS, "XMS: Allocate HMA\n"); if (HMA_allocated) { R_AX = 0x0; R_BL = XMS_HMA_ALREADY_USED; } else { HMA_allocated = 1; R_AX = 0x1; R_BL = XMS_SUCCESS; } break; case XMS_FREE_HIGH_MEMORY: debug(D_XMS, "XMS: Free HMA\n"); if (HMA_allocated) { HMA_allocated = 0; R_AX = 0x1; R_BL = XMS_SUCCESS; } else { R_AX = 0x0; R_BL = XMS_HMA_NOT_ALLOCATED; } break; case XMS_GLOBAL_ENABLE_A20: debug(D_XMS, "XMS: Global enable A20\n"); if (HMA_a20 == 0) enable_a20(); HMA_a20 = 1; R_AX = 0x1; R_BL = XMS_SUCCESS; break; case XMS_GLOBAL_DISABLE_A20: debug(D_XMS, "XMS: Global disable A20\n"); if (HMA_a20 != 0) disable_a20(); HMA_a20 = 0; R_AX = 0x1; R_BL = XMS_SUCCESS; break; /* * This is an accumulating call. Every call increments HMA_a20. * Caller must use LOCAL_DISBALE_A20 once for each previous call * to LOCAL_ENABLE_A20. */ case XMS_LOCAL_ENABLE_A20: debug(D_XMS, "XMS: Local enable A20\n"); HMA_a20++; if (HMA_a20 == 1) enable_a20(); R_AX = 0x1; R_BL = XMS_SUCCESS; break; case XMS_LOCAL_DISABLE_A20: debug(D_XMS, "XMS: Local disable A20\n"); if (HMA_a20 > 0) HMA_a20--; if (HMA_a20 == 0) disable_a20(); R_AX = 0x1; R_BL = XMS_SUCCESS; break; case XMS_QUERY_A20: /* * Disabled because DOS permanently scans this, to avoid endless output. */ #if 0 debug(D_XMS, "XMS: Query A20\n"); */ #endif R_AX = (HMA_a20 > 0) ? 0x1 : 0x0; R_BL = XMS_SUCCESS; break; case XMS_QUERY_FREE_EXTENDED_MEMORY: /* DOS MEM.EXE chokes, if the HMA is enabled and the reported * free space includes the HMA. So we subtract 64kB from the * space reported, if the HMA is enabled. */ if (HMA_a20 < 0) R_EAX = R_EDX = xms_free_mem / 1024; else R_EAX = R_EDX = (xms_free_mem / 1024) - 64; if (xms_free_mem == 0) R_BL = XMS_FULL; else R_BL = XMS_SUCCESS; debug(D_XMS, "XMS: Query free EMM: Returned %dkB\n", R_AX); break; case XMS_ALLOCATE_EXTENDED_MEMORY: { size_t req_siz; int hindx, hnum; void *mem; debug(D_XMS, "XMS: Allocate EMM: "); /* Enough handles ? */ if ((hnum = get_free_handle()) == 0) { R_AX = 0x00; R_BL = XMS_OUT_OF_HANDLES; debug(D_XMS, " Out of handles\n"); break; } hindx = hnum - 1; req_siz = R_DX * 1024; /* Enough memory ? */ if (req_siz > xms_free_mem) { R_AX = 0x00; R_BL = XMS_FULL; debug(D_XMS, " No memory left\n"); break; } xms_hand[hindx].size = req_siz; xms_hand[hindx].num_locks = 0; /* XXX * Not sure about that: Is it possible to reserve a handle * but with no memory attached ? XMS specs are unclear on * that point. Linux implementation does it this way. */ if (req_siz == 0) { /* This handle is reserved, but has size 0 and no address */ xms_hand[hindx].addr = XMS_NULL_ALLOC; } else { if ((mem = malloc(req_siz)) == NULL) fatal("XMS: Cannot malloc !"); xms_hand[hindx].addr = (u_long)mem; } xms_free_mem -= req_siz; xms_used_mem += req_siz; num_free_handle--; R_AX = 0x1; R_DX = hnum; R_BL = XMS_SUCCESS; debug(D_XMS, " Allocated %d kB, handle %d\n", req_siz / 1024, hnum); break; } case XMS_FREE_EXTENDED_MEMORY: { int hnum, hindx; debug(D_XMS, "XMS: Free EMM: "); hnum = R_DX; if (hnum > NUM_HANDLES || hnum == 0) { R_AX = 0x0; R_BL = XMS_INVALID_HANDLE; debug(D_XMS, " Invalid handle\n"); break; } hindx = hnum - 1; if (xms_hand[hindx].addr == 0) { R_AX = 0x0; R_BL = XMS_INVALID_HANDLE; debug(D_XMS, " Invalid handle\n"); } else if (xms_hand[hindx].num_locks > 0) { R_AX = 0x0; R_BL = XMS_BLOCK_IS_LOCKED; debug(D_XMS, " Is locked\n"); } else { if (xms_hand[hindx].addr != XMS_NULL_ALLOC) { free((void *)xms_hand[hindx].addr); xms_free_mem += xms_hand[hindx].size; xms_used_mem -= xms_hand[hindx].size; } xms_hand[hindx].addr = 0; xms_hand[hindx].size = 0; xms_hand[hindx].num_locks = 0; num_free_handle++; debug(D_XMS, " Success for handle %d\n", hnum); R_AX = 0x1; R_BL = XMS_SUCCESS; } break; } case XMS_MOVE_EXTENDED_MEMORY_BLOCK: { u_long srcptr, dstptr; u_long srcoffs, dstoffs; int srcidx, dstidx; const struct EMM *eptr; int n; debug(D_XMS, "XMS: Move EMM block: "); eptr = (struct EMM *)MAKEPTR(R_DS, R_SI); /* Sanity check: Don't allow eptr pointing to emulator data */ if (((u_long)eptr + sizeof(struct EMM)) >= 0x100000) { R_AX = 0x0; R_BL = XMS_GENERAL_ERROR; debug(D_XMS, " Offset to EMM structure wrong\n"); break; } /* Validate handles and offsets */ if (eptr->src_handle > NUM_HANDLES) { R_AX = 0x0; R_BL = XMS_INVALID_SOURCE_HANDLE; debug(D_XMS, " Invalid handle\n"); break; } if (eptr->dst_handle > NUM_HANDLES) { R_AX = 0x0; R_BL = XMS_INVALID_DESTINATION_HANDLE; debug(D_XMS, " Invalid handle\n"); break; } srcidx = eptr->src_handle - 1; dstidx = eptr->dst_handle - 1; srcoffs = eptr->src_offset; dstoffs = eptr->dst_offset; n = eptr->nbytes; /* Length must be even, see XMS spec */ if (n & 1) { R_AX = 0x0; R_BL = XMS_INVALID_LENGTH; debug(D_XMS, " Length not even\n"); break; } if (eptr->src_handle != 0) { srcptr = xms_hand[srcidx].addr; if (srcptr == 0 || srcptr == XMS_NULL_ALLOC) { R_AX = 0x0; R_BL = XMS_INVALID_SOURCE_HANDLE; debug(D_XMS, " Invalid source handle\n"); break; } if ((srcoffs + n) > xms_hand[srcidx].size) { R_AX = 0x0; R_BL = XMS_INVALID_SOURCE_OFFSET; debug(D_XMS, " Invalid source offset\n"); break; } srcptr += srcoffs; } else { srcptr = VECPTR(srcoffs); /* Sanity check: Don't allow srcptr pointing to * emulator data above 1M */ if ((srcptr + n) >= 0x100000) { R_AX = 0x0; R_BL = XMS_GENERAL_ERROR; debug(D_XMS, " Source segment invalid\n"); break; } } if (eptr->dst_handle != 0) { dstptr = xms_hand[dstidx].addr; if (dstptr == NULL || dstptr == XMS_NULL_ALLOC) { R_AX = 0x0; R_BL = XMS_INVALID_DESTINATION_HANDLE; debug(D_XMS, " Invalid dest handle\n"); break; } if ((dstoffs + n) > xms_hand[dstidx].size) { R_AX = 0x0; R_BL = XMS_INVALID_DESTINATION_OFFSET; debug(D_XMS, " Invalid dest offset\n"); break; } dstptr += dstoffs; } else { dstptr = VECPTR(dstoffs); /* Sanity check: Don't allow dstptr pointing to * emulator data above 1M */ if ((dstptr + n) >= 0x100000) { R_AX = 0x0; R_BL = XMS_GENERAL_ERROR; debug(D_XMS, " Dest segment invalid\n"); break; } } memmove((void *)dstptr, (void *)srcptr, n); debug(D_XMS, "Moved from %08lx to %08lx, %04x bytes\n", srcptr, dstptr, n); R_AX = 0x1; R_BL = XMS_SUCCESS; break; } case XMS_LOCK_EXTENDED_MEMORY_BLOCK: { int hnum,hindx; debug(D_XMS, "XMS: Lock EMM block\n"); hnum = R_DX; if (hnum > NUM_HANDLES || hnum == 0) { R_AX = 0x0; R_BL = XMS_INVALID_HANDLE; break; } hindx = hnum - 1; if (xms_hand[hindx].addr == 0) { R_AX = 0x0; R_BL = XMS_INVALID_HANDLE; break; } if (xms_hand[hindx].num_locks == 255) { R_AX = 0x0; R_BL = XMS_BLOCK_LOCKCOUNT_OVERFLOW; break; } xms_hand[hindx].num_locks++; R_AX = 0x1; /* * The 32 bit "physical" address is returned here. I hope * the solution to simply return the linear address of the * malloced area is good enough. Most DOS programs won't * need this anyway. It could be important for future DPMI. */ R_BX = xms_hand[hindx].addr & 0xffff; R_DX = (xms_hand[hindx].addr & 0xffff0000) >> 16; break; } case XMS_UNLOCK_EXTENDED_MEMORY_BLOCK: { int hnum,hindx; debug(D_XMS, "XMS: Unlock EMM block\n"); hnum = R_DX; if (hnum > NUM_HANDLES || hnum == 0) { R_AX = 0x0; R_BL = XMS_INVALID_HANDLE; break; } hindx = hnum - 1; if (xms_hand[hindx].addr == 0) { R_AX = 0x0; R_BL = XMS_INVALID_HANDLE; break; } if (xms_hand[hindx].num_locks == 0) { R_AX = 0x0; R_BL = XMS_BLOCK_NOT_LOCKED; break; } xms_hand[hindx].num_locks--; R_AX = 0x1; R_BL = XMS_SUCCESS; break; } case XMS_GET_EMB_HANDLE_INFORMATION: { int hnum,hindx; debug(D_XMS, "XMS: Get handle information: DX=%04x\n", R_DX); hnum = R_DX; if (hnum > NUM_HANDLES || hnum == 0) { R_AX = 0x0; R_BL = XMS_INVALID_HANDLE; break; } hindx = hnum - 1; if (xms_hand[hindx].addr == 0) { R_AX = 0x0; R_BL = XMS_INVALID_HANDLE; break; } R_AX = 0x1; R_BH = xms_hand[hindx].num_locks; R_BL = num_free_handle; R_DX = xms_hand[hindx].size / 1024; break; } case XMS_RESIZE_EXTENDED_MEMORY_BLOCK: { int hnum,hindx; size_t req_siz; long sizediff; void *mem; debug(D_XMS, "XMS: Resize EMM block\n"); hnum = R_DX; req_siz = R_BX * 1024; if (hnum > NUM_HANDLES || hnum == 0) { R_AX = 0x0; R_BL = XMS_INVALID_HANDLE; break; } hindx = hnum - 1; if (xms_hand[hindx].addr == 0) { R_AX = 0x0; R_BL = XMS_INVALID_HANDLE; break; } if (xms_hand[hindx].num_locks > 0) { R_AX = 0x0; R_BL = XMS_BLOCK_IS_LOCKED; break; } sizediff = req_siz - xms_hand[hindx].size; if (sizediff > 0) { if ((sizediff + xms_used_mem) > xms_maxsize) { R_AX = 0x0; R_BL = XMS_FULL; break; } } if (sizediff == 0) { /* Never trust DOS programs */ R_AX = 0x1; R_BL = XMS_SUCCESS; break; } xms_used_mem += sizediff; xms_free_mem -= sizediff; if (xms_hand[hindx].addr == XMS_NULL_ALLOC) { if ((mem = malloc(req_siz)) == NULL) fatal("XMS: Cannot malloc !"); xms_hand[hindx].addr = (u_long)mem; xms_hand[hindx].size = req_siz; } else { if ((mem = realloc((void *)xms_hand[hindx].addr,req_siz)) == NULL) fatal("XMS: Cannot realloc !"); xms_hand[hindx].addr = (u_long)mem; xms_hand[hindx].size = req_siz; } R_AX = 0x1; R_BL = XMS_SUCCESS; break; } case XMS_ALLOCATE_UMB: { u_long req_siz; UMB_block *bp; debug(D_XMS, "XMS: Allocate UMB: DX=%04x\n", R_DX); req_siz = R_DX * 16; /* Some programs try to allocate 0 bytes. XMS spec says * nothing about this. So the driver grants the request * but it rounds up to the next paragraph size (1) and * returns this amount of memory */ if (req_siz == 0) req_siz = 0x10; /* First try to find an exact fit */ if ((bp = find_exact_block(req_siz)) != NULL) { /* Found ! Move block from free list to alloc list */ remove_block(&UMB_freelist, bp); add_block(&UMB_alloclist, bp); R_AX = 0x1; R_DX = req_siz >> 4; R_BX = bp->addr >> 4; break; } /* Try to find a block big enough */ bp = find_block(req_siz); if (bp == NULL) { R_AX = 0x0; R_BL = XMS_NO_UMBS_AVAILABLE; R_DX = 0x0; } else if (bp->size < req_siz) { R_AX = 0x0; R_BL = XMS_REQUESTED_UMB_TOO_BIG; R_DX = bp->size / 16; } else { UMB_block *newbp; /* Found a block large enough. Split it into the size * we need, rest remains on the free list. New block * goes to the alloc list */ newbp = create_block(bp->addr, req_siz); bp->addr += req_siz; bp->size -= req_siz; add_block(&UMB_alloclist, newbp); R_AX = 0x1; R_BX = newbp->addr >> 4; R_DX = req_siz / 16; } break; } case XMS_DEALLOCATE_UMB: { u_long req_addr; UMB_block *blk; debug(D_XMS, "XMS: Deallocate UMB: DX=%04x\n", R_DX); req_addr = R_DX << 4; if ((blk = find_allocated_block(req_addr)) == NULL) { R_AX = 0x0; R_BL = XMS_INVALID_UMB_SEGMENT; } else { /* Move the block from the alloc list to the free list * and try to do garbage collection */ remove_block(&UMB_alloclist, blk); add_block(&UMB_freelist, blk); merge_blocks(); R_AX = 0x1; R_BL = XMS_SUCCESS; } break; } /* * If the option DOS=UMB is enabled, DOS grabs the entire UMB * at boot time. In any other case this is used to load resident * utilities. I don't think this function is neccesary here. */ case XMS_REALLOCATE_UMB: debug(D_XMS, "XMS: Reallocate UMB\n"); R_AX = 0x0; R_BL = XMS_NOT_IMPLEMENTED; break; /* Some test programs use this call */ case XMS_QUERY_FREE_EXTENDED_MEMORY_LARGE: /* DOS MEM.EXE chokes, if the HMA is enabled and the reported * free space includes the HMA. So we subtract 64kB from the * space reported, if the HMA is enabled. */ if (HMA_a20 < 0) R_EAX = R_EDX = xms_free_mem / 1024; else R_EAX = R_EDX = (xms_free_mem / 1024) - 64; /* ECX should return the highest address of any memory block * We return 1MB + size of extended memory */ R_ECX = 1024 * 1024 + xms_maxsize -1; if (xms_free_mem == 0) R_BL = XMS_FULL; else R_BL = XMS_SUCCESS; debug(D_XMS, "XMS: Query free EMM(large): Returned %dkB\n", R_AX); break; /* These are the same as the above functions, but they use 32 bit * registers (i.e. EDX instead of DX). This is for allocations of * more than 64MB. I think this will hardly be used in the emulator * It seems to work without them, but the functions are in the XMS 3.0 * spec. If something breaks because they are not here, I can implement * them */ case XMS_ALLOCATE_EXTENDED_MEMORY_LARGE: case XMS_FREE_EXTENDED_MEMORY_LARGE: debug(D_XMS, "XMS: %02x function called, not implemented\n", R_AH); R_AX = 0x0; R_BL = XMS_NOT_IMPLEMENTED; break; default: debug(D_ALWAYS, "XMS: Unimplemented function %02x, \n", R_AH); R_AX = 0; R_BL = XMS_NOT_IMPLEMENTED; break; } }