DEF SEG + PEEK/POKE hack

Post your FreeBASIC tips and tricks here. Please don’t post your code without including an explanation.
v1ctor
Site Admin
Posts: 3801
Joined: May 27, 2005 8:08
Location: SP / Bra[s]il
Contact:

DEF SEG + PEEK/POKE hack

Postby v1ctor » Jan 03, 2008 23:06

There were some (one?) complaints in another forum about how ugly FB was because it's doesn't let you use DEF SEG and doesn't include things like the "Redo from start" message, so i wrote a small hack to show how to compile those n-lines pure-QB context apps without changing a SINGLE line of the original source.

W.I.P., i may implement DEF SEG into -lang qb in future just for fun - even without a SINGLE user asking for that during these 3 long years.

The current SVN version is needed to compile this source.

Code: Select all

'' peekpoke.bi
''
'' DEF SEG + PEEK/POKE to screen hack
'' copyleft (c) Space Ghost (ps: Zorak stinks)
''

'' TODO:
'' [ ] handle plannar modes (screen 7 and 12)
'' [ ] handle multiple pages (screen 7?)
'' [ ] handle text-mode
'' [ ] handle BIOS access
'' [ ] if seg = 0, use 'fbrocks' as base
''      note: 1) this won't work with local vars because SS != DS
''             2) -lang qb must allocate main()'s locals in DS as before so most programs will work

type fbrocks
   seg as __uinteger
   lastseg as __uinteger
   isgfx as long
   islinear as long
   w as long
   h as long
   bpp as long
   pitch as long
   buffer as __byte __ptr
end type

   dim shared fbrocks as fbrocks

#define def fbrocks.

'':::::
#define varseg( v ) __cuint(__culng( @v ) __shr 16)

#undef varptr
#define varptr( v ) __cuint(__culng( @v ) and &h0000FFFF&)

'':::::
__private sub fbrocks_changeSeg
   
   '' this is safe to be done if the seg was set using varseg(), because no
   '' symbol will be allocated above &hA0000000 (> 2GB)
   
   select case fbrocks.seg
   case &hA000&
      __screeninfo fbrocks.w, fbrocks.h, , fbrocks.bpp, fbrocks.pitch
      fbrocks.buffer = __screenptr
      fbrocks.isgfx = -1
      fbrocks.islinear = -1
   
   case else
      fbrocks.isgfx = 0
         
   end select

   fbrocks.lastseg = fbrocks.seg

end sub

'':::::
#undef poke
__private sub poke( byval ofs as __uinteger, byval value as integer ) static
   if( fbrocks.seg <> fbrocks.lastseg ) then
      fbrocks_changeSeg
   end if
   
   if( fbrocks.isgfx ) then
      dim x as long, y as long
      if( fbrocks.islinear ) then
         x = ofs mod fbrocks.w
         y = ofs \ fbrocks.w
         fbrocks.buffer[y * fbrocks.pitch + x] = value
         __screenunlock y, y
      else
      end if
   
   else
      *__cast( __byte __ptr, (fbrocks.seg __shl 16) + ofs ) = value
      
   end if
   
end sub

'':::::
#undef peek
__private function peek( byval ofs as __uinteger ) as integer
   if( fbrocks.seg <> fbrocks.lastseg ) then
      fbrocks_changeSeg
   end if
   
   if( fbrocks.isgfx ) then
      dim x as long, y as long
      if( fbrocks.islinear ) then
         x = ofs mod fbrocks.w
         y = ofs \ fbrocks.w
         peek = fbrocks.buffer[y * fbrocks.pitch + x]
      else
      end if
   
   else
      peek = *__cast( __byte __ptr, (fbrocks.seg __shl 16) + ofs )
   end if
   
end function


Neat QB test, use "-lang qb -include peekpoke.bi" (without quotes) to compile:

Code: Select all

'Raycaster 36 lines by Enthropy
SCREEN 13
xpos = 1.5
ypos = 1.5
DEF SEG = &HA000
DIM SHARED map(9, 9) AS INTEGER, flut(200) AS SINGLE, tex(31, 31) AS INTEGER, foff(15) AS INTEGER
6 IF p% < 100 THEN READ map(p% \ 10, p% MOD 10) ELSE IF p% < 200 THEN flut(p% + 1) = 25600 / (p% - 99) ELSE IF p% < 200 + 1024 THEN tex((p% - 200) \ 32, (p% - 200) AND 31) = 16 + 16 * RND
IF p% > 1223 AND p% < 1288 THEN PALETTE p% - 1224, (p% - 1224) * (1 + 256 + 65536&) ELSE IF p% > 1223 AND p% < 1352 THEN PALETTE (p% - 1224), (p% - 1288) * 256& ELSE IF p% > 1223 AND p% < 1416 THEN PALETTE (p% - 1224), (p% - 1352) * 65536& ELSE IF p% > 1223 AND p% < 1432 THEN foff(p% - 1416) = 64 * COS(p% * 3.141593 / 8)
p% = p% + 1
1 IF p% < 215 + 1224 THEN 6 ELSE dx = COS(angle) * .05 - (x% - 160) * SIN(angle) * .05 / 160
dy = (x% - 160) * COS(angle) * .05 / 160 + SIN(angle) * .05
IF dx < -.0001 THEN nextxt = -(xpos - INT(xpos)) / dx ELSE IF dx > .0001 THEN nextxt = (1 - xpos + INT(xpos)) / dx ELSE IF dx = 0 THEN nextxt = 10000
IF dx < -.0001 THEN dxt = -1 / dx ELSE IF dx > .0001 THEN dxt = 1 / dx ELSE IF dx = 0 THEN dxt = 10000
IF dy < -.0001 THEN nextyt = -(ypos - INT(ypos)) / dy ELSE IF dy > .0001 THEN nextyt = (1 - ypos + INT(ypos)) / dy ELSE IF dy = 0 THEN nextyt = 10000
IF dy < -.0001 THEN dyt = -1 / dy ELSE IF dy > .0001 THEN dyt = 1 / dy ELSE IF dy = 0 THEN dyt = 10000
xm% = INT(xpos)
ym% = INT(ypos)
2 IF nextxt < nextyt THEN t = nextxt ELSE t = nextyt
IF nextxt < nextyt THEN xm% = xm% + SGN(dx) ELSE ym% = ym% + SGN(dy)
IF nextxt < nextyt THEN nextxt = nextxt + dxt ELSE nextyt = nextyt + dyt
IF (map(ym%, xm%) = 0 AND t < 256) OR t < .1 THEN GOTO 2 ELSE tx% = ((xpos + ypos + t * (dx + dy)) * 32) AND 31
foff2% = foff(f% AND 15)
d1% = 99 - INT((800 + foff2%) / t)
d2% = 102 + INT((800 - foff2%) / t)
calc = (25600 - 32 * foff2%)
4 IF y% < d1% THEN POKE p& + x%, 128 + tex((xpos + dx * flut(200 - y%)) AND 31, (ypos + dy * flut(200 - y%)) AND 31) ELSE IF y% < d2% THEN POKE p& + x%, tex(((32 * (y% - d1%)) \ (d2% - d1%)), tx%) ELSE POKE p& + x%, 64 + tex((xpos * 32 + dx * calc / (y% - 99)) AND 31, (ypos * 32 + dy * calc / (y% - 99)) AND 31)
p& = (p& + 320) MOD 64000
y% = (y% + 1) MOD 200
IF y% > 0 THEN 4 ELSE x% = (x% + 1) MOD 320
3 IF x% > 0 THEN 1 ELSE a$ = INKEY$
IF a$ = "" THEN 3 ELSE angle = angle + ((a$ = CHR$(0) + "K") - (a$ = CHR$(0) + "M")) * .1
xpos2 = ((a$ = CHR$(0) + "H") - (a$ = CHR$(0) + "P")) * COS(angle) * .05
ypos2 = ((a$ = CHR$(0) + "H") - (a$ = CHR$(0) + "P")) * SIN(angle) * .05
IF map(INT(ypos - .05), INT(xpos - xpos2 - xpos2 - .05)) = 0 AND map(INT(ypos - .05), INT(xpos - xpos2 - xpos2 + .05)) = 0 AND map(INT(ypos + .05), INT(xpos - xpos2 - xpos2 - .05)) = 0 AND map(INT(ypos + .05), INT(xpos - xpos2 - xpos2 + .05)) = 0 THEN xpos = xpos - xpos2
IF map(INT(ypos - ypos2 - ypos2 - .05), INT(xpos - .05)) = 0 AND map(INT(ypos - ypos2 - ypos2 + .05), INT(xpos - .05)) = 0 AND map(INT(ypos - ypos2 - ypos2 - .05), INT(xpos + .05)) = 0 AND map(INT(ypos - ypos2 - ypos2 + .05), INT(xpos + .05)) = 0 THEN ypos = ypos - ypos2
f% = f% + (a$ = CHR$(0) + "H") - (a$ = CHR$(0) + "P")
IF a$ <> CHR$(27) THEN GOTO 1
'' data's rewritten to not screw with the forum formatting
DATA 7,8,7,8,7,8,7,8,7,8,7,0,0,0,0,0,0
data 0,0,8,8,0,9,1,0,2,10,2,0,7,7
data 0,1,9,0,0,0,10,0,8,8,0,0,0,0
data 0,0,0,0,7,7,0,3,11,3,11,0,0
data 0,8,8,0,11,0,0,3,0,0,0,7
data 7,0,3,0,0,11,0,0,0,8,8,0
data 0,0,0,0,0,0,0,7,8,7,8,7,8,7,8,7,8,8
KristopherWindsor
Posts: 2428
Joined: Jul 19, 2006 19:17
Location: Sunnyvale, CA
Contact:

Postby KristopherWindsor » Jan 03, 2008 23:22

Wow, that's the messiest QB code I ever saw. :-)

Can someone explain why the Peek / Poke / etc. features in FB did not already support this? Ie, how does the current Peek in FB different from the function posted here? I never used these things in QB, so I do not know how they work. ;-)
maddogg6
Posts: 824
Joined: Dec 07, 2005 22:58
Contact:

Postby maddogg6 » Jan 03, 2008 23:55

I was wondering about PEEK/POKE too - when it applies to Virtual memory..?? I mean, it makes complete sense to me when physical memory is all that is involved - but with Windows/VMemory - I shutter to think to even attempt using those commands now... but, all that may be from my probable mis-conceptions in same way... But then, segmented memory isnt something I completely understand anyway... ???

(btw, no reply needed - I have yet *needed* such things, tho - if direct access to hardware was allowed maybe I would have some use for those commands/functions -?? - heh I prolly just illuminated my true ignorance on memory vs IO mapping - oh well. But maybe something added to those pages in wikki could help idiots like myself - I said maybe/could :D )
anonymous1337
Posts: 5494
Joined: Sep 12, 2005 20:06
Location: California

Postby anonymous1337 » Jan 04, 2008 0:05

... What the hell? What is wrong with you? Why would you do such a thing? O.O;; Well if this works out, it would help people port code over from QB a lot easier ^^;;... I think.

Also, your sense of humor never ceases to astound me.
notthecheatr
Posts: 1759
Joined: May 23, 2007 21:52
Location: Cut Bank, MT
Contact:

Postby notthecheatr » Jan 04, 2008 0:40

Explanation for the uninformed ;)

PEEK and POKE naturally work differently than in QB because of the way memory is handled in 16-bit DOS. For one thing, it's 20-bit instead of 32-bit; for another, there's the funny business with using a segment and an address instead of just a flat literal physical address (since even though addresses are 20-bit only 16 bits may be handled by the processor at once). That's why in QB you have the monkey business with DEF SEG.

In 32-bit mode DEF SEG is illogical and pointless, but there are (probably two or three) die-hard QB fanatics who want it back, and for some this is practically the sole complaint FB raises. That's why v1ctor did this. It is, as he called it, a hack, but every improvement in compatibility between -lang qb and the real thing silences yet one more argument against using FB over QB, until at last the only argument left to make will be the one that goes "I want a blue text mode IDE and certain annoying bugs that occur every once in a while" and then we'll have everyone except the really die-hard grey-on-blue-text fanatics.

Long live FB!
cha0s
Site Admin
Posts: 5317
Joined: May 27, 2005 6:42
Location: Illinois
Contact:

Postby cha0s » Jan 04, 2008 7:16

Code: Select all

#define def fbrocks.


haha, that's brilliantly nasty man :P Great framework to expand off of though, each address just needs a case to emulate it, eh?
DrV
Site Admin
Posts: 2116
Joined: May 27, 2005 18:39
Location: Midwestern USA
Contact:

Postby DrV » Jan 04, 2008 15:25

Is calling screenunlock without screenlock allowed?
cha0s
Site Admin
Posts: 5317
Joined: May 27, 2005 6:42
Location: Illinois
Contact:

Postby cha0s » Jan 04, 2008 15:39

It should be okay iirc, there is a variable that is protected by mutex that tells if the screen is locked or not...
notthecheatr
Posts: 1759
Joined: May 23, 2007 21:52
Location: Cut Bank, MT
Contact:

Postby notthecheatr » Jan 04, 2008 15:48

DrV wrote:Is calling screenunlock without screenlock allowed?


That type of thing should always be allowed since otherwise we would have to keep track of it ourselves. I know MutexUnlock() can be called without MutexLock(); I would assume the same is true of ScreenUnlock() and ScreenLock().
anonymous1337
Posts: 5494
Joined: Sep 12, 2005 20:06
Location: California

Postby anonymous1337 » Jan 04, 2008 15:57

That type of thing should always be allowed since otherwise we would have to keep track of it ourselves.
*deallocates memory before allocating it*
DrV
Site Admin
Posts: 2116
Joined: May 27, 2005 18:39
Location: Midwestern USA
Contact:

Postby DrV » Jan 04, 2008 16:39

Why would you ever call MutexUnlock without first calling MutexLock? That totally destroys the whole point of using a mutex. I don't think ScreenUnlock should be called like this either (the screen could be modified while the background thread is copying it), so really in this case, ScreenLock should be called in each Poke before modifying the screen.
notthecheatr
Posts: 1759
Joined: May 23, 2007 21:52
Location: Cut Bank, MT
Contact:

Postby notthecheatr » Jan 04, 2008 22:22

anonymous1337 wrote:
That type of thing should always be allowed since otherwise we would have to keep track of it ourselves.
*deallocates memory before allocating it*


Memory is different, you have to get a handle for the memory you allocate (in this case, just a pointer). It's one thing to lock/unlock a handle, it's another to create/destroy. Obviously you can't destroy something you haven't created, but there's no reason you can't unlock something even if it's already unlocked. Not very useful, but it's possible.

I've actually used it before. In my interpreter (which runs in its own thread) I pause simply by entering a loop. When I enter the loop the mutex is already locked, but inside the loop it needs to be unlocked so the variable used to tell it to unpause (exit the loop) can be turned off. Then the mutex is locked again when we check it. After the loop, we unlock the mutex because if we never entered the loop it's still locked.
DrV
Site Admin
Posts: 2116
Joined: May 27, 2005 18:39
Location: Midwestern USA
Contact:

Postby DrV » Jan 04, 2008 23:15

But in that case you are still only ever calling MutexUnlock on a mutex that is *locked* - not calling MutexUnlock on an already unlocked mutex. The lock and unlock don't have to be nearby in the code or anything like that (although they have to be in the same thread).
Dav
Posts: 28
Joined: Jul 21, 2005 13:41
Contact:

cool

Postby Dav » Jan 05, 2008 1:23

Well, this kind of hack has my attention. I've been unable to compile several QB text mode apps in FB easily because they depend so much on DEF SEF & POKE to work. Like this old binary file viewer here which uses POKE to display every character on the screen safely (PRINT can't do it).

I find it amazing how FB can work around such a limitations with a small hack. Haven't d/l the latest version yet. Will do it now.

- Dav
v1ctor
Site Admin
Posts: 3801
Joined: May 27, 2005 8:08
Location: SP / Bra[s]il
Contact:

Postby v1ctor » Jan 05, 2008 11:17

I checked the screenunlock code and it seemed safe, at least on Windows, i dunno on Linux (not sure if the gfxlib will ever allocate device/system surfaces that really need to be locked).

The was_this_really_locked test was added by Angelo ages ago by request. If they are using an IDE (and hitting F5 on every line typed :) and running their games in full-screen, a unpaired screenlock/screenunlock would cause a lock up.

Return to “Tips and Tricks”

Who is online

Users browsing this forum: No registered users and 4 guests