Jump to content
IGNORED

Colossal Cave Adventure diary


vprette

Recommended Posts

By the way, the build tools are available in Cygwin. In any case, you can get the free Active Perl.

 

I've sometimes had problems with Cygwin. Their whole "cygdrive" fiction, trying to transform Windows paths into Unix paths and flipping slashes around really messes things up. Dunno if they've stopped doing that, but that was the reason I left Cygwin. That said, I have plenty of happy coworkers, so maybe the problem's just me.

 

The ActiveState Perl is what I use. It worked first-time with this script. It may not be the world's sexiest build script, but it gets the job done.

Link to comment
Share on other sites

Oops, one tiny, tiny bug crept in. In "build.pl", delete this line (near the end of the file):

 

binmode($cfh);

 

That's not supposed to be there. The only real effect it has is to generate a .cfg file with *nix-style newlines instead of Windows-style on a Windows machine. jzIntv doesn't care, but notepad does. **shrug**

Link to comment
Share on other sites

By the way, the build tools are available in Cygwin. In any case, you can get the free Active Perl.

 

I've sometimes had problems with Cygwin. Their whole "cygdrive" fiction, trying to transform Windows paths into Unix paths and flipping slashes around really messes things up. Dunno if they've stopped doing that, but that was the reason I left Cygwin. That said, I have plenty of happy coworkers, so maybe the problem's just me.

 

The ActiveState Perl is what I use. It worked first-time with this script. It may not be the world's sexiest build script, but it gets the job done.

 

Cygwin still does that. "/drives/c/program\ files/". Yup, it's rather silly. I avoid any problems by "thinking UNIX" when using Cygwin, and not interacting with any Windows-ish parts. In essence, just rooting somewhere off the C:\ drive. I also set up symlinks and environment variables to my common directories so that I don't have to remember, is it "c:/" or "/drives/c/" or "/mnt/drives/c/"?

 

-dZ.

Edited by DZ-Jay
Link to comment
Share on other sites

Cygwin still does that. "/drives/c/program\ files/". Yup, it's rather silly. I avoid any problems by "thinking UNIX" when using Cygwin, and not interacting with any Windows-ish parts. In essence, just rooting somewhere off the C:\ drive. I also set up symlinks and environment variables to my common directories so that I don't have to remember, is it "c:/" or "/drives/c/" or "/mnt/drives/c/"?

 

Yuck. :-) You know, it wasn't too much of a problem for me, actually. But, when I was building jzIntv with Cygwin, explaining to people how to tell jzIntv where to find their games made me feel like a certain textile-challenged emperor. As it is, the command line already turns a few people off. The cygdrive thing, though, would be enough for many folk to argue jzIntv didn't actually have a Windows port, but rather was a Unix program that happens to run in Windows somehow, which isn't the case at all.

 

Anyway... getting a little off topic. The new perl build script at least limits the dependencies to one program that has a solid Windows port and is easy to install. If we end up needing more build shenanigans, we can fold it into that perl script.

Edited by intvnut
Link to comment
Share on other sites

Ok, just a minor update. I added some word-wrapping logic (which makes the screen much more readable) and a *More* facility to pause after each screenful. Both make it much easier to playtest. I also added the build.pl fix, and renamed 26K to 34K as it should be.

 

Hello

if I see right, the text now is organised to never cut the words between two rows, right?

this is very good....

 

In fact, to move your code into my scheleton seems very complex for me.... so I may consider keeping my code from title to intro and hidden novel up to level 1 when your code will take place..... to do this and keep my original screen layout, I need to:

 

1 - reintroduce the custom font

2 - skip the initial message with request for instruction (those are exaclty the same written in the manual)

3 - use the circle/arrow instead of "more" as indicating the text will continue. When 2 pages need to be shown because of a very long text, there will be 2 yellow arrows

4 - handle the command from controller (8 direction + up/down + pause music)

 

in whic .asm I have to deep to make those changes?

1 -> "ansi.asm"?

2 -> ?

3 -> ?

4 -> ?

 

page_state_screen.JPG

Link to comment
Share on other sites

Ok, just a minor update. I added some word-wrapping logic (which makes the screen much more readable) and a *More* facility to pause after each screenful. Both make it much easier to playtest. I also added the build.pl fix, and renamed 26K to 34K as it should be.

 

Hello

if I see right, the text now is organised to never cut the words between two rows, right?

this is very good....

 

In fact, to move your code into my scheleton seems very complex for me.... so I may consider keeping my code from title to intro and hidden novel up to level 1 when your code will take place..... to do this and keep my original screen layout, I need to:

 

1 - reintroduce the custom font

2 - skip the initial message with request for instruction (those are exaclty the same written in the manual)

3 - use the circle/arrow instead of "more" as indicating the text will continue. When 2 pages need to be shown because of a very long text, there will be 2 yellow arrows

4 - handle the command from controller (8 direction + up/down + pause music)

 

in whic .asm I have to deep to make those changes?

1 -> "ansi.asm"?

2 -> ?

3 -> ?

4 -> ?

 

page_state_screen.JPG

 

Ok, let's wade through all this. First off, I figure you'd just completely replace ansi.asm with something to handle output differently. I just used it as a quick and dirty starting point because I had it handy. All text output eventually goes through "putchar", and that's defined in puts.asm. Whatever output mechanism you define, you'd hook it up there.

 

For example, one idea would be to have it print everything to a virtual "frame buffer" in memory, and then you could use the disc to scroll around. That code would also handle the "more" facility and so on. The best strategy would be to slurp up all input to this off-screen buffer until the game gets to a point where it's waiting for input, and then your display code takes over presenting it to the player. All of the display update and such would then be handled by tasks you hang off of P Machinery. You hook the two together through putchar.

 

Changing the font over happens "automagically" when you replace ansi.asm with your own code. :-)

 

User input currently comes in at two places: getline.asm and more.asm. The second one, "more.asm", would go away once you have the more sophisticated output display. "getline" is where you know the engine has switched from output to input mode. When the game reaches getline, you can let the user page through the display, scrolling up and down if they like. This is also where you'd inject keystrokes for commands triggered from the controller keypad.

 

I would suggest removing the call to SCAN_ECS from getline, and change it to a more generic "getkey" function. Have a separate P Machinery task scan the keyboard into an input buffer that getkey consults. If someone hits the controller disc, you can flush the keyboard buffer and replace it with the user's command. Also insert an "ESC" at the start of a controller-triggered command if it's a verb, and that will cause getline to back out anything else the user has typed. (Try it -- ESC already does this actually.) Something like this (untested):

 

typeahead   EQU         20

           BYTEARRAY   kb,     typeahead   ;; allow some typeahead
           BYTEVAR     kb.head             ;; first char to return to player
           BYTEVAR     kb.tail             ;; most recently queued keystroke

;; ======================================================================== ;;
;;  GETKEY  Read the next key of input from the player.                     ;;
;; ======================================================================== ;;
getkey      PROC
           PSHR        R5

           ; Wait for a key to show up.
@@wait:     ;CALL       yield               ; allow time slicing properly
           MVI         kb.head,    R1
           CMP         kb.tail,    R1
           BEQ         @@wait

           INCR        R1                  ; consume keystroke
           CMPI        #typeahead, R1      ; wrap around end of buffer
           BLT         @@no_wrap     
           CLRR        R1
@@no_wrap   MVO         R1,         kb.head

           ADDI        #kb,        R1
           MVI@        R1,         R0      ; get keystroke
           PULR        PC
           ENDP

;; ======================================================================== ;;
;;  FLUSHK  Flush the keyboard buffer                                       ;;
;; ======================================================================== ;;
flushk      PROC
           CLRR        R0
           MVO         R0,         kb.head
           MVO         R0,         kb.tail
           JR          R5
           ENDP

;; ======================================================================== ;;
;;  PUTKEY  Put a keystroke into the keyboard buffer.                       ;;
;; ======================================================================== ;;
putkey      PROC

           ; Is there room?
           MVI         kb.tail,    R1
           SUB         kb.head,    R1
           BPL         @@no_wrap1
           ADDI        #typeahead, R1      ; handle tail-wrap

@@no_wrap1  CMPI        #typeahead, R1
           BGE         @@leave             ; no room


           MVI         kb.tail,    R1
           INCR        R1
           CMPI        #typeahead, R1
           BLT         @@no_wrap2
           CLRR        R1
@@no_wrap2  MVO         R1,         kb.tail ; Make room for keystroke

           ADDI        #kb,        R1
           MVO@        R0,         R1      ; put in keystroke

@@leave     JR          R5
           ENDP

;; ======================================================================== ;;
;;  KEYSCAN Keyboard scanning task                                          ;;
;; ======================================================================== ;;
keyscan     PROC
           PSHR        R5

           CALL        SCAN_KBD
           CMPI        #KEY.NONE,  R0
           BEQ         @@no_key

           PULR        R5
           B           putkey              ; queue the keystroke

@@nokey     PULR        PC
           ENDP


;; ======================================================================== ;;
;;  KEYPAD  Handle numeric keys on controller keypad.                       ;;
;;          This function goes in the controller dispatch table.            ;;
;; ======================================================================== ;;
keypad      PROC
           ANDI        #$FF,       R2      ; ignore controller number
           CMPI        #$80,       R2      ; ignore key release events
           BLT         @@press
           JR          R5

@@press:    PSHR        R5

           MOVR        R2,         R1
           ADDI        #@@cmdptr,  R1      ; Command string pointer table
           MVI@        R1,         R4      ; Is it a 'macro'?
           TSTR        R4
           BNEQ        @@do_macro

           ; put code here to process R1 if it isn't a macro command
           ; ie. pause/unpause music, etc.

@@done:     PULR        PC

@@do_macro:
           CALL        flushk              ; flush queued keystrokes
           MVII        #KEY.ESC,   R0
           CALL        putkey              ; queue an ESC to flush getline

@@keyloop:  MVI@        R4,         R0
           TSTR        R0
           BEQ         @@done

           CALL        putkey
           B           @@keyloop

;                       0 
@@cmdptr    DECLE       @@look;
;                       1       2       3
           DECLE       @@get,  @@n,    @@drop
;                       4       5       6
           DECLE       @@w,    @@out,  @@e
;                       7       8       9
           DECLE       0,      @@s,    0           ; 7 and 9 -- leave open?
;                       C               E
           DECLE       0,              0           ; C and E -- leave open?

@@look:     STRING      "look",  $A, 0
@@get:      STRING      "get ",      0
@@n:        STRING      "north", $A, 0
@@drop:     STRING      "drop ",     0
@@w:        STRING      "west",  $A, 0
@@out:      STRING      "out",   $A, 0        
@@e:        STRING      "east",  $A, 0
@@s:        STRING      "south", $A, 0

           ENDP

 

That integrates the two input streams into one. Then in getline, instead of calling SCAN_ECS, you just call getkey:

 

           CALL        SCAN_KBD
           CMPI        #KEY.NONE,  R0
           BEQ         @@loop

 

becomes

 

           CALL        getkey

 

 

The initial welcome message gets triggered by this code in adv.asm:

       MVII    #INITIAL,   R0
       CALL    process

@@forever:
       MVII    #REPEAT,    R0
       CALL    process
       B       @@forever

 

That part gets replaced entirely when you pull everything into your framework. The adv.asm file needs to go away--it's just a placeholder since I didn't build this directly on P Machinery. This loop should become a P Machinery task of sorts when you're in the "in game" state:

 

@@forever:
       MVII    #REPEAT,    R0
       CALL    process
       B       @@forever

 

 

Got all that?

getkey.asm

Link to comment
Share on other sites

Attached is a quick stab at an ansi.asm replacement. It also defines control characters 16 thru 31 to change foreground colors. You'd call 'refresh' from an interrupt context to keep the display refreshed. I haven't tested this code -- I don't even know if it assembles correctly -- but I thought I'd at least get this stab down in writing.

 

To integrate it w/ CC, you'd delete the 'putchar' function from puts.asm. Also, you need to change all references to A_r and A_c in the code to vfb.r and vfb.c. The 'more' functionality will be broken for now, but that can be fixed later.

 

The main idea of this code is that you can scroll the screen by simply updating vfb.tos and setting vfb.do_refr. The next frame, the screen will scroll to the new line. So, you can write your disc handler to manipulate the scroll position, and allow the player to scroll back through several pages of text. (Right now I've set it to 64 lines, which is just over 5 screenfuls of text.) This should fit comfortably in JLP RAM alongside the other buffers.

vfb.asm

Link to comment
Share on other sites

Just a quick bit of status from my end: I've updated the engine to "yield" periodically while running as a self-triggering task via RUNQ, and have converted it to use the VFB approach for text instead of the ANSI interpreter. The "more" functionality is absent in this version, but we can add it back later.

 

At this point, I probably need to start integrating it with P Machinery. The core engine is pretty much ready for that now.

 

EDIT: Valter, if you send me the code you have, I can try integrating mine with yours and sending you back the result. You can email it to me or PM me if you prefer not to post it here.

adv5_intv.zip

Edited by intvnut
Link to comment
Share on other sites

Just a quick bit of status from my end: I've updated the engine to "yield" periodically while running as a self-triggering task via RUNQ, and have converted it to use the VFB approach for text instead of the ANSI interpreter. The "more" functionality is absent in this version, but we can add it back later. At this point, I probably need to start integrating it with P Machinery. The core engine is pretty much ready for that now. EDIT: Valter, if you send me the code you have, I can try integrating mine with yours and sending you back the result. You can email it to me or PM me if you prefer not to post it here.

 

I'm sorry I haven't been able to help much with the integration, I've been busy with many things at the same time and I haven't even had the chance of running your engine.

 

Joe, let me know if you have any questions regarding P-Machinery, of if I can help in any way.

 

-dZ.

Link to comment
Share on other sites

BTW, one nifty feature that comes with the latest code drop above is "typeahead": You can continue to type while the engine "thinks", and it'll buffer up to 20 keystrokes.

 

The code uses a (bugfixed) version of the getkey code I posted earlier. Once it drops into P Machinery and we hook up a controller scanner, then getkey can return keystrokes from both the hand controller and the ECS keyboard as one unified input stream.

Link to comment
Share on other sites

I'm sorry I haven't been able to help much with the integration, I've been busy with many things at the same time and I haven't even had the chance of running your engine.

 

Joe, let me know if you have any questions regarding P-Machinery, of if I can help in any way.

 

Sure thing, and I understand. I believe Valter has already been hanging some code off of P-Machinery, so I'd like to start with whatever he has. I downloaded a version from the other thread, but I think I should set to modifying whatever Valter already has to avoid further integration issues.

 

One thing I did run into: I can't just trivially get rid of the introduction screen. We'll have to modify the actual game database (the .d files), since all the variable initialization occurs in the same execution stream that prints out the welcome message. Either that, or I'll have to run the init once, capture the memory image, and bypass the intro that way. That may actually be better, since it'll speed up "boot".

Link to comment
Share on other sites

Joe,

 

Does your engine include its own keyboard input processor, or were you expecting to hook it up to P-Machinery?

 

P-Machinery offers hooks for input processors. They just need to be defined in the state dispatch table, and that's all.

 

My intention was to create a generic getKey() keyboard handler that reads the keyboard input, and then a keyboard input processor which is just a wrapper to it with the purpose of triggering key-down and key-up events.

 

A similar architecture exists for the game controllers in P-Machinery, where there's an internal "getKey()" function that handles de-bouncing and de-noising the input signal; and individual input processors that wrap around it, decode the results, and trigger the appropriate events.

 

However, for this game perhaps it'll be easier to just ignore the future re-use of this architecture and write an all-in-one input processor for your getKey() function that will handle the buffer and all.

 

-dZ.

Link to comment
Share on other sites

Once it drops into P Machinery and we hook up a controller scanner, then getkey can return keystrokes from both the hand controller and the ECS keyboard as one unified input stream.

 

The architecture in P-Machinery is similar to your SCANHAND routine, in that it decodes the input and triggers events. The event handlers are the game-specific parts, which in this case should be the ones handling the buffer. This allows for other applications using the keyboard without buffering.

 

Then for this input processor you'll have events for:

  • Keypad
  • Action buttons
  • Disc
  • ECS Keyboard

Edited by DZ-Jay
Link to comment
Share on other sites

Joe,

 

Does your engine include its own keyboard input processor, or were you expecting to hook it up to P-Machinery?

 

P-Machinery offers hooks for input processors. They just need to be defined in the state dispatch table, and that's all.

 

My intention was to create a generic getKey() keyboard handler that reads the keyboard input, and then a keyboard input processor which is just a wrapper to it with the purpose of triggering key-down and key-up events.

 

A similar architecture exists for the game controllers in P-Machinery, where there's an internal "getKey()" function that handles de-bouncing and de-noising the input signal; and individual input processors that wrap around it, decode the results, and trigger the appropriate events.

 

However, for this game perhaps it'll be easier to just ignore the future re-use of this architecture and write an all-in-one input processor for your getKey() function that will handle the buffer and all.

 

-dZ.

 

dZ,

 

For the ECS keyboard, I'm expecting something to call "keyscan" periodically to scan the ECS keyboard and drop keystrokes into my typeahead buffer. The rest of the game calls "getkey" which consumes from that buffer. (I also just modified getkey to pump keyscan if the keyboard buffer is empty.) I'd suggest keyscan get called, say, once per frame. In my simple driver program (adv.asm), I just call it from the ISR.

 

For controller input, I'm expecting something else to call a controller scanner and do decode/dispatch, like I've done in the past with SCANHAND / QTASK. For controller events that translate into keystrokes, I've written (and posted upthread) code that receives those events and expands them into the typeahead buffer. That particular handler expects SCANHAND-style events, but it can be adapted to whatever controller scanner you like. The main thing is that it wants to be called when it sees a keypad key that it has to process.

 

Colossal Cave doesn't need keydown/keyup, just keydown. (I figure autorepeat isn't really useful in this game.) So if your input processor framework just calls keyscan, that's sufficient. The keystrokes, though, won't generate events into the event queue. Rather, they go straight to my typeahead buffer.

 

For reference, here's all the keyboard logic. SCAN_KBD is the keyboard scanner I posted on the ECS keyboard tutorial.

 

typeahead   EQU         20

           BYTEARRAY   kb,     typeahead   ;; allow some typeahead
           BYTEVAR     kb.head             ;; first char to return to player
           BYTEVAR     kb.tail             ;; most recently queued keystroke

;; ======================================================================== ;;
;;  GETKEY  Read the next key of input from the player.                     ;;
;; ======================================================================== ;;
getkey      PROC
           PSHR        R5

           ; Wait for a key to show up.
           B           @@first
@@wait:     CALL        yield               ; allow time slicing properly
           CALL        keyscan             ; why not?
@@first:    MVI         kb.head,    R1
           CMP         kb.tail,    R1
           BEQ         @@wait

           INCR        R1                  ; consume keystroke
           CMPI        #typeahead, R1      ; wrap around end of buffer
           BLT         @@no_wrap     
           CLRR        R1
@@no_wrap   MVO         R1,         kb.head

           ADDI        #kb,        R1
           MVI@        R1,         R0      ; get keystroke
           PULR        PC
           ENDP

;; ======================================================================== ;;
;;  FLUSHK  Flush the keyboard buffer                                       ;;
;; ======================================================================== ;;
flushk      PROC
           CLRR        R0
           MVO         R0,         kb.head
           MVO         R0,         kb.tail
           JR          R5
           ENDP

;; ======================================================================== ;;
;;  PUTKEY  Put a keystroke into the keyboard buffer.                       ;;
;; ======================================================================== ;;
putkey      PROC

           ; Is there room?
           MVI         kb.tail,    R1
           SUB         kb.head,    R1
           BPL         @@no_wrap1
           ADDI        #typeahead, R1      ; handle tail-wrap

@@no_wrap1  CMPI        #typeahead, R1
           BGE         @@leave             ; no room


           MVI         kb.tail,    R1
           INCR        R1
           CMPI        #typeahead, R1
           BLT         @@no_wrap2
           CLRR        R1
@@no_wrap2  MVO         R1,         kb.tail ; Make room for keystroke

           ADDI        #kb,        R1
           MVO@        R0,         R1      ; put in keystroke

@@leave     JR          R5
           ENDP

;; ======================================================================== ;;
;;  KEYSCAN Keyboard scanning task                                          ;;
;; ======================================================================== ;;
keyscan     PROC
           PSHR        R5

           CALL        SCAN_KBD
           CMPI        #KEY.NONE,  R0
           BEQ         @@no_key

           PULR        R5
           B           putkey              ; queue the keystroke

@@no_key    PULR        PC
           ENDP

 

And here's the event handler that converts controller input to a sequence of keystrokes, which I called a "macro" below. My expectation was that whatever decodes the controller would eventually dispatch to this to handle keypad events.

 

;; ======================================================================== ;;
;;  KEYPAD  Handle numeric keys on controller keypad.                       ;;
;;          This function goes in the controller dispatch table.            ;;
;; ======================================================================== ;;
keypad      PROC
           ANDI        #$FF,       R2      ; ignore controller number
           CMPI        #$80,       R2      ; ignore key release events
           BLT         @@press
           JR          R5

@@press:    PSHR        R5

           MOVR        R2,         R1
           ADDI        #@@cmdptr,  R1      ; Command string pointer table
           MVI@        R1,         R4      ; Is it a 'macro'?
           TSTR        R4
           BNEQ        @@do_macro

           ; put code here to process R1 if it isn't a macro command
           ; ie. pause/unpause music, etc.

@@done:     PULR        PC

@@do_macro:
           CALL        flushk              ; flush queued keystrokes
           MVII        #KEY.ESC,   R0
           CALL        putkey              ; queue an ESC to flush getline

@@keyloop:  MVI@        R4,         R0
           TSTR        R0
           BEQ         @@done

           CALL        putkey
           B           @@keyloop

;                       0 
@@cmdptr    DECLE       @@look;
;                       1       2       3
           DECLE       @@get,  @@n,    @@drop
;                       4       5       6
           DECLE       @@w,    @@out,  @@e
;                       7       8       9
           DECLE       0,      @@s,    0           ; 7 and 9 -- leave open?
;                       C               E
           DECLE       0,              0           ; C and E -- leave open?

@@look:     STRING      "look",  $A, 0
@@get:      STRING      "get ",      0
@@n:        STRING      "north", $A, 0
@@drop:     STRING      "drop ",     0
@@w:        STRING      "west",  $A, 0
@@out:      STRING      "out",   $A, 0        
@@e:        STRING      "east",  $A, 0
@@s:        STRING      "south", $A, 0

           ENDP

 

Speaking of macros, we could add macros on the ECS keyboard side too. If you see CTRL+letter, it's pretty easy to expand that out into a word, which might make typing common verbs a little nicer.

 

Link to comment
Share on other sites

Once it drops into P Machinery and we hook up a controller scanner, then getkey can return keystrokes from both the hand controller and the ECS keyboard as one unified input stream.

 

The architecture in P-Machinery is similar to your SCANHAND routine, in that it decodes the input and triggers events. The event handlers are the game-specific parts, which in this case should be the ones handling the buffer. This allows for other applications using the keyboard without buffering.

 

Then for this input processor you'll have events for:

  • Keypad
  • Action buttons
  • Disc
  • ECS Keyboard

 

It wouldn't be hard to replace my keyscan with your ECS keyboard event generator. Just have the event handler call "putkey" for each keydown event.

Edited by intvnut
Link to comment
Share on other sites

Once it drops into P Machinery and we hook up a controller scanner, then getkey can return keystrokes from both the hand controller and the ECS keyboard as one unified input stream.

 

The architecture in P-Machinery is similar to your SCANHAND routine, in that it decodes the input and triggers events. The event handlers are the game-specific parts, which in this case should be the ones handling the buffer. This allows for other applications using the keyboard without buffering.

 

Then for this input processor you'll have events for:

  • Keypad
  • Action buttons
  • Disc
  • ECS Keyboard

 

It wouldn't be hard to replace my keyscan with your ECS keyboard event generator. Just have the event handler call "putkey" for each keydown event.

 

In fact, it would look almost identical to the keypad handler I posted above. It could sort keystrokes into "macro" and "not macro", even. You might even be able to merge them into one function that handles both, just with two entry points so that you can shift the event number ranges.

Link to comment
Share on other sites

Once it drops into P Machinery and we hook up a controller scanner, then getkey can return keystrokes from both the hand controller and the ECS keyboard as one unified input stream.

 

The architecture in P-Machinery is similar to your SCANHAND routine, in that it decodes the input and triggers events. The event handlers are the game-specific parts, which in this case should be the ones handling the buffer. This allows for other applications using the keyboard without buffering.

 

Then for this input processor you'll have events for:

  • Keypad
  • Action buttons
  • Disc
  • ECS Keyboard

 

It wouldn't be hard to replace my keyscan with your ECS keyboard event generator. Just have the event handler call "putkey" for each keydown event.

 

I guess I'm muddling the waters by using terms that collide with yours. Your "getkey" function is the one that fetches from the buffer, right? My "getkey" function (which I admit is probably a misnomer) is the one that polls the controller.

 

In your scenario, it'll also write into the typeahead buffer, so it would be your "keyscan" routine.

 

If I understand your approach, the controller model should be different from the keyboard model, because both use cases are different. The former is event based, and the latter depends on a typeahead buffer that needs to get read (and thus, flushed) by the game periodically.

 

Is this right?

 

-dZ.

Link to comment
Share on other sites

For the ECS keyboard, I'm expecting something to call "keyscan" periodically to scan the ECS keyboard and drop keystrokes into my typeahead buffer. The rest of the game calls "getkey" which consumes from that buffer. (I also just modified getkey to pump keyscan if the keyboard buffer is empty.) I'd suggest keyscan get called, say, once per frame. In my simple driver program (adv.asm), I just call it from the ISR.

 

Just a note on this: By default, P-Machinery calls the assigned input processor for the current state on the "idle" task (when the queue is empty).

 

When I say "by default," I mean that's how it was written. Since the game loop is intended to run once per ISR, this resulted in at least 60 Hz resolution in the worse case scenario, and a much higher resolution in the normal cases. For twitchy arcade games like Pac-Man and Christmas Carol, this was a good thing.

 

I don't expect it should be such a big deal to change this to call the input processor at the end of the game loop from the ISR.

 

-dZ.

Link to comment
Share on other sites

It wouldn't be hard to replace my keyscan with your ECS keyboard event generator. Just have the event handler call "putkey" for each keydown event.

 

I guess I'm muddling the waters by using terms that collide with yours. Your "getkey" function is the one that fetches from the buffer, right? My "getkey" function (which I admit is probably a misnomer) is the one that polls the controller.

 

In your scenario, it'll also write into the typeahead buffer, so it would be your "keyscan" routine.

 

If I understand your approach, the controller model should be different from the keyboard model, because both use cases are different. The former is event based, and the latter depends on a typeahead buffer that needs to get read (and thus, flushed) by the game periodically.

 

Is this right?

 

It's not hard to adapt to the event-based model. It is, in effect, what I have now, I've just short circuited the event queue. If you've already got an input event generator, then the keydown event handler for both the controller keypad and the ECS keyboard are pretty much identical. Both will (usually) result in one or more calls to "putkey" to insert keystrokes into my typeahead buffer.

 

I say "usually" because Valter does want to use certain keypad inputs for non-keystroke things, but that's OK. We may even want ECS keyboard bindings for special actions too. So really, the use model for both the keypad and keyboard are more similar than different.

 

So yeah, you can replace keyscan with whatever you had in mind for ECS keyboard scanning, and just register a keyboard event handler which looks almost identical (and may even share 99% of the code with) the keypad event handler I have above.

Link to comment
Share on other sites

For the ECS keyboard, I'm expecting something to call "keyscan" periodically to scan the ECS keyboard and drop keystrokes into my typeahead buffer. The rest of the game calls "getkey" which consumes from that buffer. (I also just modified getkey to pump keyscan if the keyboard buffer is empty.) I'd suggest keyscan get called, say, once per frame. In my simple driver program (adv.asm), I just call it from the ISR.

 

Just a note on this: By default, P-Machinery calls the assigned input processor for the current state on the "idle" task (when the queue is empty).

 

When I say "by default," I mean that's how it was written. Since the game loop is intended to run once per ISR, this resulted in at least 60 Hz resolution in the worse case scenario, and a much higher resolution in the normal cases. For twitchy arcade games like Pac-Man and Christmas Carol, this was a good thing.

 

I don't expect it should be such a big deal to change this to call the input processor at the end of the game loop from the ISR.

 

-dZ.

 

One downside of the "yield" strategy is that the task queue is never actually empty, since it always immediately queues a "resume" task before returning to RUNQ. I guess a yield should also trigger the idle tasks to run? That should be easy enough. Instead of returning to RUNQ.loop, it can return to RUNQ.bktsk.

Link to comment
Share on other sites

For the ECS keyboard, I'm expecting something to call "keyscan" periodically to scan the ECS keyboard and drop keystrokes into my typeahead buffer. The rest of the game calls "getkey" which consumes from that buffer. (I also just modified getkey to pump keyscan if the keyboard buffer is empty.) I'd suggest keyscan get called, say, once per frame. In my simple driver program (adv.asm), I just call it from the ISR.

 

Just a note on this: By default, P-Machinery calls the assigned input processor for the current state on the "idle" task (when the queue is empty).

 

When I say "by default," I mean that's how it was written. Since the game loop is intended to run once per ISR, this resulted in at least 60 Hz resolution in the worse case scenario, and a much higher resolution in the normal cases. For twitchy arcade games like Pac-Man and Christmas Carol, this was a good thing.

 

I don't expect it should be such a big deal to change this to call the input processor at the end of the game loop from the ISR.

 

-dZ.

 

One downside of the "yield" strategy is that the task queue is never actually empty, since it always immediately queues a "resume" task before returning to RUNQ. I guess a yield should also trigger the idle tasks to run? That should be easy enough. Instead of returning to RUNQ.loop, it can return to RUNQ.bktsk.

 

Of course, if I do that, I'll have to take this out of yield:

           MVI     TSKQHD,         R0
           CMP     TSKQTL,         R0
           BNEQ    @@doit
           JR      R5

That short-circuits the yield if no other task has been queued.

 

See, while 'process' is running, the queue will be empty, sure, but it always re-queues itself before returnning, so RUNQ never sees the queue empty. :-)

Link to comment
Share on other sites

For the ECS keyboard, I'm expecting something to call "keyscan" periodically to scan the ECS keyboard and drop keystrokes into my typeahead buffer. The rest of the game calls "getkey" which consumes from that buffer. (I also just modified getkey to pump keyscan if the keyboard buffer is empty.) I'd suggest keyscan get called, say, once per frame. In my simple driver program (adv.asm), I just call it from the ISR.

 

Just a note on this: By default, P-Machinery calls the assigned input processor for the current state on the "idle" task (when the queue is empty).

 

When I say "by default," I mean that's how it was written. Since the game loop is intended to run once per ISR, this resulted in at least 60 Hz resolution in the worse case scenario, and a much higher resolution in the normal cases. For twitchy arcade games like Pac-Man and Christmas Carol, this was a good thing.

 

I don't expect it should be such a big deal to change this to call the input processor at the end of the game loop from the ISR.

 

-dZ.

 

One downside of the "yield" strategy is that the task queue is never actually empty, since it always immediately queues a "resume" task before returning to RUNQ. I guess a yield should also trigger the idle tasks to run? That should be easy enough. Instead of returning to RUNQ.loop, it can return to RUNQ.bktsk.

 

Of course, if I do that, I'll have to take this out of yield:

		MVI	 TSKQHD,		 R0
		CMP	 TSKQTL,		 R0
		BNEQ	@@doit
		JR	  R5

That short-circuits the yield if no other task has been queued.

 

See, while 'process' is running, the queue will be empty, sure, but it always re-queues itself before returnning, so RUNQ never sees the queue empty. :-)

 

I get it. The queue is not really empty from the RUNQ perspective, but yield calls the idle task before queueing itself.

 

I wonder if it'll be better if P-Machinery were to just queue the input processor at the bottom of the state ISR, like it does with DOTIMER()...

 

Your thoughts?

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...