iMX6ULL应用移植 | 移植 infoNES 模拟器(重玩经典NES游戏)

没玩过NES游戏的童年,可能不是80后的童年。我们小时候是从玩FC开始接触游戏机的,那时真的是红极一时啊,我上初中时还省吃俭用买了一台小霸王,暑假里把电视机都给打爆了!那时任天堂单是FC机的主机的发售收入就超过全美的电视台的收入的总和,在人们的心目中扎下了任天堂的这个招牌。

前言

1983年7月15日,由日本任天堂株式会社(原本是生产日式扑克即“花札”)的宫本茂先生领导开发的一种第三代家用电子游戏机:FC,全称:Family Computer,也称作:Famicom;在欧美发售时则被称为nes,全称:Nintendo Entertainment System;在中国大陆、台湾和香港等地,因其外壳为红白两色,所以人们俗称其为“红白机”,正式进入市场销售,并于后来取得了巨大成功,由此揭开了家用电子游戏机遍布世界任何角落,电子游戏全球大普及的序幕。

什么是InfNES?

一款NES游戏模拟器。InfoNES可以很容易地被移植到各个平台,作者是Martin Freij。他是一位瑞典的程序员和游戏爱好者,于2002年开发了infoNES模拟器。infoNES是一个基于NES(任天堂娱乐系统)的模拟器,旨在让人们能够在计算机上玩经典的NES游戏。

InfoNES具备良好的可移植性,它将与环境有关的内容都清出了软件内核,并且单独集合于一个InfoNES_System.h中,我们要做的就是实现这里提到的各种函数,再把InfoNES加入到我们的工程中一起编译。

最近成功实现了USB接口的FC手柄驱动,使得在imx6ull开发板玩游戏具有可玩性,这里将这个移植过程记录下来。如果对NES模拟器的源码实现感兴趣,infoNES也是个不错的研究对象,代码结构清晰,可以让你了解到如何模拟实现k6502这款经典cpu的,加深对计算机体系结构的理解。

接下来让我们重温下经典,缅怀下童年吧!

池塘外的迷路书上,知鸟在声声叫着夏天......,伴随着优美的歌声,仿佛穿越回来了,少年。

完成以下操作,让你即刻拥有款移动游戏机,实现童年时的梦想。

很早之前我在imax283平台上移植过infoNES,那时我的github仓地址是:

https://github.com/yongzhena/infoNES

这次直接拉取下来用,只是修改下joypad手柄驱动的代码就可以完美运行啦。

移植过程

整个移植过程主要涉及三部分,显示、声音输出和usb手柄支持。前两个直接拉取上面的我的仓直接就具备了,这里着重介绍下USB手柄驱动支持。

基于fb0的LCD显示

在InfoNES_System_Linux.cpp文件中修改。显示这块儿实现两个函数,一个是lcd_fb_init,一个是lcd_fb_display_px。

static int lcd_fb_init()
{//如果使用 mmap 打开方式 必须是 读定方式fb_fd = open("/dev/fb0", O_RDWR);if(-1 == fb_fd){printf("cat't open /dev/fb0 \n");return -1;}//获取屏幕参数if(-1 == ioctl(fb_fd, FBIOGET_VSCREENINFO, &var)){close(fb_fd);printf("cat't ioctl /dev/fb0 \n");return -1;}//计算参数px_width     = var.bits_per_pixel /8;line_width   = var.xres * px_width;screen_width = var.yres * line_width;lcd_width    = var.xres;lcd_height   = var.yres;printf("fb width:%d height:%d pixel:%d \n", lcd_width, lcd_height,px_width*8);fb_mem = (unsigned char *)mmap(NULL, screen_width, PROT_READ | PROT_WRITE, MAP_SHARED, fb_fd, 0);if(fb_mem == (void *)-1){close(fb_fd);printf("cat't mmap /dev/fb0 \n");return -1;}//清屏memset(fb_mem, 0 , screen_width);return 0;
}
static int lcd_fb_display_px(WORD color, int x, int y)
{unsigned char  *pen8;unsigned short *pen16;pen8 = (unsigned char *)(fb_mem + y*line_width + x*px_width);pen16 = (unsigned short *)pen8;*pen16 = color;return 0;
}

以下的实现注意zoom_x_tab,zoom_y_tab这两项。它的作用是对像素做了全屏和放大处理。 源码里的make_zoom_tab()就是干这个用。如果觉得屏幕很大,放大后颗粒感很重,能否再优化?这里是个可能的优化方向。

/*===================================================================*/
/*                                                                   */
/*      InfoNES_LoadFrame() :                                        */
/*           Transfer the contents of work frame on the screen       */
/*                                                                   */
/*===================================================================*/
unsigned short ChColor(unsigned short color)
{return (color>>3)<<4|(color&0x001f);
}void InfoNES_LoadFrame()
{int x,y;int line_width;WORD wColor,R,G,B,Gr;//修正 if(0 < fb_fd){for (y = 0; y < lcd_height; y++ ){line_width = zoom_y_tab[y] * NES_DISP_WIDTH;for (x = 0; x < lcd_width; x++ ){wColor = ChColor(WorkFrame[line_width  + zoom_x_tab[x]]);lcd_fb_display_px(wColor, x, y);}}}/*16 bit per pixel*//* Exchange 16-bit to 256 gray *//*for (y = 0; y < NES_DISP_HEIGHT; y++ ){for (x = 0; x < NES_DISP_WIDTH; x++ ){//wColor = WorkFrame[y * lcd_width  + x ];wColor = WorkFrame[ ( y << 8 ) + x ];R = ( ( wColor & 0x7c00 ) >>7 );G = ( ( wColor & 0x03e0 ) >>2 );B = ( ( wColor & 0x001f ) <<3 );            //Gr= ( ( 9798*R + 19235*G + 3735*B)>>15);wColor=(WORD)((B<<16)|(G<<8)|R);lcd_fb_display_px(wColor, x, y);}}*/  
}

基于Alsa的声音支持

实现这个声音支持的前提是,板子上得有基于alsa框架的音频驱动且功能正常。否则以下这些实现里需要全部留空,不用实现。

/*===================================================================*/
/*                                                                   */
/*        InfoNES_SoundInit() : Sound Emulation Initialize           */
/*                                                                   */
/*===================================================================*/
void InfoNES_SoundInit( void )
{}/*===================================================================*/
/*                                                                   */
/*        InfoNES_SoundOpen() : Sound Open                           */
/*                                                                   */
/*===================================================================*/
int InfoNES_SoundOpen( int samples_per_sync, int sample_rate )
{// sample_rate 采样率 44100// samples_per_sync  735// 采样率 / 8 * 声道数 = 44100 / 8 * 1 = 5512.5// 8位 声音/*声道数 1采样率 44100采样位数 8每次播放块大小(NES  APU 每次生成一块)735*/unsigned int rate      = sample_rate;snd_pcm_hw_params_t *hw_params;if(0 > snd_pcm_open(&playback_handle, "default", SND_PCM_STREAM_PLAYBACK, 0)) {printf("snd_pcm_open err\n");return -1;}printf("snd_pcm_open ok!\nsamples_per_sync=%d,sample_rate=%d\n",samples_per_sync,sample_rate);if(0 > snd_pcm_hw_params_malloc(&hw_params)){printf("snd_pcm_hw_params_malloc err\n");return -1;}if(0 > snd_pcm_hw_params_any(playback_handle, hw_params)){printf("snd_pcm_hw_params_any err\n");return -1;}if(0 > snd_pcm_hw_params_set_access(playback_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) {printf("snd_pcm_hw_params_any err\n");return -1;}//16bit PCM 数据if(0 > snd_pcm_hw_params_set_format(playback_handle, hw_params, SND_PCM_FORMAT_U8)){printf("snd_pcm_hw_params_set_format err\n");return -1;}if(0 > snd_pcm_hw_params_set_rate_near(playback_handle, hw_params, &rate, 0)) {printf("snd_pcm_hw_params_set_rate_near err\n");return -1;}//单声道 非立体声if(0 > snd_pcm_hw_params_set_channels(playback_handle, hw_params, 1)){printf("snd_pcm_hw_params_set_channels err\n");return -1;}if(0 > snd_pcm_hw_params(playback_handle, hw_params)) {printf("snd_pcm_hw_params err\n");return -1;}snd_pcm_hw_params_free(hw_params);if(0 > snd_pcm_prepare(playback_handle)) {printf("snd_pcm_prepare err\n");return -1;}return 1;
}/*===================================================================*/
/*                                                                   */
/*        InfoNES_SoundClose() : Sound Close                         */
/*                                                                   */
/*===================================================================*/
void InfoNES_SoundClose( void )
{snd_pcm_close(playback_handle);
}/*===================================================================*/
/*                                                                   */
/*            InfoNES_SoundOutput() : Sound Output 5 Waves           */
/*                                                                   */
/*===================================================================*/
void InfoNES_SoundOutput( int samples, BYTE *wave1, BYTE *wave2, BYTE *wave3, BYTE *wave4, BYTE *wave5 )
{int i;int ret;unsigned char wav;unsigned char *pcmBuf = (unsigned char *)malloc(samples);//printf("InfoNES_SoundOutput,samples=%d\n",samples);//printf("\n");for (i=0; i <samples; i++){wav = (wave1[i] + wave2[i] + wave3[i] + wave4[i] + wave5[i]) / 5;//单声道 8位数据pcmBuf[i] = wav;//printf("%02x",wav);}//printf("\n");ret = snd_pcm_writei(playback_handle, pcmBuf, samples);if(-EPIPE == ret){snd_pcm_prepare(playback_handle);}free(pcmBuf);return ;
}

USB手柄支持

接下来这块儿是介绍的重点,实现usb手柄驱动的支持。这样才有可玩性啊。我买的这款USB的游戏手柄很便宜,也很容易买到。如果你的USB手柄不是这款,那么实现驱动支持的原理也是类似的,万变不离宗,只是键值对应关系跟我的可能不一样,实测改下即可。

关于USB游戏手柄的驱动支持,参见我的上篇博文:iMX6ULL驱动开发 | 让imx6ull开发板支持usb接口FC游戏手柄_特立独行的猫a的博客-CSDN博客

不想按上文总结的重新编译内核的话,可以把驱动单独编译成模块动态加载进去。

这里介绍下让infoNES支持usb手柄需要做哪些移植。

按键键值测试小程序

#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <linux/input.h> #define _EV_KEY         0x01    /* button pressed/released */
#define _EV_ABS         0x03    
#define _EV_MSC         0x04   int main() {printf("hello,usb hid joystick key test\n");int fd = open("/dev/input/event3", O_RDONLY);struct input_event e;while(1) {read(fd, &e, sizeof(e));switch(e.type) {case _EV_KEY:printf("type: %d, code: %d,value: %d, time: %d\n", e.type, e.code,e.value, e.time);break;case _EV_ABS:printf("type: %d, code: %d,value: %d, time: %d\n", e.type, e.code,e.value, e.time);break;case _EV_MSC:printf("type: %d, code: %d,value: %d, time: %d\n", e.type, e.code,e.value, e.time);break;default:if(e.type != 0){printf("type:%d, code: %d,value: %d, time: %d\n",e.type, e.code,e.value, e.time);}}}close(fd);return 0;
}

joypad_input.cpp文件修改

主要是USBjoypadGet()接口的实现,要跟FC手柄的键值对应上。

static int USBjoypadGet(void)
{/*** FC手柄 bit 键位对应关系 真实手柄中有一个定时器,处理 连A  连B * 0  1   2       3       4    5      6     7* A  B   Select  Start  Up   Down   Left  Right*///因为 USB 手柄每次只能读到一位键值 所以要有静态变量保存上一次的值static unsigned char joypad = 0;struct input_event e;if(0 < read (USBjoypad_fd, &e, sizeof(e))){if(0x3 == e.type){/*上:value:0 type:0x3 code:0x1value:127 type:0x3 code:0x1*/if(0 == e.value && 0x1 == e.code){joypad |= 1<<4;printf("Up\n");}/*下:value:255 type:0x3 code:0x1value:127 type:0x3 code:0x1*/if(255 == e.value && 0x1 == e.code){joypad |= 1<<5;printf("Down\n");}//松开if(127 == e.value && 0x1 == e.code){joypad &= ~(1<<4 | 1<<5);}/*左:value:0 type:0x3 code:0x0value:127 type:0x3 code:0x0*/if(0 == e.value && 0 == e.code){joypad |= 1<<6;printf("Left\n");}/*右:value:255 type:0x3 code:0x0value:127 type:0x3 code:0x0*/if(255 == e.value && 0 == e.code){joypad |= 1<<7;printf("Right\n");}//松开if(127 == e.value && 0 == e.code){joypad &= ~(1<<6 | 1<<7);}}if(0x1 == e.type){/*选择:value:0x1 type:0x1 code:296value:0x0 type:0x1 code:296*/if(0x1 == e.value && 296 == e.code){joypad |= 1<<2;printf("Select\n");}if(0x0 == e.value && 296 == e.code){joypad &= ~(1<<2);}/*开始:value:0x1 type:0x1 code:297value:0x0 type:0x1 code:297*/if(0x1 == e.value && 297 == e.code){joypad |= 1<<3;printf("Start\n");}if(0x0 == e.value && 297 == e.code){joypad &= ~(1<<3);}/*Avalue:0x1 type:0x1 code:288value:0x0 type:0x1 code:288*/if(0x1 == e.value && 288 == e.code){joypad |= 1<<0;printf("A\n");}if(0x0 == e.value && 288 == e.code){joypad &= ~(1<<0);}/*Bvalue:0x1 type:0x1 code:289value:0x0 type:0x1 code:289*/if(0x1 == e.value && 289 == e.code){joypad |= 1<<1;printf("B\n");}if(0x0 == e.value && 289 == e.code){joypad &= ~(1<<1);}/*Xvalue:0x1 type:0x1 code:290value:0x0 type:0x1 code:290*/if(0x1 == e.value && 290 == e.code){joypad |= 1<<0;printf("X\n");}if(0x0 == e.value && 290 == e.code){joypad &= ~(1<<0);}/*Yvalue:0x1 type:0x1 code:291value:0x0 type:0x1 code:291*/if(0x1 == e.value && 291 == e.code){joypad |= 1<<1;printf("Y\n");}if(0x0 == e.value && 291 == e.code){joypad &= ~(1<<1);}}return joypad;}return -1;
}

完整实现

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <pthread.h>
#include <linux/input.h> #define JOYPAD_DEV "/dev/joypad"
#define USB_JS_DEV "/dev/input/event3"typedef struct JoypadInput{int (*DevInit)(void);int (*DevExit)(void);int (*GetJoypad)(void);struct JoypadInput *ptNext;pthread_t tTreadID;     /* 子线程ID */
}T_JoypadInput, *PT_JoypadInput;//全局变量通过互斥体访问
static unsigned char g_InputEvent;static pthread_mutex_t g_tMutex  = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t  g_tConVar = PTHREAD_COND_INITIALIZER;static int joypad_fd;
static int USBjoypad_fd;
static PT_JoypadInput g_ptJoypadInputHead;static void *InputEventTreadFunction(void *pVoid)
{/* 定义函数指针 */int (*GetJoypad)(void);GetJoypad = (int (*)(void))pVoid;while (1){//因为有阻塞所以没有输入时是休眠g_InputEvent = GetJoypad();//有数据时唤醒pthread_mutex_lock(&g_tMutex);/*  唤醒主线程 */pthread_cond_signal(&g_tConVar);pthread_mutex_unlock(&g_tMutex);}
}static int RegisterJoypadInput(PT_JoypadInput ptJoypadInput)
{PT_JoypadInput tmp;if(ptJoypadInput->DevInit()){return -1;}//初始化成功创建子线程 将子项的GetInputEvent 传进来pthread_create(&ptJoypadInput->tTreadID, NULL, InputEventTreadFunction, (void*)ptJoypadInput->GetJoypad);if(! g_ptJoypadInputHead){g_ptJoypadInputHead = ptJoypadInput;}else{tmp = g_ptJoypadInputHead;while(tmp->ptNext){tmp = tmp->ptNext;}tmp->ptNext = ptJoypadInput;}ptJoypadInput->ptNext = NULL;return 0;
}static int joypadGet(void)
{static unsigned char joypad = 0;//printf("joypadGet val:\n");joypad = read(joypad_fd, 0, 0);return joypad;
}static int joypadDevInit(void)
{joypad_fd = open(JOYPAD_DEV, O_RDONLY);if(-1 == joypad_fd){printf("%s dev not found \r\n", JOYPAD_DEV);return -1;}return 0;
}static int joypadDevExit(void)
{close(joypad_fd);return 0;
}static T_JoypadInput joypadInput = {joypadDevInit,joypadDevExit,joypadGet,
};static int USBjoypadGet(void)
{/*** FC手柄 bit 键位对应关系 真实手柄中有一个定时器,处理 连A  连B * 0  1   2       3       4    5      6     7* A  B   Select  Start  Up   Down   Left  Right*///因为 USB 手柄每次只能读到一位键值 所以要有静态变量保存上一次的值static unsigned char joypad = 0;struct input_event e;if(0 < read (USBjoypad_fd, &e, sizeof(e))){if(0x3 == e.type){/*上:value:0 type:0x3 code:0x1value:127 type:0x3 code:0x1*/if(0 == e.value && 0x1 == e.code){joypad |= 1<<4;printf("Up\n");}/*下:value:255 type:0x3 code:0x1value:127 type:0x3 code:0x1*/if(255 == e.value && 0x1 == e.code){joypad |= 1<<5;printf("Down\n");}//松开if(127 == e.value && 0x1 == e.code){joypad &= ~(1<<4 | 1<<5);}/*左:value:0 type:0x3 code:0x0value:127 type:0x3 code:0x0*/if(0 == e.value && 0 == e.code){joypad |= 1<<6;printf("Left\n");}/*右:value:255 type:0x3 code:0x0value:127 type:0x3 code:0x0*/if(255 == e.value && 0 == e.code){joypad |= 1<<7;printf("Right\n");}//松开if(127 == e.value && 0 == e.code){joypad &= ~(1<<6 | 1<<7);}}if(0x1 == e.type){/*选择:value:0x1 type:0x1 code:296value:0x0 type:0x1 code:296*/if(0x1 == e.value && 296 == e.code){joypad |= 1<<2;printf("Select\n");}if(0x0 == e.value && 296 == e.code){joypad &= ~(1<<2);}/*开始:value:0x1 type:0x1 code:297value:0x0 type:0x1 code:297*/if(0x1 == e.value && 297 == e.code){joypad |= 1<<3;printf("Start\n");}if(0x0 == e.value && 297 == e.code){joypad &= ~(1<<3);}/*Avalue:0x1 type:0x1 code:288value:0x0 type:0x1 code:288*/if(0x1 == e.value && 288 == e.code){joypad |= 1<<0;printf("A\n");}if(0x0 == e.value && 288 == e.code){joypad &= ~(1<<0);}/*Bvalue:0x1 type:0x1 code:289value:0x0 type:0x1 code:289*/if(0x1 == e.value && 289 == e.code){joypad |= 1<<1;printf("B\n");}if(0x0 == e.value && 289 == e.code){joypad &= ~(1<<1);}/*Xvalue:0x1 type:0x1 code:290value:0x0 type:0x1 code:290*/if(0x1 == e.value && 290 == e.code){joypad |= 1<<0;printf("X\n");}if(0x0 == e.value && 290 == e.code){joypad &= ~(1<<0);}/*Yvalue:0x1 type:0x1 code:291value:0x0 type:0x1 code:291*/if(0x1 == e.value && 291 == e.code){joypad |= 1<<1;printf("Y\n");}if(0x0 == e.value && 291 == e.code){joypad &= ~(1<<1);}}return joypad;}return -1;
}static int USBjoypadDevInit(void)
{USBjoypad_fd = open(USB_JS_DEV, O_RDONLY);if(-1 == USBjoypad_fd){printf("%s dev not found \r\n", USB_JS_DEV);return -1;}return 0;
}static int USBjoypadDevExit(void)
{close(USBjoypad_fd);return 0;
}static T_JoypadInput usbJoypadInput = {USBjoypadDevInit,USBjoypadDevExit,USBjoypadGet,
};int InitJoypadInput(void)
{int iErr = 0;//iErr = RegisterJoypadInput(&joypadInput);iErr = RegisterJoypadInput(&usbJoypadInput);return iErr;
}int GetJoypadInput(void)
{/* 休眠 */pthread_mutex_lock(&g_tMutex);pthread_cond_wait(&g_tConVar, &g_tMutex);	/* 被唤醒后,返回数据 */pthread_mutex_unlock(&g_tMutex);return g_InputEvent;
}

编译生成

最后,交叉编译生成可执行文件,放到板子上执行即可,插上USB手柄就可以玩啦,运行不错!还很流畅。需要注意的是,为了支持声音,使用了alsa的头文件并链接了libasound库。需确保你的环境里有这个库,没有的话不支持声音输出,可以去掉这个链接。文末有NES游戏的ROM资源。

makefile脚本

#根据实际路径修改工具链路径
CHAIN_ROOT=/opt/yang/imax6ul/gcc-linaro-arm-linux-gnueabihf-4.9-2014.09_linux/bin
CROSS_COMPILE=$(CHAIN_ROOT)/arm-linux-gnueabihf-#CHAIN_ROOT= /home/yang/b503/ctools/gcc-linaro-arm-linux-gnueabihf-4.9-2014.09_linux/bin
#CROSS_COMPILE=$(CHAIN_ROOT)/arm-linux-gnueabihf-
#CROSS_COMPILE = CC     := $(CROSS_COMPILE)gcc
#CC = arm-poky-linux-gnueabi-gcc
TARBALL = InfoNES08J# InfoNES
.CFILES =	./../K6502.cpp \./../InfoNES.cpp \./../InfoNES_Mapper.cpp \./../InfoNES_pAPU.cpp \./InfoNES_System_Linux.cpp joypad_input.cpp.OFILES	=	$(.CFILES:.cpp=.o)CCFLAGS =    -o2 -fsigned-char  -I../
LDFILGS = -lstdc++	-L../libs	# gcc3.x.xall: InfoNESInfoNES: $(.OFILES)$(CC) $(INCLUDES) -o $@ $(.OFILES) $(LDFILGS) -lm  -lpthread -lasound.cpp.o:$(CC) $(INCLUDES) -c $(CCFLAGS) $*.cpp  -o $@clean:rm -f $(.OFILES) ../*~ ../*/*~ corecleanall:rm -f $(.OFILES) ../*~ ../*/*~ core InfoNESrelease: clean alltar:( cd ..; \tar cvf $(TARBALL).tar ./*; \gzip $(TARBALL).tar \)install:install ./InfoNES /usr/local/bin

其他资源

NES红白机全屏显示

NES专题——NES游戏机简介_nesfc_金小庭的博客-CSDN博客

V3S移植nes游戏模拟器(附带游戏合集)_v3s编译游戏模拟器_qq_46604211的博客-CSDN博客

任天堂红白机nes游戏简介 任天堂红白机nes游戏简介

资料:内含众多NES的游戏ROM文件及运行模拟器

链接:https://pan.baidu.com/s/1uXAxLKGmKGwZFB3Yraq8gg  提取码:qxcy 

游戏合集并解压,然后改名为游戏名为英文
链接:https://pan.baidu.com/s/16hIWwYQQEX9aOBDG1dVa0A
提取码:asdf

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/23894.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

循环结构的学习

循环结构 文章目录 为什么要学习循环while循环dowhile循环偶数之和断点调试购物结算循环的选择类名和全类名摄氏华氏对照表for循环for执行次序五门功课成绩for的特殊写法break和continue录入客户信息_continue使代码优雅小数的比较不能用或! 为什么要学习循环 在编写代码时&a…

Spring集成Web

目录 1、简介 2、监听器 3、Spring提供的listener 3.1、xml 3.2、配置类 3.3、WebApplicationContextUtils 3.4、说明 4、自己复现的listener 4.1、ContextLoaderListener 4.2、WebApplicationContextUtils 4.3、Web调用 ⭐作者介绍&#xff1a;大二本科网络工程专业…

vue2-v-show和v-if有什么区别,使用场景分别是什么?

1、v-show和v-if的共同点 在vue中&#xff0c;v-if和v-show的作用效果是相同的&#xff08;不含v-else&#xff09;&#xff0c;都能控制元素在页面是否显示&#xff0c;在用法上也相同。 当表达式为true的时候&#xff0c;都会占据页面的位置 当表达式为false的时候&#xff…

AI介绍——chat gpt/文心一言/claude/bard/星火大模型/bing AI

AI体验 1. AI 介绍&#xff08;注册和使用&#xff09;1.1 Chat GPT1.2 文心一言1.3 Slack 上的 Claude1.3.1 Claude 介绍1.3.2 Claude 使用 1.4 Google的Bard1.4.1 Bard 介绍1.4.2 Bard 使用 1.5 科大讯飞的星火大模型1.5.1 星火大模型 介绍1.5.2 星火大模型 使用 1.6 new bin…

Linux软件安装

软件安装 安装软件的方式 软件的安装方式 二进制发布包安装: 软件已经针对具体平台编译打包发布&#xff0c;只要解压&#xff0c;修改配置即可rpm安装:软件已经按照redhat的包管理规范进行打包&#xff0c;使用rpm命令进行安装&#xff0c;不能自行解决库依赖问题yum安装:一…

65 # 实现 http-server 里的 gzip 压缩

用 zlib 来实现 gzip 压缩 服务端优化都是&#xff1a;压缩 缓存 前端可以通过 webpack 插件进行压缩 gzip 根据替换来实现的&#xff0c;重复率越高&#xff0c;压缩后的结果越小 const zlib require("zlib"); const fs require("fs"); const path …

数据可视化:Matplotlib详解及实战

1 Matplotlib介绍 Matplotlib是Python中最常用的可视化工具之一,可以非常方便地创建海量类型的2D图表和一些基本的3D图表。 Matplotlib提供了一个套面向绘图对象编程的API接口&#xff0c;能够很轻松地实现各种图像的绘制&#xff0c;并且它可以配合Python GUI工具&#xff08;…

黑马程序员SpringMVC练手项目

目录 1、需求 2、项目准备 pom.xml SQL jdbc.properties log4j.properties applicationContext.xml spring-mvc.xml web.xml 3、工作流程 4、难点 项目已经上传到gitee&#xff1a;https://gitee.com/xzl-it/my-projects 1、需求 SpringMVC项目练习&#xff1a;数…

基于Open3D的点云处理14-法向量

法向量 计算法向量的接口函数&#xff1a; Open3d使用estimate_normals函数来计算法向量。其参数设置Open3d提供了3中参数搜索的方法&#xff08;所有计算的法向量模长为1&#xff09;&#xff1a; open3d.geometry.KDTreeSearchParamKNN(knn20) # 计…

Linux从安装到实战 常用命令 Bash常用功能 用户和组管理

1.0初识Linux 1.1虚拟机介绍 1.2VMware Workstation虚拟化软件 下载CentOS; 1.3远程链接Linux系统 &FinalShell 链接finalshell半天没连接进去 他说ip adress 看IP地址是在虚拟机上 win11主机是 终端输入&#xff1a; ifconfig VMware虚拟机的设置 & ssh连接_snge…

初识MySQL数据库之用户管理

目录 一、用户管理 二、用户 1. 用户信息 2. 创建用户 3. 用户登录测试 4. 删除用户 5. 设置用户远端登录 6. 修改密码 6.1 修改当前用户的密码 6.2 root用户修改指定用户的密码 三、权限 1. 数据库中的各个权限含义 2. 给用户授权 3. 查看用户拥有权限 4. 授权…

Flink之JDBC Sink

这里介绍一下Flink Sink中jdbc sink的使用方法,以mysql为例,这里代码分为两种,事务和非事务 非事务代码 import org.apache.flink.connector.jdbc.JdbcConnectionOptions; import org.apache.flink.connector.jdbc.JdbcExecutionOptions; import org.apache.flink.connector.…

设计模式概述与UML图

文章目录 一、设计模式概述1. 软件设计模式的产生背景2. 软件设计模式的概念3. 学习设计模式的必要性4. 设计模式分类&#xff08;1&#xff09;创建型模式&#xff08;2&#xff09;结构型模式&#xff08;3&#xff09;行为型模式 二、UML图1. 类图概述2. 类图作用3. 类图表示…

深入学习JVM —— GC垃圾回收机制

前言 前面荔枝已经梳理了有关JVM的体系结构和类加载机制&#xff0c;也详细地介绍了JVM在类加载时的双亲委派模型&#xff0c;而在这篇文章中荔枝将会比较详细地梳理有关JVM学习的另一大重点——GC垃圾回收机制的相关知识&#xff0c;重点了解的比如对象可达性的判断、四种回收…

215. 数组中的第K个最大元素(快排+大根堆+小根堆)

题目链接&#xff1a;力扣 解题思路&#xff1a; 方法一&#xff1a;基于快速排序 因为题目中只需要找到第k大的元素&#xff0c;而快速排序中&#xff0c;每一趟排序都可以确定一个最终元素的位置。 当使用快速排序对数组进行降序排序时&#xff0c;那么如果有一趟排序过程…

VSCode C/C++ 分目录编译配置

分目录编译配置记录 launch.json文件 注释处为修改内容 {// 使用 IntelliSense 了解相关属性。 // 悬停以查看现有属性的描述。// 欲了解更多信息&#xff0c;请访问: https://go.microsoft.com/fwlink/?linkid830387"version": "0.2.0","configur…

PHP8的表达式-PHP8知识详解

表达式是 PHP 最重要的基石。在 PHP8中&#xff0c;几乎所写的任何东西都是一个表达式。简单但却最精确的定义一个表达式的方式就是"任何有值的东西"。 最基本的表达式形式是常量和变量。当键入"$a 5"&#xff0c;即将值"5"分配给变量 $a。&quo…

后端进阶之路——综述Spring Security认证,授权(一)

前言 「作者主页」&#xff1a;雪碧有白泡泡 「个人网站」&#xff1a;雪碧的个人网站 「推荐专栏」&#xff1a; ★java一站式服务 ★ ★前端炫酷代码分享 ★ ★ uniapp-从构建到提升★ ★ 从0到英雄&#xff0c;vue成神之路★ ★ 解决算法&#xff0c;一个专栏就够了★ ★ 架…

【数据结构篇】手写双向链表、单向链表(超详细)

文章目录 链表1、基本介绍2、单向链表2.1 带头节点的单向链表测试类&#xff1a;链表实现类&#xff1a; 2.2 不带头节点的单向链表2.3 练习测试类&#xff1a;链表实现类&#xff1a; 3、双向链表测试类&#xff1a;双向链表实现类&#xff1a; 4、单向环形链表**测试类**&…

Gitlab CI/CD笔记-第二天-GitOps的流水线常用关键词(1)

一、常用关键词 在Gitlab项目的根目录需要创建一个 .gitlab-ci.yaml的文件。 这个文件就是定义的流水线。Call :"Pipeline as code" 二、这条流水线怎么写&#xff1f; 一、掌握常用的关键词即可。 1.关键词分类 1.全局关键词 Global Keywards 2.任务关键词…