2012年11月11日 星期日

[懷舊] jmcce 1.5 (1) - in apple ibook/ubuntu (powerpc)

這次要在 ibook/ubuntu (cpu: powerpc) 上測試 jmcce。

不過先來聊聊 fbterm 神奇的 vesa dirver。fbterm 除了使用 linux framebuffer 還提供了 vesa driver, 這是怎麼做的? 我很好奇, 原來是使用 libx86, 進而呼叫 bios 0x10 來使用 bios vesa function, 參考 vesadev.cpp L 48 ~ 61:

210 void VesaDev::printModes()

從這裡開始追起, linux 還有這樣的 library 可以用, 真是令我驚訝。不過這不是我想要的方式, 我想要的方式是直接操作 vga register, 不使用 bios call。這個 library 神奇的地方是: linux 處於保護模式下, 無法使用 bios call, 可是這 library 卻可以提供呼叫 bios 的能力, 真是厲害。

vesadev.cpp
1 /*
2 * Copyright © 2008-2010 dragchan <zgchan317@gmail.com>
3 * This file is part of FbTerm.
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 *
19 */

20
21 #include "config.h"
22 #ifdef ENABLE_VESA
23
24 #include <stdlib.h>
25 #include <stdio.h>
26 #include <unistd.h>
27 #include <string.h>
28 #include <fcntl.h>
29 #include <sys/io.h>
30 #include <sys/mman.h>
31 extern "C" {
32 #include <libx86.h>
33 }
34 #include "vesadev.h"
35 #include "vbe.h"
36 #include "fbterm.h"
37
38 #define MAX_VIDEO_MEM (16 * 1024 * 1024)
39 #define MIN(a,b) ((a) < (b) ? (a) : (b))
40
41 static s16 cur_mode;
42 static vbe_mode_info_block mode_info;
43 static LRMI_regs r;
44 static u32 scanline_width, scanline_height;
45
46 extern u32 effective_uid;
47
48 static bool call_bios()
49 {
50 bool success = true;
51 u16 fun = r.eax;
53 if (!LRMI_int(0x10, &r)) success = false;
54 if ((fun >> 8) == 0x4f && (r.eax & 0xffff) != 0x4f) success = false;
56 if (!success) {
57 fprintf(stderr, "call VESA function 0x%x failure!\n", fun);
58 }
60 return success;
61 }
62
63 static void save_restore_state(bool save)
64 {
65 static void *state_buf;
66 static u32 state_size;
67 static bool saved = false;
68
69 if (save) {
70 if (saved) return;
71
72 r.eax = VBE_FUN_SAVE_RESTORE_STATE;
73 r.ecx = 0xf; /* all states */
74 r.edx = 0; /* get buffer size */
75
76 if (!call_bios()) return;
77
78 state_size = (r.ebx & 0xffff) * 64;
79 state_buf = malloc(state_size);
80
81 void *buf = LRMI_alloc_real(state_size);
82
83 r.eax = VBE_FUN_SAVE_RESTORE_STATE;
84 r.ecx = 0xf; /* all states */
85 r.edx = 1; /* save state */
86 r.es = (long)buf >> 4;
87 r.ebx = (long)buf & 0xf;
88
89 if (call_bios()) {
90 saved = true;
91 memcpy(state_buf, buf, state_size);
92 }
93
94 LRMI_free_real(buf);
95 } else if (saved) {
96 void *buf = LRMI_alloc_real(state_size);
97 memcpy(buf, state_buf, state_size);
98
99 r.eax = VBE_FUN_SAVE_RESTORE_STATE;
100 r.ecx = 0xf; /* all states */
101 r.edx = 2; /* restore state */
102 r.es = (long)buf >> 4;
103 r.ebx = (long)buf & 0xf;
104
105 call_bios();
106 LRMI_free_real(buf);
107 }
108 }
109
110 static void save_restore_mode(bool save)
111 {
112 static s16 mode = -1;
113
114 if (save) {
115 r.eax = VBE_FUN_GET_CURRENT_MODE;
116 if (call_bios()) mode = r.ebx;
117 } else if (mode != -1) {
118 r.eax = VBE_FUN_SET_MODE;
119 r.ebx = mode;
120 call_bios();
121 }
122 }
123
124 // input: mode 0 for auto-detect
125 // return: -1 for failure
126 static s16 get_mode(s16 mode, bool only_print)
127 {
128 seteuid(0);
129 bool ret = (!LRMI_init() || ioperm(0, 1024, 1) || iopl(3));
130 seteuid(getuid());
131
132 if (ret) {
133 fprintf(stderr, "Using VESA requires root privilege\n");
134 return -1;
135 }
136
137 vbe_info_block *info = (vbe_info_block *)LRMI_alloc_real(sizeof(*info));
138
139 r.eax = VBE_FUN_GET_INFO;
140 r.es = (long)info >> 4;
141 memcpy(info->vbe_signature, "VBE2", 4);
142
143 if (!call_bios() || strncmp(info->vbe_signature, "VESA", 4) != 0) {
144 fprintf(stderr, "No VESA bios\n");
145 LRMI_free_real(info);
146 return -1;
147 }
148
149 s16 *mode_list = (s16*)(info->video_mode_list_seg * 16 + info->video_mode_list_off);
150 vbe_mode_info_block *minfo = (vbe_mode_info_block *)LRMI_alloc_real(sizeof(*minfo));
151 u32 xres_tmp, yres_tmp, bpp_tmp;
152 s16 mode_tmp = -1;
153 bool valid_mode = false;
154
155 for (; *mode_list != -1; mode_list++) {
156 r.eax = VBE_FUN_GET_MODE_INFO;
157 r.ecx = *mode_list;
158 r.es = (long)minfo >> 4;
159
160 if (!call_bios()) continue;
161
162 if (!(minfo->mode_attributes & VBE_ATTR_MODE_SUPPORTED)
163 || !(minfo->mode_attributes & VBE_ATTR_COLOR)
164 || !(minfo->mode_attributes & VBE_ATTR_GRAPHICS)
165 || !(minfo->mode_attributes & VBE_ATTR_LINEAR)) continue;
166
167 switch (minfo->bits_per_pixel) {
168 case 8:
169 if (minfo->memory_model != VBE_MODEL_PACKED) continue;
170 break;
171 case 15:
172 case 16:
173 case 32:
174 if (minfo->memory_model != VBE_MODEL_RGB) continue;
175 break;
176 default:
177 continue;
178 }
179
180 valid_mode = true;
181
182 if (only_print) {
183 printf("[%d]: %dx%d-%dbpp\n", *mode_list,
184 minfo->x_resolution, minfo->y_resolution, minfo->bits_per_pixel);
185 } else {
186 if (mode) {
187 if (mode == *mode_list) return mode;
188 } else {
189 if ((mode_tmp == -1)
190 || (minfo->x_resolution > xres_tmp && minfo->y_resolution > yres_tmp)
191 || (minfo->x_resolution == xres_tmp && minfo->y_resolution == yres_tmp && minfo->bits_per_pixel > bpp_tmp)) {
192
193 mode_tmp = *mode_list;
194 xres_tmp = minfo->x_resolution;
195 yres_tmp = minfo->y_resolution;
196 bpp_tmp = minfo->bits_per_pixel;
197 }
198 }
199 }
200 }
201
202 if (!valid_mode) fprintf(stderr, "can't find a valid VESA mode for your video card\n");
203 else if (!only_print && mode_tmp == -1) fprintf(stderr, "%d isn't a valid VESA mode for your video card\n", mode);
204
205 LRMI_free_real(info);
206 LRMI_free_real(minfo);
207 return mode_tmp;
208 }
209
210 void VesaDev::printModes()
211 {
212 get_mode(0, true);
213 }
214
215 VesaDev *VesaDev::initVesaDev(s16 mode)
216 {
217 cur_mode = get_mode(mode, false);
218 if (cur_mode == -1) return 0;
219
220 void *minfo = LRMI_alloc_real(sizeof(mode_info));
221
222 r.eax = VBE_FUN_GET_MODE_INFO;
223 r.ecx = cur_mode;
224 r.es = (long)minfo >> 4;
225 call_bios();
226
227 memcpy(&mode_info, minfo, sizeof(mode_info));
228 LRMI_free_real(minfo);
229
230 cur_mode |= 0x4000; // use linear frame buffer mode
231 return new VesaDev();
232 }
233
234 VesaDev::VesaDev()
235 {
236 mWidth = mode_info.x_resolution;
237 mHeight = mode_info.y_resolution;
238 mBitsPerPixel = mode_info.bits_per_pixel;
239
240 scanline_width = mWidth;
241 scanline_height = mHeight;
242 }
243
244 VesaDev::~VesaDev()
245 {
246 if (mVMemBase) munmap(mVMemBase, mBytesPerLine * scanline_height);
247 }
248
249 const s8 *VesaDev::drvId()
250 {
251 return "VESA";
252 }
253
254 void VesaDev::switchVc(bool enter)
255 {
256 if (enter) {
257 static bool inited = false;
258
259 if (!inited) {
260 save_restore_mode(true);
261 save_restore_state(true);
262 }
263
264 r.eax = VBE_FUN_SET_MODE;
265 r.ebx = cur_mode;
266 call_bios();
267
268 r.eax = VBE_FUN_LOGICAL_SCANLINE;
269 r.ebx = 0; // set scan line length in pixels
270 r.ecx = scanline_width;
271 call_bios();
272
273 if (!inited) {
274 inited = true;
275
276 r.eax = VBE_FUN_LOGICAL_SCANLINE;
277 r.ebx = 1; // get scan line length
278 call_bios(); // return ebx: scan line length in bytes
279 // ecx: scan line length in pixels
280 // edx: maximum scan lines
281
282 mBytesPerLine = r.ebx;
283 initScrollType();
284
285 seteuid(0);
286 s32 fd = open("/dev/mem", O_RDWR);
287 seteuid(getuid());
288
289 mVMemBase = (u8 *)mmap(0, mBytesPerLine * scanline_height, PROT_READ | PROT_WRITE, MAP_SHARED, fd, mode_info.phys_base_ptr);
290 close(fd);
291 }
292 } else {
293 save_restore_mode(false);
294 save_restore_state(false);
295 }
296
297 Screen::switchVc(enter);
298 }
299
300 void VesaDev::initScrollType()
301 {
302 r.eax = VBE_FUN_DISPLAY_START;
303 r.ebx = 0; // set display start
304 r.ecx = 0; // x = 0
305 r.edx = 0; // y = 0
306 if (!call_bios()) return;
307
308 if (mRotateType == Rotate0 || mRotateType == Rotate180) {
309 r.eax = VBE_FUN_LOGICAL_SCANLINE;
310 r.ebx = 1; // get scan line length
311 call_bios();
312
313 u32 max_height = MIN(r.edx, MAX_VIDEO_MEM / mBytesPerLine);
314 if (max_height <= mHeight) return;
315
316 mScrollType = YPan;
317 mOffsetMax = max_height - mHeight;
318 scanline_height = max_height;
319 } else {
320 // mWidth/mHeight has been swapped
321 u32 width = mHeight, height = mWidth;
322
323 r.eax = VBE_FUN_LOGICAL_SCANLINE;
324 r.ebx = 3; // get maximum scan line length
325 if (!call_bios()) return;
326
327 u32 max_width = r.ecx;
328
329 r.eax = VBE_FUN_LOGICAL_SCANLINE;
330 r.ebx = 0; // set scan line length in pixels
331 call_bios();
332
333 r.eax = VBE_FUN_LOGICAL_SCANLINE;
334 r.ebx = 1; // get scan line length
335 call_bios();
336
337 if (r.ecx <= width || r.edx < height) return;
338
339 scanline_width = max_width;
340 mScrollType = XPan;
341 mBytesPerLine = r.ebx;
342 mOffsetMax = r.ecx - width;
343 }
344 }
345
346 void VesaDev::setupOffset()
347 {
348 if (mScrollType != YPan && mScrollType != XPan) return;
349
350 r.eax = VBE_FUN_DISPLAY_START;
351 r.ebx = 0; // set display start
352 if (mScrollType == YPan) {
353 r.ecx = 0;
354 r.edx = mOffsetCur;
355 } else {
356 r.ecx = mOffsetCur;
357 r.edx = 0;
358 }
359 call_bios();
360 }
361
362 void VesaDev::setupPalette(bool restore)
363 {
364 if (mode_info.memory_model != VBE_MODEL_PACKED || restore) return;
365
366 vbe_palette_entry *buf = (vbe_palette_entry *)LRMI_alloc_real(sizeof(*buf) * NR_COLORS);
367
368 for (u32 i = 0; i < NR_COLORS; i++) {
369 buf[i].red = mPalette[i].red >> 2;
370 buf[i].green = mPalette[i].green >> 2;
371 buf[i].blue = mPalette[i].blue >> 2;
372 }
373
374 r.eax = VBE_FUN_PALETTE_DATA;
375 r.ebx = 0; // set palette data
376 r.ecx = NR_COLORS; // number of palette entry
377 r.edx = 0; // first palette entry
378 r.es = (long)buf >> 4;
379 call_bios();
380
381 LRMI_free_real(buf);
382 }
383 #endif

powerpc ubuntu 平台沒有 libx86 可裝, 無法使用 uvesa, 只能用其他的 fb 功能, 我是拿掉 radeon module, 修正一些 compile issue 後, 可以在 ibook/ubuntu 上使用。

下方影片是 jmcce 在 ibook (cpu: powerpc) ubuntu 10.04 上的測試畫面, 純粹使用 linux framebuffer, 由於 jmcce 只支援 fb 256 色, 所以需要使用 fbset -depth 8 設定為 256 色, 解析度則因為無法調整為 640X480, 維持原來的 1024X768, 所以畫面看起來就變成這樣子了。捲動效果還是不快。



非 x86/linux 無法使用 svgalib, 所以只能單純的使用 framebuffer, 看來執行得還算可以。

沒有留言:

張貼留言

使用 google 的 reCAPTCHA 驗證碼, 總算可以輕鬆留言了。

我實在受不了 spam 了, 又不想讓大家的眼睛花掉, 只好放棄匿名留言。這是沒辦法中的辦法了。留言的朋友需要有 google 帳號。