From 7ff83a71810233276517bc497e93ac15267bc830 Mon Sep 17 00:00:00 2001 From: arpi Date: Sun, 9 Feb 2003 20:18:23 +0000 Subject: linux->osdep git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@9381 b3059339-0415-0410-9bf9-f77b7e298cf2 --- osdep/.cvsignore | 1 + osdep/Makefile | 46 +++ osdep/getch2.c | 256 ++++++++++++++++ osdep/getch2.h | 20 ++ osdep/keycodes.h | 36 +++ osdep/lrmi.c | 912 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ osdep/lrmi.h | 86 ++++++ osdep/scandir.c | 134 ++++++++ osdep/shmem.c | 107 +++++++ osdep/shmem.h | 4 + osdep/strsep.c | 42 +++ osdep/timer-lx.c | 67 ++++ osdep/timer.h | 17 ++ osdep/vbelib.c | 730 ++++++++++++++++++++++++++++++++++++++++++++ osdep/vbelib.h | 227 ++++++++++++++ osdep/vsscanf.c | 20 ++ 16 files changed, 2705 insertions(+) create mode 100644 osdep/.cvsignore create mode 100644 osdep/Makefile create mode 100644 osdep/getch2.c create mode 100644 osdep/getch2.h create mode 100644 osdep/keycodes.h create mode 100644 osdep/lrmi.c create mode 100644 osdep/lrmi.h create mode 100644 osdep/scandir.c create mode 100644 osdep/shmem.c create mode 100644 osdep/shmem.h create mode 100644 osdep/strsep.c create mode 100644 osdep/timer-lx.c create mode 100644 osdep/timer.h create mode 100644 osdep/vbelib.c create mode 100644 osdep/vbelib.h create mode 100644 osdep/vsscanf.c (limited to 'osdep') diff --git a/osdep/.cvsignore b/osdep/.cvsignore new file mode 100644 index 0000000000..4671378aef --- /dev/null +++ b/osdep/.cvsignore @@ -0,0 +1 @@ +.depend diff --git a/osdep/Makefile b/osdep/Makefile new file mode 100644 index 0000000000..f42b913d07 --- /dev/null +++ b/osdep/Makefile @@ -0,0 +1,46 @@ + +include ../config.mak + +LIBNAME = libosdep.a + +SRCS=getch2.c timer-lx.c shmem.c strsep.c vsscanf.c scandir.c # timer.c +OBJS=$(SRCS:.c=.o) + +ifeq ($(TARGET_ARCH_X86),yes) +ifeq ($(TARGET_OS),Linux) +SRCS += lrmi.c vbelib.c +endif +endif + +CFLAGS = $(OPTFLAGS) -I. -I.. $(EXTRA_INC) +# -I/usr/X11R6/include/ + +.SUFFIXES: .c .o + +# .PHONY: all clean + +.c.o: + $(CC) -c $(CFLAGS) -o $@ $< + +$(LIBNAME): $(OBJS) + $(AR) r $(LIBNAME) $(OBJS) + +all: $(LIBNAME) + +clean: + rm -f *.o *.a *~ + +distclean: + rm -f Makefile.bak *.o *.a *~ .depend + +dep: depend + +depend: + $(CC) -MM $(CFLAGS) $(SRCS) 1>.depend + +# +# include dependency files if they exist +# +ifneq ($(wildcard .depend),) +include .depend +endif diff --git a/osdep/getch2.c b/osdep/getch2.c new file mode 100644 index 0000000000..dec9b91a0f --- /dev/null +++ b/osdep/getch2.c @@ -0,0 +1,256 @@ +/* GyS-TermIO v2.0 (for GySmail v3) (C) 1999 A'rpi/ESP-team */ + +#include "../config.h" + +//#define USE_TERMCAP +#ifndef __OS2__ +#define USE_IOCTL +#endif + +#define MAX_KEYS 64 +#define BUF_LEN 256 + +#include +#include +#include +#include +#include +#ifdef USE_IOCTL +#include +#endif + +#ifdef HAVE_TERMIOS +#ifdef HAVE_TERMIOS_H +#include +#endif +#ifdef HAVE_SYS_TERMIOS_H +#include +#endif +#endif + +#include + +#include "keycodes.h" + +#ifdef HAVE_TERMIOS +static struct termios tio_orig; +#endif +static int getch2_len=0; +static char getch2_buf[BUF_LEN]; + +int screen_width=80; +int screen_height=24; + +typedef struct { + int len; + int code; + char chars[8]; +} keycode_st; +static keycode_st getch2_keys[MAX_KEYS]; +static int getch2_key_db=0; + +#ifdef USE_TERMCAP + +#if 0 +#include +#else + extern int tgetent (char *BUFFER, char *TERMTYPE); + extern int tgetnum (char *NAME); + extern int tgetflag (char *NAME); + extern char *tgetstr (char *NAME, char **AREA); +#endif + +static char term_buffer[4096]; +static char term_buffer2[4096]; +static char *term_p=term_buffer2; + +static void termcap_add(char *id,int code){ +char *p=tgetstr(id,&term_p); + if(!p) return; + if(getch2_key_db>=MAX_KEYS) return; + getch2_keys[getch2_key_db].len=strlen(p); + strncpy(getch2_keys[getch2_key_db].chars,p,8); + getch2_keys[getch2_key_db].code=code; + ++getch2_key_db; +/* printf("%s=%s\n",id,p); */ +} + +static int success=0; + +int load_termcap(char *termtype){ + if(!termtype) termtype=getenv("TERM"); + if(!termtype) termtype="unknown"; + success=tgetent(term_buffer, termtype); + if(success<0){ printf("Could not access the 'termcap' data base.\n"); return 0; } + if(success==0){ printf("Terminal type `%s' is not defined.\n", termtype);return 0;} + + screen_width=tgetnum("co"); + screen_height=tgetnum("li"); + if(screen_width<1 || screen_width>255) screen_width=80; + if(screen_height<1 || screen_height>255) screen_height=24; + + termcap_add("kP",KEY_PGUP); + termcap_add("kN",KEY_PGDWN); + termcap_add("kh",KEY_HOME); + termcap_add("kH",KEY_END); + termcap_add("kI",KEY_INS); + termcap_add("kD",KEY_DEL); + termcap_add("kb",KEY_BS); + termcap_add("kl",KEY_LEFT); + termcap_add("kd",KEY_DOWN); + termcap_add("ku",KEY_UP); + termcap_add("kr",KEY_RIGHT); + termcap_add("k0",KEY_F+0); + termcap_add("k1",KEY_F+1); + termcap_add("k2",KEY_F+2); + termcap_add("k3",KEY_F+3); + termcap_add("k4",KEY_F+4); + termcap_add("k5",KEY_F+5); + termcap_add("k6",KEY_F+6); + termcap_add("k7",KEY_F+7); + termcap_add("k8",KEY_F+8); + termcap_add("k9",KEY_F+9); + termcap_add("k;",KEY_F+10); + return getch2_key_db; +} + +#endif + +void get_screen_size(){ +#ifdef USE_IOCTL + struct winsize ws; + if (ioctl(0, TIOCGWINSZ, &ws) < 0 || !ws.ws_row || !ws.ws_col) return; +/* printf("Using IOCTL\n"); */ + screen_width=ws.ws_col; + screen_height=ws.ws_row; +#endif +} + +int getch2(int time){ + int len=0; + int code=0; + int i=0; + + while(!getch2_len || (getch2_len==1 && getch2_buf[0]==27)){ + fd_set rfds; + struct timeval tv; + int retval; + /* Watch stdin (fd 0) to see when it has input. */ + FD_ZERO(&rfds); FD_SET(0,&rfds); + /* Wait up to 'time' microseconds. */ + tv.tv_sec=time/1000; tv.tv_usec = (time%1000)*1000; + retval=select(1, &rfds, NULL, NULL, &tv); + if(retval<=0) return -1; + /* Data is available now. */ + retval=read(0,&getch2_buf[getch2_len],BUF_LEN-getch2_len); + if(retval<1) return -1; + getch2_len+=retval; + } + + /* First find in the TERMCAP database: */ + for(i=0;i1){ + int c=getch2_buf[1]; + if(c==10 || c==13) if(c!=code) len=2; + } + code=KEY_ENTER; + goto found; + } + } else if(getch2_len>1){ + int c=getch2_buf[1]; + if(c==27){ code=KEY_ESC; len=2; goto found;} + if(c>='0' && c<='9'){ code=c-'0'+KEY_F; len=2; goto found;} + if(getch2_len>=4 && c=='[' && getch2_buf[2]=='['){ + int c=getch2_buf[3]; + if(c>='A' && c<'A'+12){ code=KEY_F+1+c-'A';len=4;goto found;} + } + if(c=='[' || c=='O') if(getch2_len>=3){ + int c=getch2_buf[2]; + static short int ctable[]={ KEY_UP,KEY_DOWN,KEY_RIGHT,KEY_LEFT,0, + KEY_END,KEY_PGDWN,KEY_HOME,KEY_PGUP,0,0,KEY_INS,0,0,0, + KEY_F+1,KEY_F+2,KEY_F+3,KEY_F+4}; + if(c>='A' && c<='S') + if(ctable[c-'A']){ code=ctable[c-'A']; len=3; goto found;} + } + if(getch2_len>=4 && c=='[' && getch2_buf[3]=='~'){ + int c=getch2_buf[2]; + int ctable[8]={KEY_HOME,KEY_INS,KEY_DEL,KEY_END,KEY_PGUP,KEY_PGDWN,KEY_HOME,KEY_END}; + if(c>='1' && c<='8'){ code=ctable[c-'1']; len=4; goto found;} + } + if(getch2_len>=5 && c=='[' && getch2_buf[4]=='~'){ + int i=getch2_buf[2]-'0'; + int j=getch2_buf[3]-'0'; + if(i>=0 && i<=9 && j>=0 && j<=9){ + static short int ftable[20]={ + 11,12,13,14,15, 17,18,19,20,21, + 23,24,25,26,28, 29,31,32,33,34 }; + int a=i*10+j; + for(i=0;i<20;i++) if(ftable[i]==a){ code=KEY_F+1+i;len=5;goto found;} + } + } + } +found: + if((getch2_len-=len)>0){ + int i; + for(i=0;i +#include +#include +#include + +#ifdef USE_LIBC_VM86 +#include +#endif + +#include +#include +#include +#include +#include + +#include "lrmi.h" + +#define REAL_MEM_BASE ((void *)0x10000) +#define REAL_MEM_SIZE 0x10000 +#define REAL_MEM_BLOCKS 0x100 + +struct mem_block + { + unsigned int size : 20; + unsigned int free : 1; + }; + +static struct + { + int ready; + int count; + struct mem_block blocks[REAL_MEM_BLOCKS]; + } mem_info = { 0 }; + +static int +real_mem_init(void) + { + void *m; + int fd_zero; + + if (mem_info.ready) + return 1; + + fd_zero = open("/dev/zero", O_RDONLY); + if (fd_zero == -1) + { + perror("open /dev/zero"); + return 0; + } + + m = mmap((void *)REAL_MEM_BASE, REAL_MEM_SIZE, + PROT_READ | PROT_WRITE | PROT_EXEC, + MAP_FIXED | MAP_PRIVATE, fd_zero, 0); + + if (m == (void *)-1) + { + perror("mmap /dev/zero"); + close(fd_zero); + return 0; + } + + mem_info.ready = 1; + mem_info.count = 1; + mem_info.blocks[0].size = REAL_MEM_SIZE; + mem_info.blocks[0].free = 1; + + return 1; + } + + +static void +insert_block(int i) + { + memmove( + mem_info.blocks + i + 1, + mem_info.blocks + i, + (mem_info.count - i) * sizeof(struct mem_block)); + + mem_info.count++; + } + +static void +delete_block(int i) + { + mem_info.count--; + + memmove( + mem_info.blocks + i, + mem_info.blocks + i + 1, + (mem_info.count - i) * sizeof(struct mem_block)); + } + +void * +LRMI_alloc_real(int size) + { + int i; + char *r = (char *)REAL_MEM_BASE; + + if (!mem_info.ready) + return NULL; + + if (mem_info.count == REAL_MEM_BLOCKS) + return NULL; + + size = (size + 15) & ~15; + + for (i = 0; i < mem_info.count; i++) + { + if (mem_info.blocks[i].free && size < mem_info.blocks[i].size) + { + insert_block(i); + + mem_info.blocks[i].size = size; + mem_info.blocks[i].free = 0; + mem_info.blocks[i + 1].size -= size; + + return (void *)r; + } + + r += mem_info.blocks[i].size; + } + + return NULL; + } + + +void +LRMI_free_real(void *m) + { + int i; + char *r = (char *)REAL_MEM_BASE; + + if (!mem_info.ready) + return; + + i = 0; + while (m != (void *)r) + { + r += mem_info.blocks[i].size; + i++; + if (i == mem_info.count) + return; + } + + mem_info.blocks[i].free = 1; + + if (i + 1 < mem_info.count && mem_info.blocks[i + 1].free) + { + mem_info.blocks[i].size += mem_info.blocks[i + 1].size; + delete_block(i + 1); + } + + if (i - 1 >= 0 && mem_info.blocks[i - 1].free) + { + mem_info.blocks[i - 1].size += mem_info.blocks[i].size; + delete_block(i); + } + } + + +#define DEFAULT_VM86_FLAGS (IF_MASK | IOPL_MASK) +#define DEFAULT_STACK_SIZE 0x1000 +#define RETURN_TO_32_INT 255 + +static struct + { + int ready; + unsigned short ret_seg, ret_off; + unsigned short stack_seg, stack_off; + struct vm86_struct vm; + } context = { 0 }; + + +static inline void +set_bit(unsigned int bit, void *array) + { + unsigned char *a = array; + + a[bit / 8] |= (1 << (bit % 8)); + } + + +static inline unsigned int +get_int_seg(int i) + { + return *(unsigned short *)(i * 4 + 2); + } + + +static inline unsigned int +get_int_off(int i) + { + return *(unsigned short *)(i * 4); + } + + +static inline void +pushw(unsigned short i) + { + struct vm86_regs *r = &context.vm.regs; + r->esp -= 2; + *(unsigned short *)(((unsigned int)r->ss << 4) + r->esp) = i; + } + + +int +LRMI_init(void) + { + void *m; + int fd_mem; + + if (context.ready) + return 1; + + if (!real_mem_init()) + return 0; + + /* + Map the Interrupt Vectors (0x0 - 0x400) + BIOS data (0x400 - 0x502) + and the ROM (0xa0000 - 0x100000) + */ + fd_mem = open("/dev/mem", O_RDWR); + + if (fd_mem == -1) + { + perror("open /dev/mem"); + return 0; + } + + m = mmap((void *)0, 0x502, + PROT_READ | PROT_WRITE | PROT_EXEC, + MAP_FIXED | MAP_PRIVATE, fd_mem, 0); + + if (m == (void *)-1) + { + perror("mmap /dev/mem"); + return 0; + } + + m = mmap((void *)0xa0000, 0x100000 - 0xa0000, + PROT_READ | PROT_WRITE, + MAP_FIXED | MAP_SHARED, fd_mem, 0xa0000); + + if (m == (void *)-1) + { + perror("mmap /dev/mem"); + return 0; + } + + + /* + Allocate a stack + */ + m = LRMI_alloc_real(DEFAULT_STACK_SIZE); + + context.stack_seg = (unsigned int)m >> 4; + context.stack_off = DEFAULT_STACK_SIZE; + + /* + Allocate the return to 32 bit routine + */ + m = LRMI_alloc_real(2); + + context.ret_seg = (unsigned int)m >> 4; + context.ret_off = (unsigned int)m & 0xf; + + ((unsigned char *)m)[0] = 0xcd; /* int opcode */ + ((unsigned char *)m)[1] = RETURN_TO_32_INT; + + memset(&context.vm, 0, sizeof(context.vm)); + + /* + Enable kernel emulation of all ints except RETURN_TO_32_INT + */ + memset(&context.vm.int_revectored, 0, sizeof(context.vm.int_revectored)); + set_bit(RETURN_TO_32_INT, &context.vm.int_revectored); + + context.ready = 1; + + return 1; + } + + +static void +set_regs(struct LRMI_regs *r) + { + context.vm.regs.edi = r->edi; + context.vm.regs.esi = r->esi; + context.vm.regs.ebp = r->ebp; + context.vm.regs.ebx = r->ebx; + context.vm.regs.edx = r->edx; + context.vm.regs.ecx = r->ecx; + context.vm.regs.eax = r->eax; + context.vm.regs.eflags = DEFAULT_VM86_FLAGS; + context.vm.regs.es = r->es; + context.vm.regs.ds = r->ds; + context.vm.regs.fs = r->fs; + context.vm.regs.gs = r->gs; + } + + +static void +get_regs(struct LRMI_regs *r) + { + r->edi = context.vm.regs.edi; + r->esi = context.vm.regs.esi; + r->ebp = context.vm.regs.ebp; + r->ebx = context.vm.regs.ebx; + r->edx = context.vm.regs.edx; + r->ecx = context.vm.regs.ecx; + r->eax = context.vm.regs.eax; + r->flags = context.vm.regs.eflags; + r->es = context.vm.regs.es; + r->ds = context.vm.regs.ds; + r->fs = context.vm.regs.fs; + r->gs = context.vm.regs.gs; + } + +#define DIRECTION_FLAG (1 << 10) + +static void +em_ins(int size) + { + unsigned int edx, edi; + + edx = context.vm.regs.edx & 0xffff; + edi = context.vm.regs.edi & 0xffff; + edi += (unsigned int)context.vm.regs.ds << 4; + + if (context.vm.regs.eflags & DIRECTION_FLAG) + { + if (size == 4) + asm volatile ("std; insl; cld" + : "=D" (edi) : "d" (edx), "0" (edi)); + else if (size == 2) + asm volatile ("std; insw; cld" + : "=D" (edi) : "d" (edx), "0" (edi)); + else + asm volatile ("std; insb; cld" + : "=D" (edi) : "d" (edx), "0" (edi)); + } + else + { + if (size == 4) + asm volatile ("cld; insl" + : "=D" (edi) : "d" (edx), "0" (edi)); + else if (size == 2) + asm volatile ("cld; insw" + : "=D" (edi) : "d" (edx), "0" (edi)); + else + asm volatile ("cld; insb" + : "=D" (edi) : "d" (edx), "0" (edi)); + } + + edi -= (unsigned int)context.vm.regs.ds << 4; + + context.vm.regs.edi &= 0xffff0000; + context.vm.regs.edi |= edi & 0xffff; + } + +static void +em_rep_ins(int size) + { + unsigned int ecx, edx, edi; + + ecx = context.vm.regs.ecx & 0xffff; + edx = context.vm.regs.edx & 0xffff; + edi = context.vm.regs.edi & 0xffff; + edi += (unsigned int)context.vm.regs.ds << 4; + + if (context.vm.regs.eflags & DIRECTION_FLAG) + { + if (size == 4) + asm volatile ("std; rep; insl; cld" + : "=D" (edi), "=c" (ecx) + : "d" (edx), "0" (edi), "1" (ecx)); + else if (size == 2) + asm volatile ("std; rep; insw; cld" + : "=D" (edi), "=c" (ecx) + : "d" (edx), "0" (edi), "1" (ecx)); + else + asm volatile ("std; rep; insb; cld" + : "=D" (edi), "=c" (ecx) + : "d" (edx), "0" (edi), "1" (ecx)); + } + else + { + if (size == 4) + asm volatile ("cld; rep; insl" + : "=D" (edi), "=c" (ecx) + : "d" (edx), "0" (edi), "1" (ecx)); + else if (size == 2) + asm volatile ("cld; rep; insw" + : "=D" (edi), "=c" (ecx) + : "d" (edx), "0" (edi), "1" (ecx)); + else + asm volatile ("cld; rep; insb" + : "=D" (edi), "=c" (ecx) + : "d" (edx), "0" (edi), "1" (ecx)); + } + + edi -= (unsigned int)context.vm.regs.ds << 4; + + context.vm.regs.edi &= 0xffff0000; + context.vm.regs.edi |= edi & 0xffff; + + context.vm.regs.ecx &= 0xffff0000; + context.vm.regs.ecx |= ecx & 0xffff; + } + +static void +em_outs(int size) + { + unsigned int edx, esi; + + edx = context.vm.regs.edx & 0xffff; + esi = context.vm.regs.esi & 0xffff; + esi += (unsigned int)context.vm.regs.ds << 4; + + if (context.vm.regs.eflags & DIRECTION_FLAG) + { + if (size == 4) + asm volatile ("std; outsl; cld" + : "=S" (esi) : "d" (edx), "0" (esi)); + else if (size == 2) + asm volatile ("std; outsw; cld" + : "=S" (esi) : "d" (edx), "0" (esi)); + else + asm volatile ("std; outsb; cld" + : "=S" (esi) : "d" (edx), "0" (esi)); + } + else + { + if (size == 4) + asm volatile ("cld; outsl" + : "=S" (esi) : "d" (edx), "0" (esi)); + else if (size == 2) + asm volatile ("cld; outsw" + : "=S" (esi) : "d" (edx), "0" (esi)); + else + asm volatile ("cld; outsb" + : "=S" (esi) : "d" (edx), "0" (esi)); + } + + esi -= (unsigned int)context.vm.regs.ds << 4; + + context.vm.regs.esi &= 0xffff0000; + context.vm.regs.esi |= esi & 0xffff; + } + +static void +em_rep_outs(int size) + { + unsigned int ecx, edx, esi; + + ecx = context.vm.regs.ecx & 0xffff; + edx = context.vm.regs.edx & 0xffff; + esi = context.vm.regs.esi & 0xffff; + esi += (unsigned int)context.vm.regs.ds << 4; + + if (context.vm.regs.eflags & DIRECTION_FLAG) + { + if (size == 4) + asm volatile ("std; rep; outsl; cld" + : "=S" (esi), "=c" (ecx) + : "d" (edx), "0" (esi), "1" (ecx)); + else if (size == 2) + asm volatile ("std; rep; outsw; cld" + : "=S" (esi), "=c" (ecx) + : "d" (edx), "0" (esi), "1" (ecx)); + else + asm volatile ("std; rep; outsb; cld" + : "=S" (esi), "=c" (ecx) + : "d" (edx), "0" (esi), "1" (ecx)); + } + else + { + if (size == 4) + asm volatile ("cld; rep; outsl" + : "=S" (esi), "=c" (ecx) + : "d" (edx), "0" (esi), "1" (ecx)); + else if (size == 2) + asm volatile ("cld; rep; outsw" + : "=S" (esi), "=c" (ecx) + : "d" (edx), "0" (esi), "1" (ecx)); + else + asm volatile ("cld; rep; outsb" + : "=S" (esi), "=c" (ecx) + : "d" (edx), "0" (esi), "1" (ecx)); + } + + esi -= (unsigned int)context.vm.regs.ds << 4; + + context.vm.regs.esi &= 0xffff0000; + context.vm.regs.esi |= esi & 0xffff; + + context.vm.regs.ecx &= 0xffff0000; + context.vm.regs.ecx |= ecx & 0xffff; + } + +static void +em_inbl(unsigned char literal) + { + context.vm.regs.eax = inb(literal) & 0xff; + } + +static void +em_inb(void) + { + asm volatile ("inb (%w1), %b0" + : "=a" (context.vm.regs.eax) + : "d" (context.vm.regs.edx), "0" (context.vm.regs.eax)); + } + +static void +em_inw(void) + { + asm volatile ("inw (%w1), %w0" + : "=a" (context.vm.regs.eax) + : "d" (context.vm.regs.edx), "0" (context.vm.regs.eax)); + } + +static void +em_inl(void) + { + asm volatile ("inl (%w1), %0" + : "=a" (context.vm.regs.eax) + : "d" (context.vm.regs.edx)); + } + +static void +em_outbl(unsigned char literal) + { + outb(context.vm.regs.eax & 0xff, literal); + } + +static void +em_outb(void) + { + asm volatile ("outb %b0, (%w1)" + : : "a" (context.vm.regs.eax), + "d" (context.vm.regs.edx)); + } + +static void +em_outw(void) + { + asm volatile ("outw %w0, (%w1)" + : : "a" (context.vm.regs.eax), + "d" (context.vm.regs.edx)); + } + +static void +em_outl(void) + { + asm volatile ("outl %0, (%w1)" + : : "a" (context.vm.regs.eax), + "d" (context.vm.regs.edx)); + } + +static int +emulate(void) + { + unsigned char *insn; + struct + { + unsigned int size : 1; + unsigned int rep : 1; + } prefix = { 0, 0 }; + int i = 0; + + insn = (unsigned char *)((unsigned int)context.vm.regs.cs << 4); + insn += context.vm.regs.eip; + + while (1) + { + if (insn[i] == 0x66) + { + prefix.size = 1 - prefix.size; + i++; + } + else if (insn[i] == 0xf3) + { + prefix.rep = 1; + i++; + } + else if (insn[i] == 0xf0 || insn[i] == 0xf2 + || insn[i] == 0x26 || insn[i] == 0x2e + || insn[i] == 0x36 || insn[i] == 0x3e + || insn[i] == 0x64 || insn[i] == 0x65 + || insn[i] == 0x67) + { + /* these prefixes are just ignored */ + i++; + } + else if (insn[i] == 0x6c) + { + if (prefix.rep) + em_rep_ins(1); + else + em_ins(1); + i++; + break; + } + else if (insn[i] == 0x6d) + { + if (prefix.rep) + { + if (prefix.size) + em_rep_ins(4); + else + em_rep_ins(2); + } + else + { + if (prefix.size) + em_ins(4); + else + em_ins(2); + } + i++; + break; + } + else if (insn[i] == 0x6e) + { + if (prefix.rep) + em_rep_outs(1); + else + em_outs(1); + i++; + break; + } + else if (insn[i] == 0x6f) + { + if (prefix.rep) + { + if (prefix.size) + em_rep_outs(4); + else + em_rep_outs(2); + } + else + { + if (prefix.size) + em_outs(4); + else + em_outs(2); + } + i++; + break; + } + else if (insn[i] == 0xe4) + { + em_inbl(insn[i + 1]); + i += 2; + break; + } + else if (insn[i] == 0xe6) + { + em_outbl(insn[i + 1]); + i += 2; + break; + } + else if (insn[i] == 0xec) + { + em_inb(); + i++; + break; + } + else if (insn[i] == 0xed) + { + if (prefix.size) + em_inl(); + else + em_inw(); + i++; + break; + } + else if (insn[i] == 0xee) + { + em_outb(); + i++; + break; + } + else if (insn[i] == 0xef) + { + if (prefix.size) + em_outl(); + else + em_outw(); + + i++; + break; + } + else + return 0; + } + + context.vm.regs.eip += i; + return 1; + } + + +/* + I don't know how to make sure I get the right vm86() from libc. + The one I want is syscall # 113 (vm86old() in libc 5, vm86() in glibc) + which should be declared as "int vm86(struct vm86_struct *);" in + . + + This just does syscall 113 with inline asm, which should work + for both libc's (I hope). +*/ +#if !defined(USE_LIBC_VM86) +static int +lrmi_vm86(struct vm86_struct *vm) + { + int r; +#ifdef __PIC__ + asm volatile ( + "pushl %%ebx\n\t" + "movl %2, %%ebx\n\t" + "int $0x80\n\t" + "popl %%ebx" + : "=a" (r) + : "0" (113), "r" (vm)); +#else + asm volatile ( + "int $0x80" + : "=a" (r) + : "0" (113), "b" (vm)); +#endif + return r; + } +#else +#define lrmi_vm86 vm86 +#endif + + +static void +debug_info(int vret) + { + int i; + unsigned char *p; + + fputs("vm86() failed\n", stderr); + fprintf(stderr, "return = 0x%x\n", vret); + fprintf(stderr, "eax = 0x%08lx\n", context.vm.regs.eax); + fprintf(stderr, "ebx = 0x%08lx\n", context.vm.regs.ebx); + fprintf(stderr, "ecx = 0x%08lx\n", context.vm.regs.ecx); + fprintf(stderr, "edx = 0x%08lx\n", context.vm.regs.edx); + fprintf(stderr, "esi = 0x%08lx\n", context.vm.regs.esi); + fprintf(stderr, "edi = 0x%08lx\n", context.vm.regs.edi); + fprintf(stderr, "ebp = 0x%08lx\n", context.vm.regs.ebp); + fprintf(stderr, "eip = 0x%08lx\n", context.vm.regs.eip); + fprintf(stderr, "cs = 0x%04x\n", context.vm.regs.cs); + fprintf(stderr, "esp = 0x%08lx\n", context.vm.regs.esp); + fprintf(stderr, "ss = 0x%04x\n", context.vm.regs.ss); + fprintf(stderr, "ds = 0x%04x\n", context.vm.regs.ds); + fprintf(stderr, "es = 0x%04x\n", context.vm.regs.es); + fprintf(stderr, "fs = 0x%04x\n", context.vm.regs.fs); + fprintf(stderr, "gs = 0x%04x\n", context.vm.regs.gs); + fprintf(stderr, "eflags = 0x%08lx\n", context.vm.regs.eflags); + + fputs("cs:ip = [ ", stderr); + + p = (unsigned char *)((context.vm.regs.cs << 4) + (context.vm.regs.eip & 0xffff)); + + for (i = 0; i < 16; ++i) + fprintf(stderr, "%02x ", (unsigned int)p[i]); + + fputs("]\n", stderr); + } + + +static int +run_vm86(void) + { + unsigned int vret; + + while (1) + { + vret = lrmi_vm86(&context.vm); + + if (VM86_TYPE(vret) == VM86_INTx) + { + unsigned int v = VM86_ARG(vret); + + if (v == RETURN_TO_32_INT) + return 1; + + pushw(context.vm.regs.eflags); + pushw(context.vm.regs.cs); + pushw(context.vm.regs.eip); + + context.vm.regs.cs = get_int_seg(v); + context.vm.regs.eip = get_int_off(v); + context.vm.regs.eflags &= ~(VIF_MASK | TF_MASK); + + continue; + } + + if (VM86_TYPE(vret) != VM86_UNKNOWN) + break; + + if (!emulate()) + break; + } + +#ifdef ORIGINAL_LRMI_CODE_THAT_GOT_IFDEFED_OUT + debug_info(vret); +#endif + return 0; + } + + +int +LRMI_call(struct LRMI_regs *r) + { + unsigned int vret; + + memset(&context.vm.regs, 0, sizeof(context.vm.regs)); + + set_regs(r); + + context.vm.regs.cs = r->cs; + context.vm.regs.eip = r->ip; + + if (r->ss == 0 && r->sp == 0) + { + context.vm.regs.ss = context.stack_seg; + context.vm.regs.esp = context.stack_off; + } + else + { + context.vm.regs.ss = r->ss; + context.vm.regs.esp = r->sp; + } + + pushw(context.ret_seg); + pushw(context.ret_off); + + vret = run_vm86(); + + get_regs(r); + + return vret; + } + + +int +LRMI_int(int i, struct LRMI_regs *r) + { + unsigned int vret; + unsigned int seg, off; + + seg = get_int_seg(i); + off = get_int_off(i); + + /* + If the interrupt is in regular memory, it's probably + still pointing at a dos TSR (which is now gone). + */ + if (seg < 0xa000 || (seg << 4) + off >= 0x100000) + { +#ifdef ORIGINAL_LRMI_CODE_THAT_GOT_IFDEFED_OUT + fprintf(stderr, "Int 0x%x is not in rom (%04x:%04x)\n", i, seg, off); +#endif + return 0; + } + + memset(&context.vm.regs, 0, sizeof(context.vm.regs)); + + set_regs(r); + + context.vm.regs.cs = seg; + context.vm.regs.eip = off; + + if (r->ss == 0 && r->sp == 0) + { + context.vm.regs.ss = context.stack_seg; + context.vm.regs.esp = context.stack_off; + } + else + { + context.vm.regs.ss = r->ss; + context.vm.regs.esp = r->sp; + } + + pushw(DEFAULT_VM86_FLAGS); + pushw(context.ret_seg); + pushw(context.ret_off); + + vret = run_vm86(); + + get_regs(r); + + return vret; + } + diff --git a/osdep/lrmi.h b/osdep/lrmi.h new file mode 100644 index 0000000000..8f4f88abdd --- /dev/null +++ b/osdep/lrmi.h @@ -0,0 +1,86 @@ +/* +Linux Real Mode Interface - A library of DPMI-like functions for Linux. + +Copyright (C) 1998 by Josh Vanderhoof + +You are free to distribute and modify this file, as long as you +do not remove this copyright notice and clearly label modified +versions as being modified. + +This software has NO WARRANTY. Use it at your own risk. +Original location: http://cvs.debian.org/lrmi/ +*/ + +#ifndef LRMI_H +#define LRMI_H + +struct LRMI_regs + { + unsigned int edi; + unsigned int esi; + unsigned int ebp; + unsigned int reserved; + unsigned int ebx; + unsigned int edx; + unsigned int ecx; + unsigned int eax; + unsigned short int flags; + unsigned short int es; + unsigned short int ds; + unsigned short int fs; + unsigned short int gs; + unsigned short int ip; + unsigned short int cs; + unsigned short int sp; + unsigned short int ss; + }; + + +#ifndef LRMI_PREFIX +#define LRMI_PREFIX LRMI_ +#endif + +#define LRMI_CONCAT2(a, b) a ## b +#define LRMI_CONCAT(a, b) LRMI_CONCAT2(a, b) +#define LRMI_MAKENAME(a) LRMI_CONCAT(LRMI_PREFIX, a) + +/* + Initialize + returns 1 if sucessful, 0 for failure +*/ +#define LRMI_init LRMI_MAKENAME(init) +int +LRMI_init(void); + +/* + Simulate a 16 bit far call + returns 1 if sucessful, 0 for failure +*/ +#define LRMI_call LRMI_MAKENAME(call) +int +LRMI_call(struct LRMI_regs *r); + +/* + Simulate a 16 bit interrupt + returns 1 if sucessful, 0 for failure +*/ +#define LRMI_int LRMI_MAKENAME(int) +int +LRMI_int(int interrupt, struct LRMI_regs *r); + +/* + Allocate real mode memory + The returned block is paragraph (16 byte) aligned +*/ +#define LRMI_alloc_real LRMI_MAKENAME(alloc_real) +void * +LRMI_alloc_real(int size); + +/* + Free real mode memory +*/ +#define LRMI_free_real LRMI_MAKENAME(free_real) +void +LRMI_free_real(void *m); + +#endif diff --git a/osdep/scandir.c b/osdep/scandir.c new file mode 100644 index 0000000000..868dd7cb57 --- /dev/null +++ b/osdep/scandir.c @@ -0,0 +1,134 @@ +/* + * scandir, alphasort - scan a directory + * + * implementation for systems that do not have it in libc + */ + +#include "../config.h" + +#ifndef HAVE_SCANDIR + +#include +#include +#include +#include +#include + +/* + * convenience helper function for scandir's |compar()| function: + * sort directory entries using strcoll(3) + */ +int +alphasort(const void *_a, const void *_b) +{ + struct dirent **a = (struct dirent **)_a; + struct dirent **b = (struct dirent **)_b; + return strcoll((*a)->d_name, (*b)->d_name); +} + + +#define strverscmp(a,b) strcoll(a,b) /* for now */ + +/* + * convenience helper function for scandir's |compar()| function: + * sort directory entries using GNU |strverscmp()| + */ +int +versionsort(const void *_a, const void *_b) +{ + struct dirent **a = (struct dirent **)_a; + struct dirent **b = (struct dirent **)_b; + return strverscmp((*a)->d_name, (*b)->d_name); +} + +/* + * The scandir() function reads the directory dirname and builds an + * array of pointers to directory entries using malloc(3). It returns + * the number of entries in the array. A pointer to the array of + * directory entries is stored in the location referenced by namelist. + * + * The select parameter is a pointer to a user supplied subroutine + * which is called by scandir() to select which entries are to be + * included in the array. The select routine is passed a pointer to + * a directory entry and should return a non-zero value if the + * directory entry is to be included in the array. If select is null, + * then all the directory entries will be included. + * + * The compar parameter is a pointer to a user supplied subroutine + * which is passed to qsort(3) to sort the completed array. If this + * pointer is null, the array is not sorted. + */ +int +scandir(const char *dirname, + struct dirent ***ret_namelist, + int (*select)(const struct dirent *), + int (*compar)(const struct dirent **, const struct dirent **)) +{ + int i, len; + int used, allocated; + DIR *dir; + struct dirent *ent, *ent2; + struct dirent **namelist = NULL; + + if ((dir = opendir(dirname)) == NULL) + return -1; + + used = 0; + allocated = 2; + namelist = malloc(allocated * sizeof(struct dirent *)); + if (!namelist) + goto error; + + while ((ent = readdir(dir)) != NULL) { + + if (select != NULL && !select(ent)) + continue; + + /* duplicate struct direct for this entry */ + len = offsetof(struct dirent, d_name) + strlen(ent->d_name) + 1; + if ((ent2 = malloc(len)) == NULL) + return -1; + + if (used >= allocated) { + allocated *= 2; + namelist = realloc(namelist, allocated * sizeof(struct dirent *)); + if (!namelist) + goto error; + } + memcpy(ent2, ent, len); + namelist[used++] = ent2; + } + closedir(dir); + + if (compar) + qsort(namelist, used, sizeof(struct dirent *), + (int (*)(const void *, const void *)) compar); + + *ret_namelist = namelist; + return used; + + +error: + if (namelist) { + for (i = 0; i < used; i++) + free(namelist[i]); + free(namelist); + } + return -1; +} +#endif + + +#if STANDALONE_MAIN +int +main(int argc, char **argv) +{ + struct dirent **namelist; + int i, n; + + n = scandir("/etc", &namelist, NULL, alphasort); + + for (i = 0; i < n; i++) + printf("%s\n", namelist[i]->d_name); +} +#endif diff --git a/osdep/shmem.c b/osdep/shmem.c new file mode 100644 index 0000000000..9fd6047b54 --- /dev/null +++ b/osdep/shmem.c @@ -0,0 +1,107 @@ +/* + * shmem.c - Shared memory allocation + * + * based on mpg123's xfermem.c by + * Oliver Fromme + * Sun Apr 6 02:26:26 MET DST 1997 + */ + +#include "../config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_SYS_MMAN_H +#include +#endif +#include +#include + +#include "../mp_msg.h" + +#ifdef AIX +#include +#endif + +#ifdef HAVE_SHM +#include +#include +#endif + +#if defined(MAP_ANONYMOUS) && !defined(MAP_ANON) +#define MAP_ANON MAP_ANONYMOUS +#endif + +static int shmem_type=0; + +void* shmem_alloc(int size){ +void* p; +static int devzero = -1; +while(1){ + switch(shmem_type){ + case 0: // ========= MAP_ANON|MAP_SHARED ========== +#ifdef MAP_ANON + p=mmap(0,size,PROT_READ|PROT_WRITE,MAP_ANON|MAP_SHARED,-1,0); + if(p==MAP_FAILED) break; // failed + mp_dbg(MSGT_OSDEP, MSGL_DBG2, "shmem: %d bytes allocated using mmap anon (%p)\n",size,p); + return p; +#else +// system does not support MAP_ANON at all (e.g. solaris 2.5.1/2.6), just fail + mp_dbg(MSGT_OSDEP, MSGL_DBG3, "shmem: using mmap anon failed\n"); +#endif + break; + case 1: // ========= MAP_SHARED + /dev/zero ========== + if (devzero == -1 && (devzero = open("/dev/zero", O_RDWR, 0)) == -1) break; + p=mmap(0,size,PROT_READ|PROT_WRITE,MAP_SHARED,devzero,0); + if(p==MAP_FAILED) break; // failed + mp_dbg(MSGT_OSDEP, MSGL_DBG2, "shmem: %d bytes allocated using mmap /dev/zero (%p)\n",size,p); + return p; + case 2: { // ========= shmget() ========== +#ifdef HAVE_SHM + struct shmid_ds shmemds; + int shmemid; + if ((shmemid = shmget(IPC_PRIVATE, size, IPC_CREAT | 0600)) == -1) break; + if ((p = shmat(shmemid, 0, 0)) == (void *)-1){ + mp_msg(MSGT_OSDEP, MSGL_ERR, "shmem: shmat() failed: %s\n", strerror(errno)); + shmctl (shmemid, IPC_RMID, &shmemds); + break; + } + if (shmctl(shmemid, IPC_RMID, &shmemds) == -1) { + mp_msg(MSGT_OSDEP, MSGL_ERR, "shmem: shmctl() failed: %s\n", strerror(errno)); + if (shmdt(p) == -1) perror ("shmdt()"); + break; + } + mp_dbg(MSGT_OSDEP, MSGL_DBG2, "shmem: %d bytes allocated using SHM (%p)\n",size,p); + return p; +#else + mp_msg(MSGT_OSDEP, MSGL_FATAL, "shmem: no SHM support was compiled in!\n"); + return(NULL); +#endif + } + default: + mp_msg(MSGT_OSDEP, MSGL_FATAL, + "FATAL: Cannot allocate %d bytes of shared memory :(\n",size); + return NULL; + } + ++shmem_type; +} +} + +void shmem_free(void* p){ + switch(shmem_type){ + case 2: +#ifdef HAVE_SHM + if (shmdt(p) == -1) + mp_msg(MSGT_OSDEP, MSGL_ERR, "shmfree: shmdt() failed: %s\n", + strerror(errno)); +#else + mp_msg(MSGT_OSDEP, MSGL_ERR, "shmfree: no SHM support was compiled in!\n"); +#endif + break; + } +} diff --git a/osdep/shmem.h b/osdep/shmem.h new file mode 100644 index 0000000000..58ceee4e44 --- /dev/null +++ b/osdep/shmem.h @@ -0,0 +1,4 @@ + +void* shmem_alloc(int size); +void shmem_free(void* p); + diff --git a/osdep/strsep.c b/osdep/strsep.c new file mode 100644 index 0000000000..863a39cc70 --- /dev/null +++ b/osdep/strsep.c @@ -0,0 +1,42 @@ +/* strsep implementation for systems that do not have it in libc */ + +#include +#include + +#include "../config.h" + +#ifndef HAVE_STRSEP +char *strsep(char **stringp, const char *delim) { + char *begin, *end; + + begin = *stringp; + if(begin == NULL) + return NULL; + + if(delim[0] == '\0' || delim[1] == '\0') { + char ch = delim[0]; + + if(ch == '\0') + end = NULL; + else { + if(*begin == ch) + end = begin; + else if(*begin == '\0') + end = NULL; + else + end = strchr(begin + 1, ch); + } + } + else + end = strpbrk(begin, delim); + + if(end) { + *end++ = '\0'; + *stringp = end; + } + else + *stringp = NULL; + + return begin; +} +#endif diff --git a/osdep/timer-lx.c b/osdep/timer-lx.c new file mode 100644 index 0000000000..4321e46bfa --- /dev/null +++ b/osdep/timer-lx.c @@ -0,0 +1,67 @@ +// Precise timer routines for LINUX (C) LGB & A'rpi/ASTRAL + +#include +#include +#include +#include +#include "../config.h" + +int usec_sleep(int usec_delay) +{ +#ifdef HAVE_NANOSLEEP + struct timespec ts; + ts.tv_sec = usec_delay / 1000000; + ts.tv_nsec = (usec_delay % 1000000) * 1000; + return nanosleep(&ts, NULL); +#else + return usleep(usec_delay); +#endif +} + + +// Returns current time in microseconds +unsigned int GetTimer(){ + struct timeval tv; + struct timezone tz; +// float s; + gettimeofday(&tv,&tz); +// s=tv.tv_usec;s*=0.000001;s+=tv.tv_sec; + return (tv.tv_sec*1000000+tv.tv_usec); +} + +// Returns current time in milliseconds +unsigned int GetTimerMS(){ + struct timeval tv; + struct timezone tz; +// float s; + gettimeofday(&tv,&tz); +// s=tv.tv_usec;s*=0.000001;s+=tv.tv_sec; + return (tv.tv_sec*1000+tv.tv_usec/1000); +} + +static unsigned int RelativeTime=0; + +// Returns time spent between now and last call in seconds +float GetRelativeTime(){ +unsigned int t,r; + t=GetTimer(); +// t*=16;printf("time=%ud\n",t); + r=t-RelativeTime; + RelativeTime=t; + return (float)r * 0.000001F; +} + +// Initialize timer, must be called at least once at start +void InitTimer(){ + GetRelativeTime(); +} + + +#if 0 +void main(){ + float t=0; + InitTimer(); + while(1){ t+=GetRelativeTime();printf("time= %10.6f\r",t);fflush(stdout); } +} +#endif + diff --git a/osdep/timer.h b/osdep/timer.h new file mode 100644 index 0000000000..efda23ad6c --- /dev/null +++ b/osdep/timer.h @@ -0,0 +1,17 @@ +#ifndef __TIMER_H +#define __TIMER_H + +void InitTimer(); +unsigned int GetTimer(); +unsigned int GetTimerMS(); +//int uGetTimer(); +float GetRelativeTime(); + +int usec_sleep(int usec_delay); + +/* timer's callback handling */ +typedef void timer_callback( void ); +extern unsigned set_timer_callback(unsigned ms,timer_callback func); +extern void restore_timer(void); + +#endif diff --git a/osdep/vbelib.c b/osdep/vbelib.c new file mode 100644 index 0000000000..d3fc9a445f --- /dev/null +++ b/osdep/vbelib.c @@ -0,0 +1,730 @@ +/* + This file contains implementation of VESA library which is based on + LRMI (Linux real-mode interface). + So it's not an emulator - it calls real int 10h handler under Linux. + Note: VESA is available only on x86 systems. + You can redistribute this file under terms and conditions + of GNU General Public licence v2. + Written by Nick Kurshev +*/ +#include "vbelib.h" +#include "lrmi.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct VesaProtModeInterface vbe_pm_info; +static struct VesaModeInfoBlock curr_mode_info; + +static inline int VERR(const void *p) +{ + register int retval; + __asm __volatile( + "xorl %0, %0\n\t" + "verr %1\n\t" + "setnz %b0" + :"=q"(retval) + :"m"(*(unsigned char *)p) + :"memory","cc"); + return retval; +} + +#if 0 +static inline int VERW(const void *p) +{ + register int retval; + __asm __volatile( + "xorl %0, %0\n\t" + "verw %1\n\t" + "setnz %b0" + :"=q"(retval) + :"m"(*(unsigned char *)p) + :"memory","cc"); + return retval; +} +#endif + +#define HAVE_VERBOSE_VAR 1 + +#ifdef HAVE_VERBOSE_VAR +extern int verbose; + +static void __dump_regs(struct LRMI_regs *r) +{ + printf("vbelib: eax=%08lXh ebx=%08lXh ecx=%08lXh edx=%08lXh\n" + "vbelib: edi=%08lXh esi=%08lXh ebp=%08lXh esp=%08lXh\n" + "vbelib: ds=%04Xh es=%04Xh ss=%04Xh cs:ip=%04X:%04X\n" + "vbelib: fs=%04Xh gs=%04Xh ss:sp=%04X:%04X flags=%04X\n" + ,(unsigned long)r->eax,(unsigned long)r->ebx,(unsigned long)r->ecx,(unsigned long)r->edx + ,(unsigned long)r->edi,(unsigned long)r->esi,(unsigned long)r->ebp,(unsigned long)r->reserved + ,r->ds,r->es,r->ss,r->cs,r->ip + ,r->fs,r->gs,r->ss,r->sp,r->flags); +} + +static inline int VBE_LRMI_int(int int_no, struct LRMI_regs *r) +{ + int retval; + if(verbose > 1) + { + printf("vbelib: registers before int %02X\n",int_no); + __dump_regs(r); + } + retval = LRMI_int(int_no,r); + if(verbose > 1) + { + printf("vbelib: Interrupt handler returns: %X\n",retval); + printf("vbelib: registers after int %02X\n",int_no); + __dump_regs(r); + } + return retval; +} +#else +#define VBE_LRMI_int(int_no,regs) (VBE_LRMI_int(int_no,regs)) +#endif + +static FILE *my_stdin; +static FILE *my_stdout; +static FILE *my_stderr; + +static void __set_cursor_type(FILE *stdout_fd,int cursor_on) +{ + fprintf(stdout_fd,"\033[?25%c",cursor_on?'h':'l'); +} + +/* TODO: do it only on LCD or DFP. We should extract such info from DDC */ +static void hide_terminal_output( void ) +{ + my_stdin = fopen(ttyname(fileno(stdin )),"r"); + my_stdout = fopen(ttyname(fileno(stdout)),"w"); + my_stderr = fopen(ttyname(fileno(stderr)),"w"); + __set_cursor_type(stdout,0); +/*if(isatty(fileno(stdin ))) stdin =freopen("/dev/null","r",stdin );*/ + if(isatty(fileno(stdout))) stdout=freopen("/dev/null","w",stdout); + if(isatty(fileno(stderr))) stderr=freopen("/dev/null","w",stderr); +} + +static unsigned hh_int_10_seg; +static int fd_mem; +/* +the list of supported video modes is stored in the reserved portion of +the SuperVGA information record by some implementations, and it may +thus be necessary to either copy the mode list or use a different +buffer for all subsequent VESA calls +*/ +static void *controller_info; +int vbeInit( void ) +{ + unsigned short iopl_port; + size_t i; + int retval; + if(!LRMI_init()) return VBE_VM86_FAIL; + if(!(controller_info = LRMI_alloc_real(sizeof(struct VbeInfoBlock)))) return VBE_OUT_OF_DOS_MEM; + /* + Allow read/write to ALL io ports + */ + hh_int_10_seg = *(unsigned short *)PhysToVirtSO(0x0000,0x0042); + /* Video BIOS should be at C000:0000 and above */ + hh_int_10_seg >>= 12; + if(hh_int_10_seg < 0xC) return VBE_BROKEN_BIOS; + ioperm(0, 1024, 1); + iopl(3); + memset(&vbe_pm_info,0,sizeof(struct VesaProtModeInterface)); + retval = vbeGetProtModeInfo(&vbe_pm_info); + if(retval != VBE_OK) return retval; + i = 0; + if(vbe_pm_info.iopl_ports) /* Can be NULL !!!*/ + while((iopl_port=vbe_pm_info.iopl_ports[i]) != 0xFFFF + && vbe_pm_info.iopl_ports[i++] > 1023) ioperm(iopl_port,1,1); + iopl(3); + fd_mem = open("/dev/mem",O_RDWR); + hide_terminal_output(); + return VBE_OK; +} + +int vbeDestroy( void ) +{ + __set_cursor_type(my_stdout,1); + close(fd_mem); + LRMI_free_real(controller_info); + return VBE_OK; +} + +/* Fixme!!! This code is compatible only with mplayer's version of lrmi*/ +static inline int is_addr_valid(const void *p) +{ + return (p < (const void *)0x502) || + (p >= (const void *)0x10000 && p < (const void *)0x20000) || + (p >= (const void *)0xa0000 && p < (const void *)0x100000); +} + +static int check_str(const unsigned char *str) +{ + size_t i; + int null_found = 0; + for(i = 0;i < 256;i++) + { + if(is_addr_valid(&str[i])) + { + if(VERR(&str[i])) + { + if(!str[i]) { null_found = 1; break; } + } + else break; + } + else break; + } + return null_found; +} + +static int check_wrd(const unsigned short *str) +{ + size_t i; + int ffff_found = 0; + for(i = 0;i < 1024;i++) + { + if(is_addr_valid(&str[i])) + { + if(VERR(&str[i])) + { + if(str[i] == 0xffff) { ffff_found = 1; break; } + } + else break; + } + else break; + } + return ffff_found; +} + +static void print_str(unsigned char *str) +{ + size_t i; + fflush(stdout); + printf("vbelib: "); + for(i = 0;i < 256;i++) { printf("%02X(%c) ",str[i],isprint(str[i])?str[i]:'.'); if(!str[i]) break; } + printf("\n"); + fflush(stdout); +} + +static void print_wrd(unsigned short *str) +{ + size_t i; + fflush(stdout); + printf("vbelib: "); + for(i = 0;i < 256;i++) { printf("%04X ",str[i]); if(str[i] == 0xffff) break; } + printf("\n"); + fflush(stdout); +} + +int vbeGetControllerInfo(struct VbeInfoBlock *data) +{ + struct LRMI_regs r; + int retval; + memcpy(controller_info,data,sizeof(struct VbeInfoBlock)); + memset(&r,0,sizeof(struct LRMI_regs)); + r.eax = 0x4f00; + r.es = VirtToPhysSeg(controller_info); + r.edi = VirtToPhysOff(controller_info); + if(!VBE_LRMI_int(0x10,&r)) return VBE_VM86_FAIL; + retval = r.eax & 0xffff; + if(retval == 0x4f) + { + FarPtr fpdata; + retval = VBE_OK; + memcpy(data,controller_info,sizeof(struct VbeInfoBlock)); + fpdata.seg = (unsigned long)(data->OemStringPtr) >> 16; + fpdata.off = (unsigned long)(data->OemStringPtr) & 0xffff; + data->OemStringPtr = PhysToVirt(fpdata); + if(!check_str(data->OemStringPtr)) data->OemStringPtr = NULL; +#ifdef HAVE_VERBOSE_VAR + if(verbose > 1) + { + printf("vbelib: OemStringPtr=%04X:%04X => %p\n",fpdata.seg,fpdata.off,data->OemStringPtr); + if(data->OemStringPtr) print_str(data->OemStringPtr); + fflush(stdout); + } +#endif + fpdata.seg = (unsigned long)(data->VideoModePtr) >> 16; + fpdata.off = (unsigned long)(data->VideoModePtr) & 0xffff; + data->VideoModePtr = PhysToVirt(fpdata); + if(!check_wrd(data->VideoModePtr)) + { + data->VideoModePtr = NULL; + retval = VBE_BROKEN_BIOS; + } +#ifdef HAVE_VERBOSE_VAR + if(verbose > 1) + { + printf("vbelib: VideoModePtr=%04X:%04X => %p\n",fpdata.seg,fpdata.off,data->VideoModePtr); + if(data->VideoModePtr) print_wrd(data->VideoModePtr); + fflush(stdout); + } +#endif + fpdata.seg = (unsigned long)(data->OemVendorNamePtr) >> 16; + fpdata.off = (unsigned long)(data->OemVendorNamePtr) & 0xffff; + data->OemVendorNamePtr = PhysToVirt(fpdata); + if(!check_str(data->OemVendorNamePtr)) data->OemVendorNamePtr = NULL; +#ifdef HAVE_VERBOSE_VAR + if(verbose > 1) + { + printf("vbelib: OemVendorNamePtr=%04X:%04X => %p\n",fpdata.seg,fpdata.off,data->OemVendorNamePtr); + if(data->OemVendorNamePtr) print_str(data->OemVendorNamePtr); + fflush(stdout); + } +#endif + fpdata.seg = (unsigned long)(data->OemProductNamePtr) >> 16; + fpdata.off = (unsigned long)(data->OemProductNamePtr) & 0xffff; + data->OemProductNamePtr = PhysToVirt(fpdata); + if(!check_str(data->OemProductNamePtr)) data->OemProductNamePtr = NULL; +#ifdef HAVE_VERBOSE_VAR + if(verbose > 1) + { + printf("vbelib: OemProductNamePtr=%04X:%04X => %p\n",fpdata.seg,fpdata.off,data->OemProductNamePtr); + if(data->OemVendorNamePtr) print_str(data->OemProductNamePtr); + fflush(stdout); + } +#endif + fpdata.seg = (unsigned long)(data->OemProductRevPtr) >> 16; + fpdata.off = (unsigned long)(data->OemProductRevPtr) & 0xffff; + data->OemProductRevPtr = PhysToVirt(fpdata); + if(!check_str(data->OemProductRevPtr)) data->OemProductRevPtr = NULL; +#ifdef HAVE_VERBOSE_VAR + if(verbose > 1) + { + printf("vbelib: OemProductRevPtr=%04X:%04X => %p\n",fpdata.seg,fpdata.off,data->OemProductRevPtr); + if(data->OemProductRevPtr) print_str(data->OemProductRevPtr); + fflush(stdout); + } +#endif + } + return retval; +} + +int vbeGetModeInfo(unsigned mode,struct VesaModeInfoBlock *data) +{ + struct LRMI_regs r; + void *rm_space; + int retval; + if(!(rm_space = LRMI_alloc_real(sizeof(struct VesaModeInfoBlock)))) return VBE_OUT_OF_DOS_MEM; + memset(&r,0,sizeof(struct LRMI_regs)); + r.eax = 0x4f01; + r.ecx = mode; + r.es = VirtToPhysSeg(rm_space); + r.edi = VirtToPhysOff(rm_space); + if(!VBE_LRMI_int(0x10,&r)) + { + LRMI_free_real(rm_space); + return VBE_VM86_FAIL; + } + retval = r.eax & 0xffff; + if(retval == 0x4f) + { + retval = VBE_OK; + memcpy(data,rm_space,sizeof(struct VesaModeInfoBlock)); + } + LRMI_free_real(rm_space); + return retval; +} + +int vbeSetMode(unsigned mode,struct VesaCRTCInfoBlock *data) +{ + struct LRMI_regs r; + void *rm_space = NULL; + int retval; + memset(&r,0,sizeof(struct LRMI_regs)); + if(data) + { + if(!(rm_space = LRMI_alloc_real(sizeof(struct VesaCRTCInfoBlock)))) return VBE_OUT_OF_DOS_MEM; + r.es = VirtToPhysSeg(rm_space); + r.edi = VirtToPhysOff(rm_space); + memcpy(rm_space,data,sizeof(struct VesaCRTCInfoBlock)); + } + r.eax = 0x4f02; + r.ebx = mode; + retval = VBE_LRMI_int(0x10,&r); + LRMI_free_real(rm_space); + if(!retval) return VBE_VM86_FAIL; + retval = r.eax & 0xffff; + if(retval == 0x4f) + { + /* Just info for internal use (currently in SetDiplayStart func). */ + vbeGetModeInfo(mode&0x1f,&curr_mode_info); + retval = VBE_OK; + } + return retval; +} + +int vbeGetMode(unsigned *mode) +{ + struct LRMI_regs r; + int retval; + memset(&r,0,sizeof(struct LRMI_regs)); + r.eax = 0x4f03; + if(!VBE_LRMI_int(0x10,&r)) return VBE_VM86_FAIL; + retval = r.eax & 0xffff; + if(retval == 0x4f) + { + *mode = r.ebx; + retval = VBE_OK; + } + return retval; +} + +int vbeGetPixelClock(unsigned *mode,unsigned *pixel_clock) // in Hz +{ + struct LRMI_regs r; + int retval; + memset(&r,0,sizeof(struct LRMI_regs)); + r.eax = 0x4f0b; + r.ebx = 0; + r.edx = *mode; + r.ecx = *pixel_clock; + if(!VBE_LRMI_int(0x10,&r)) return VBE_VM86_FAIL; + retval = r.eax & 0xffff; + if(retval == 0x4f) + { + *pixel_clock = r.ecx; + retval = VBE_OK; + } + return retval; +} + + +int vbeSaveState(void **data) +{ + struct LRMI_regs r; + int retval; + void *rm_space; + memset(&r,0,sizeof(struct LRMI_regs)); + r.eax = 0x4f04; + r.edx = 0x00; + r.ecx = 0x0f; + if(!VBE_LRMI_int(0x10,&r)) return VBE_VM86_FAIL; + retval = r.eax & 0xffff; + if(retval != 0x4f) return retval; + if(!(rm_space = LRMI_alloc_real((r.ebx & 0xffff)*64))) return VBE_OUT_OF_DOS_MEM; + r.eax = 0x4f04; + r.edx = 0x01; + r.ecx = 0x0f; + r.es = VirtToPhysSeg(rm_space); + r.ebx = VirtToPhysOff(rm_space); + if(!VBE_LRMI_int(0x10,&r)) + { + LRMI_free_real(rm_space); + return VBE_VM86_FAIL; + } + retval = r.eax & 0xffff; + if(retval != 0x4f) + { + LRMI_free_real(rm_space); + return retval; + } + *data = rm_space; + return VBE_OK; +} + +int vbeRestoreState(void *data) +{ + struct LRMI_regs r; + int retval; + memset(&r,0,sizeof(struct LRMI_regs)); + r.eax = 0x4f04; + r.edx = 0x02; + r.ecx = 0x0f; + r.es = VirtToPhysSeg(data); + r.ebx = VirtToPhysOff(data); + retval = VBE_LRMI_int(0x10,&r); + LRMI_free_real(data); + if(!retval) return VBE_VM86_FAIL; + retval = r.eax & 0xffff; + if(retval == 0x4f) retval = VBE_OK; + return retval; +} + +int vbeGetWindow(unsigned *win_num) +{ + struct LRMI_regs r; + int retval; + memset(&r,0,sizeof(struct LRMI_regs)); + r.eax = 0x4f05; + r.ebx = (*win_num & 0x0f) | 0x0100; + if(!VBE_LRMI_int(0x10,&r)) return VBE_VM86_FAIL; + retval = r.eax & 0xffff; + if(retval == 0x4f) + { + *win_num = r.edx & 0xffff; + retval = VBE_OK; + } + return retval; +} + +int vbeSetWindow(unsigned win_num,unsigned win_gran) +{ + int retval; + if(vbe_pm_info.SetWindowCall) + { + /* Don't verbose this stuff from performance reasons */ + /* 32-bit function call is much better of int 10h */ + __asm __volatile( + "pushl %%ebx\n" + "movl %1, %%ebx\n" + ::"a"(0x4f05),"S"(win_num & 0x0f),"d"(win_gran):"memory"); + (*vbe_pm_info.SetWindowCall)(); + __asm __volatile("popl %%ebx":::"memory"); + retval = VBE_OK; + } + else + { + struct LRMI_regs r; + memset(&r,0,sizeof(struct LRMI_regs)); + r.eax = 0x4f05; + r.ebx = win_num & 0x0f; + r.edx = win_gran; + if(!VBE_LRMI_int(0x10,&r)) return VBE_VM86_FAIL; + retval = r.eax & 0xffff; + if(retval == 0x4f) retval = VBE_OK; + } + return retval; +} + +int vbeGetScanLineLength(unsigned *num_pixels,unsigned *num_bytes) +{ + struct LRMI_regs r; + int retval; + memset(&r,0,sizeof(struct LRMI_regs)); + r.eax = 0x4f06; + r.ebx = 1; + if(!VBE_LRMI_int(0x10,&r)) return VBE_VM86_FAIL; + retval = r.eax & 0xffff; + if(retval == 0x4f) + { + if(num_bytes) *num_bytes = r.ebx & 0xffff; + if(num_pixels) *num_pixels= r.ecx & 0xffff; + retval = VBE_OK; + } + return retval; +} + +int vbeGetMaxScanLines(unsigned *num_pixels,unsigned *num_bytes, unsigned *num_lines) +{ + struct LRMI_regs r; + int retval; + memset(&r,0,sizeof(struct LRMI_regs)); + r.eax = 0x4f06; + r.ebx = 3; + if(!VBE_LRMI_int(0x10,&r)) return VBE_VM86_FAIL; + retval = r.eax & 0xffff; + if(retval == 0x4f) + { + if(num_bytes) *num_bytes = r.ebx & 0xffff; + if(num_pixels) *num_pixels= r.ecx & 0xffff; + if(num_lines) *num_lines = r.edx & 0xffff; + retval = VBE_OK; + } + return retval; +} + +int vbeSetScanLineLength(unsigned num_pixels) +{ + int retval; + struct LRMI_regs r; + memset(&r,0,sizeof(struct LRMI_regs)); + r.eax = 0x4f06; + r.ebx = 0; + r.ecx = num_pixels; + if(!VBE_LRMI_int(0x10,&r)) return VBE_VM86_FAIL; + retval = r.eax & 0xffff; + if(retval == 0x4f) retval = VBE_OK; + return retval; +} + +int vbeSetScanLineLengthB(unsigned num_bytes) +{ + int retval; + struct LRMI_regs r; + memset(&r,0,sizeof(struct LRMI_regs)); + r.eax = 0x4f06; + r.ebx = 2; + r.ecx = num_bytes; + if(!VBE_LRMI_int(0x10,&r)) return VBE_VM86_FAIL; + retval = r.eax & 0xffff; + if(retval == 0x4f) retval = VBE_OK; + return retval; +} + +int vbeGetDisplayStart(unsigned *pixel_num,unsigned *scan_line) +{ + struct LRMI_regs r; + int retval; + memset(&r,0,sizeof(struct LRMI_regs)); + r.eax = 0x4f07; + r.ebx = 1; + if(!VBE_LRMI_int(0x10,&r)) return VBE_VM86_FAIL; + retval = r.eax & 0xffff; + if(retval == 0x4f) + { + if(pixel_num) *pixel_num = r.ecx & 0xffff; + if(scan_line) *scan_line = r.edx & 0xffff; + retval = VBE_OK; + } + return retval; +} + +int vbeSetDisplayStart(unsigned long offset, int vsync) +{ + int retval; + if(vbe_pm_info.SetDisplayStart) + { + /* Don't verbose this stuff from performance reasons */ + /* 32-bit function call is much better of int 10h */ + __asm __volatile( + "pushl %%ebx\n" + "movl %1, %%ebx\n" + ::"a"(0x4f07),"S"(vsync ? 0x80 : 0), + "c"((offset>>2) & 0xffff),"d"((offset>>18)&0xffff):"memory"); + (*vbe_pm_info.SetDisplayStart)(); + __asm __volatile("popl %%ebx":::"memory"); + retval = VBE_OK; + } + else + { +#if 0 + /* Something wrong here */ + struct LRMI_regs r; + unsigned long pixel_num; + memset(&r,0,sizeof(struct LRMI_regs)); + pixel_num = offset%(unsigned long)curr_mode_info.BytesPerScanLine; + if(pixel_num*(unsigned long)curr_mode_info.BytesPerScanLine!=offset) pixel_num++; + r.eax = 0x4f07; + r.ebx = vsync ? 0x82 : 2; + r.ecx = pixel_num; + r.edx = offset/(unsigned long)curr_mode_info.BytesPerScanLine; + if(!VBE_LRMI_int(0x10,&r)) return VBE_VM86_FAIL; + retval = r.eax & 0xffff; + if(retval == 0x4f) retval = VBE_OK; +#endif + retval = VBE_BROKEN_BIOS; + } + return retval; +} + +int vbeSetScheduledDisplayStart(unsigned long offset, int vsync) +{ + int retval; + struct LRMI_regs r; + unsigned long pixel_num; + memset(&r,0,sizeof(struct LRMI_regs)); + pixel_num = offset%(unsigned long)curr_mode_info.BytesPerScanLine; + if(pixel_num*(unsigned long)curr_mode_info.BytesPerScanLine!=offset) pixel_num++; + r.eax = 0x4f07; + r.ebx = vsync ? 0x82 : 2; + r.ecx = offset; + if(!VBE_LRMI_int(0x10,&r)) return VBE_VM86_FAIL; + retval = r.eax & 0xffff; + if(retval == 0x4f) retval = VBE_OK; + return retval; +} + +struct realVesaProtModeInterface +{ + unsigned short SetWindowCall; + unsigned short SetDisplayStart; + unsigned short SetPaletteData; + unsigned short iopl_ports; +}__attribute__((packed)); + +int vbeGetProtModeInfo(struct VesaProtModeInterface *pm_info) +{ + struct LRMI_regs r; + int retval; + unsigned info_offset; + struct realVesaProtModeInterface *rm_info; + memset(&r,0,sizeof(struct LRMI_regs)); + r.eax = 0x4f0a; + r.ebx = 0; + if(!VBE_LRMI_int(0x10,&r)) return VBE_VM86_FAIL; + retval = r.eax & 0xffff; + if(retval == 0x4f) + { + retval = VBE_OK; + info_offset = r.edi&0xffff; + if((r.es >> 12) != hh_int_10_seg) retval = VBE_BROKEN_BIOS; + rm_info = PhysToVirtSO(r.es,info_offset); + pm_info->SetWindowCall = PhysToVirtSO(r.es,info_offset+rm_info->SetWindowCall); + if(!is_addr_valid(pm_info->SetWindowCall)) retval = VBE_BROKEN_BIOS; +#ifdef HAVE_VERBOSE_VAR + if(verbose > 1) printf("vbelib: SetWindowCall=%04X:%04X => %p\n",r.es,info_offset+rm_info->SetWindowCall,pm_info->SetWindowCall); +#endif + pm_info->SetDisplayStart = PhysToVirtSO(r.es,info_offset+rm_info->SetDisplayStart); + if(!is_addr_valid(pm_info->SetDisplayStart)) retval = VBE_BROKEN_BIOS; +#ifdef HAVE_VERBOSE_VAR + if(verbose > 1) printf("vbelib: SetDisplayStart=%04X:%04X => %p\n",r.es,info_offset+rm_info->SetDisplayStart,pm_info->SetDisplayStart); +#endif + pm_info->SetPaletteData = PhysToVirtSO(r.es,info_offset+rm_info->SetPaletteData); + if(!is_addr_valid(pm_info->SetPaletteData)) retval = VBE_BROKEN_BIOS; +#ifdef HAVE_VERBOSE_VAR + if(verbose > 1) printf("vbelib: SetPaletteData=%04X:%04X => %p\n",r.es,info_offset+rm_info->SetPaletteData,pm_info->SetPaletteData); +#endif + pm_info->iopl_ports = PhysToVirtSO(r.es,info_offset+rm_info->iopl_ports); + if(!rm_info->iopl_ports) pm_info->iopl_ports = NULL; + else + if(!check_wrd(pm_info->iopl_ports)) + { + pm_info->iopl_ports = NULL; +/* retval = VBE_BROKEN_BIOS; <- It's for broken BIOSes only */ + } +#ifdef HAVE_VERBOSE_VAR + if(verbose > 1) + { + printf("vbelib: iopl_ports=%04X:%04X => %p\n",r.es,info_offset+rm_info->iopl_ports,pm_info->iopl_ports); + if(pm_info->iopl_ports) print_wrd(pm_info->iopl_ports); + fflush(stdout); + } +#endif + } + return retval; +} +/* --------- Standard VGA stuff -------------- */ +int vbeWriteString(int x, int y, int attr, char *str) +{ + struct LRMI_regs r; + void *rm_space = NULL; + int retval; + memset(&r,0,sizeof(struct LRMI_regs)); + r.ecx = strlen(str); + r.edx = ((y<<8)&0xff00)|(x&0xff); + r.ebx = attr; + if(!(rm_space = LRMI_alloc_real(r.ecx))) return VBE_OUT_OF_DOS_MEM; + r.es = VirtToPhysSeg(rm_space); + r.ebp = VirtToPhysOff(rm_space); + memcpy(rm_space,str,r.ecx); + r.eax = 0x1300; + retval = VBE_LRMI_int(0x10,&r); + LRMI_free_real(rm_space); + if(!retval) return VBE_VM86_FAIL; + retval = r.eax & 0xffff; + if(retval == 0x4f) retval = VBE_OK; + return retval; +} + +void * vbeMapVideoBuffer(unsigned long phys_addr,unsigned long size) +{ + void *lfb; + if(fd_mem == -1) return NULL; + if(verbose > 1) printf("vbelib: vbeMapVideoBuffer(%08lX,%08lX)\n",phys_addr,size); + /* Here we don't need with MAP_FIXED and prefered address (first argument) */ + lfb = mmap((void *)0,size,PROT_READ | PROT_WRITE,MAP_SHARED,fd_mem,phys_addr); + return lfb == (void *)-1 ? 0 : lfb; +} + +void vbeUnmapVideoBuffer(unsigned long linear_addr,unsigned long size) +{ + if(verbose > 1) printf("vbelib: vbeUnmapVideoBuffer(%08lX,%08lX)\n",linear_addr,size); + munmap((void *)linear_addr,size); +} diff --git a/osdep/vbelib.h b/osdep/vbelib.h new file mode 100644 index 0000000000..c1ae08beb8 --- /dev/null +++ b/osdep/vbelib.h @@ -0,0 +1,227 @@ +/* + VESA VBE 2.0 compatible structures and definitions. + You can redistribute this file under terms and conditions + of GNU General Public licence v2. + Written by Nick Kurshev +*/ +#ifndef __VESA_VBELIB_INCLUDED__ +#define __VESA_VBELIB_INCLUDED__ 1 + +/* Note: every pointer within structures is 32-bit protected mode pointer. + So you don't need to convert it from real mode. */ + +typedef struct tagFarPtr +{ + unsigned short off; + unsigned short seg; +}FarPtr; + +#define VBE_DAC_8BIT (1 << 0) +#define VBE_NONVGA_CRTC (1 << 1) +#define VBE_SNOWED_RAMDAC (1 << 2) +#define VBE_STEREOSCOPIC (1 << 3) +#define VBE_STEREO_EVC (1 << 4) + +struct VbeInfoBlock { + char VESASignature[4]; /* 'VESA' 4 byte signature */ + short VESAVersion; /* VBE version number */ + char * OemStringPtr; /* Pointer to OEM string */ + long Capabilities; /* Capabilities of video card */ + unsigned short* VideoModePtr; /* Pointer to supported modes */ + short TotalMemory; /* Number of 64kb memory blocks */ + /* VBE 2.0 and above */ + short OemSoftwareRev; + char * OemVendorNamePtr; + char * OemProductNamePtr; + char * OemProductRevPtr; + char reserved[222]; + char OemData[256]; /* Pad to 512 byte block size */ +}__attribute__ ((packed)); + +static inline FarPtr VirtToPhys(void *ptr) +{ + FarPtr retval; + retval.seg = ((unsigned long)ptr) >> 4; + retval.off = ((unsigned long)ptr) & 0x0f; + return retval; +} + +static inline unsigned short VirtToPhysSeg(void *ptr) +{ + return ((unsigned long)ptr) >> 4; +} + +static inline unsigned short VirtToPhysOff(void *ptr) +{ + return ((unsigned long)ptr)