Re: Work on improving/expanding 'HLA Adventure'

From: Frank Kotler (fbkotler_at_comcast.net)
Date: 12/07/04


Date: Tue, 07 Dec 2004 04:46:21 GMT

nbaker2328@charter.net wrote:

Some good ideas here. I particularly second giving the variables more
descriptive names - "full talking names", Betov calls 'em.

[snip]
...
> begin AdventureDesigner;
>
> stdout.put("----------------------------" nl);
> stdout.put(" AdventureDesigner" nl);
> stdout.put(" version 0.01" nl);
> stdout.put(" by" nl);
> stdout.put(" a.l.a. nuts" nl);
> stdout.put(" December 2004 A.D." nl);
> stdout.put("written for Linux & Windows");
> stdout.put("----------------------------" nl nl);
> stdout.put("<tap a key>" nl); //or whack the ANY key.
> stdin.readLn();
> stdin.getc();

But here we're back to version 1 of Paul's game! Why are you using
"stdout.put"? (as opposed to "stdout.puts"?) Why are you calling the OS
for every single line of text? The "any" key has to be "Enter" for this
to work, and with *both* "readln" and "getc", they're going to have to
hit it twice. Better change the message to tell 'em "Hit Enter Twice To
Continue" :)

I think I'd put that whole glob of text on one "stdout.puts". Then, if
you think it's worth it, write my own portable (using conditional
assembly - it's prettier to hide this away like the Std Lib does)
"really_get_just_one_key" routine. Like Randy suggested to my complaints
about the "readfile" not returning bytes read...

I've got such a thing - in Nasm syntax - as part of a "game" (just a
demonstration of a coding approach that Beth posted for Paul to
consider, some time ago) which has been translated *from* HLA to Nasm...
You're better off to start from Beth's HLA code if you're thinking of
reconsidering her proposed idea (which you should, IMO) - I'll stick
just the routines in question at the end of this.

The ReactOS (or its clone) version is swiped from Herbert's translation
of Beth's demo into *his* syntax... and translated to Nasm. The Linux
version is loosely based on Brian Raiter's routine in "snake" - asmutils
from http://linuxassembly.org - but both are altered enough so any bugs
are probably mine :)

Beth's original code is available from the Yahoo aoaprogramming "files"
section... or she or I can post it again here. I'm sure she'd be pleased
if we stopped translating it around from syntax to syntax and added some
new data to it as she intended originally :)

You might not think these are worthwhile, just for the "hit any key"
situation, but it might give you more flexibility in how - and when -
you parse other user input, too...

> What was the size of that record? Was it
> // a 38? A 45? Or a full LP? ;-)

It was one of those 7 inch 78's, yellow, with "The Ugly Duckling" on it
:)

Best,
Frank

P.S. On second thought, I'll append the whole program again - there are
some "%defines" scattered around, so it would be a pain to split up.
These would mostly not be needed if translated back to HLA - but I think
the "getc" is better'n HLA's. Anybody able to actually *try* this on
ReactOS?

; nasm -f elf prog.asm
; ld -s -o prog prog.o

; or for ReactOS... (will work on Windows, while we wait)
; nasm -f obj prog.asm -D_ROS_
; alink -oPE -subsys console prog.obj

    ; "-f obj" format defaults to 16-bits
    ; make sure Nasm knows we're doing 32-bit!
    ; not needed for "-f elf", but doesn't hurt
section .text use32 class=code
section .data use32
section .const use32

    ; redeclaring the sections retains the "attributes",
    ; but doesn't "carry 'em around". This keeps Nasm
    ; from whimpering when "struc" is used.
section .text
section .data
section .const

;----- this could go in a header file ------
    ; our basic room data structure
struc room_data
     NorthPtr resd 1 ; pointer to ROOM_DATA;
     SouthPtr resd 1 ; pointer to ROOM_DATA;
     EastPtr resd 1 ; pointer to ROOM_DATA;
     WestPtr resd 1 ; pointer to ROOM_DATA;
     UpPtr resd 1 ; pointer to ROOM_DATA;
     DownPtr resd 1 ; pointer to ROOM_DATA;
     RoomName resd 1 ; pointer to string;
     Description resd 1 ; pointer to string;
endstruc

    ; a macro to fill the room data structure easily
%macro room_init 8+
section .const
%%nme db %7
      db 0
%%txt db %8
      db 0
section .data
    dd %1
    dd %2
    dd %3
    dd %4
    dd %5
    dd %6
    dd %%nme
    dd %%txt
%endm

    ; a macro to calculate pointers to room data
%define RoomPtr(x) rooms + (x) * room_data_size
;----------------------------------------------

;---------------------------------------------=
    ; some miscellaneous equates

%ifdef _ROS_

%define NL 13, 10
%define STDIN -10 ; you've *gotta* be kidding!
%define STDOUT -11

extern ReadFile
import ReadFile kernel32.dll

extern WriteFile
import WriteFile kernel32.dll

extern GetStdHandle
import GetStdHandle kernel32.dll

extern GetConsoleMode
import GetConsoleMode kernel32.dll

extern SetConsoleMode
import SetConsoleMode kernel32.dll

extern ExitProcess
import ExitProcess kernel32.dll

;-----------------------
%else ; default to Linux

    struc termios
        alignb 4
        .c_iflag: resd 1 ; input mode flags
        .c_oflag: resd 1 ; output mode flags
        .c_cflag: resd 1 ; control mode flags
        .c_lflag: resd 1 ; local mode flags
        .c_line: resb 1 ; line discipline
        .c_cc: resb 19 ; control characters
    endstruc

%define NL 10
%define STDIN 0
%define STDOUT 1
%define ICANON 2 ;.Do erase and kill processing.
%define ECHO 8 ;.Enable echo.

%define TCGETS 0x5401 ; tty-"magic"
%define TCSETS 0x5402

%define SYS_EXIT 1
%define SYS_READ 3
%define SYS_WRITE 4
%define SYS_IOCTL 54

%endif

;------------------------

section .data

    IntroText db NL, "Welcome to the Simple Adventure game...", NL,\
                   NL, "It's not very interesting or complicated ",\
                       "but demonstrates using a data-centric",\
                    NL, "approach to writing adventure games, which ",\
                        "is pretty easy to code but, yet,",\
                    NL, "very compact and flexible...", NL, NL,\
                    "Press Any key (!) to begin", NL, 0

      ExitsString db NL, NL, "Exits: ", 0

      PromptString db "What now, adventurer?", NL, 0

      NoExitString db NL, "Sorry, you cannot go in that direction", NL,
0

      ExitText db NL, NL, "Thank you for playing! Bye! *wave* :)", NL,
NL, 0

      newln db 10, 0

      northprompt db "North ", 0
      southprompt db "South ", 0
      eastprompt db "East ", 0
      westprompt db "West ", 0
      upprompt db "Up ", 0
      downprompt db "Down ", 0

      max_directions equ 6
      exit_names dd northprompt, southprompt, eastprompt
                 dd westprompt, upprompt, downprompt

      CurrentRoom dd 0 ; pointer to ROOM_DATA;

      valid_commands db 'nsewudq'
      valid_commands_len equ $ - valid_commands

%ifdef _ROS_
    read_handle dd 0
    num_chars dd 0
    mode dd 0
    badreadmsg db 'Keyboard not found. Think "F1" to continue.', 0

%endif

; here comes our room data - these must be contiguous!

rooms:
    ; room 0
    room_init RoomPtr(1), 0, 0, RoomPtr(2), RoomPtr(5), 0,\
    "Entrance Hall",\
    "This is the entrance hall of the mansion...it's large and lavish ",
NL,\
    "with a marble floor and an immense staircase leading to the upper
", NL,\
    "rooms...", NL

    ; room 1
    room_init 0, RoomPtr(0), 0, RoomPtr(3), 0, RoomPtr(4),\
    "Kitchens",\
    "The kitchens of this grand mansion - hidden away behind the
splendour ", NL,\
    "of the staircase in the grand entrance hall - is large but
modest...", NL

    ; room 2
    room_init RoomPtr(3), 0, RoomPtr(0), 0, 0, 0,\
    "Study",\
    "The study has many large bookcases filled with dusty
books...judging ", NL,\
    "by what you see in this room, it's really more of a guestroom - the
", NL,\
    "bookcases a means to impress visitors - and isn't used for any
actual ", NL,\
    "studying...", NL

    ; room 3
    room_init 0, RoomPtr(2), RoomPtr(1), 0, 0, 0,\
    "Dining room",\
    "The dining room has a large table, able to seat a substantial
amount of ", NL,\
    "guests...", NL

    ; room 4
    room_init 0, 0, 0, 0, RoomPtr(1), 0,\
    "Wine Cellar",\
    "The dusty old wine cellar has a big rack of wine ", NL,\
    "bottles, though most of the shelves are empty...", NL

    ; room 5
    room_init RoomPtr(6), 0, 0, RoomPtr(7), 0, RoomPtr(0),\
    "Upstairs corridor",\
    "This uneventful corridor leads off into the various bedrooms...",
NL

    ; room 6
    room_init 0, RoomPtr(5), 0, 0, 0, 0,\
    "Master bedroom",\
    "This is the large, luxurious master bedroom for the mansion's
owner...", NL

    ; room 7
    room_init 0, 0, RoomPtr(5), 0, 0, 0,\
    "Guest bedroom",\
    "This is a small but comfortable guest bedroom...", NL

; more room data here...
;------------------------

;------------------------
section .text

%ifdef _ROS_ ; begin SimpleAdventure in ReactOS/Windows

..start:

%else ; begin SimpleAdventure in Linux

global _start ; inform ld
_start:

%endif ; back to "common" code

    mov esi, IntroText
    call putz
    mov dword [CurrentRoom], rooms
    call getc

forever:
    mov edx, [CurrentRoom]

    mov esi, newln
    call putz
    mov esi, [edx + RoomName]
    call putz
    mov esi, newln
    call putz
    call putz
    mov esi, [edx + Description]
    call putz

    mov esi, ExitsString
    call putz

    mov ecx, max_directions
    xor ebx, ebx
.showexits:
    cmp dword [edx + ebx * 4], 0
    jz .skip
    mov esi, [exit_names + ebx * 4]
    call putz
.skip:
    inc ebx
    loop .showexits

    mov esi, newln
    call putz
    call putz
.get_command:
    mov esi, PromptString
    call putz
.reget:
    call getc
    or al, 20h ; force to lowercase (even if it's not an uppercase
                    ; - this is crap, but we only care about nsewudq)
    xor ebx, ebx
    mov ecx, valid_commands_len

.check_commands:
    cmp [valid_commands + ebx], al
    je .got_command
    inc ebx
    loop .check_commands
    jmp short .reget

.got_command:
    cmp al, 'q'
    je exit
    cmp dword[edx + ebx * 4], 0
    jz .cantgo
    mov eax, [edx + ebx * 4]
    mov [CurrentRoom], eax
    jmp forever

.cantgo:
    mov esi, NoExitString
    call putz
    jmp short .get_command

exit:
    mov esi, ExitText
    call putz

%ifdef _ROS_
    push dword 0
    call [ExitProcess]
%else ; Linux
    xor ebx, ebx
    mov eax, SYS_EXIT
    int 80h
%endif

;------------------------

%ifdef _ROS_

;-----------------------
putz:
    pusha

    or eax, byte -1
.find_len:
    cmp [esi + eax + 1], byte 1
    inc eax
    jnc .find_len

    push dword 0
    push dword num_chars
    push eax
    push esi
    push dword STDOUT
    call [WriteFile]
    popa
    ret
;-----------------------------

;-------------------------
getc:
    push edx
    push eax ; this is our "buffer"

    xor eax, eax
    add eax, [read_handle]
    jnz .gothandle

    push byte STDIN
    call [GetStdHandle]

    mov [read_handle], eax

    push dword mode
    push eax
    call [GetConsoleMode]
    and byte [mode], 11111001b

    push dword [mode]
    push dword [read_handle]
    call [SetConsoleMode]

.gothandle:
    mov eax, esp
    push byte 0
    push num_chars
    push byte 1
    push eax
    push dword [read_handle]
    call [ReadFile]
    or eax, eax
    jnz .goodread

    mov esi, badreadmsg
    call putz
    push eax
    call [ExitProcess]

.goodread:
    pop eax ; get "buffer" into al
    pop edx
    ret
;---------------------------

%else ; Linux

;-----------------------
putz:
    pusha
    mov eax, SYS_WRITE
    mov ebx, STDOUT
    mov ecx, esi ; buffer
    or edx, byte -1 ; length in edx
.find_len:
    cmp [ecx + edx + 1], byte 1
    inc edx
    jnc .find_len

    int 80h
    popa
    ret
;-----------------------------

;-----------------------------
getc:
    push ebp
    mov ebp, esp

    sub esp, termios_size ; make a place for current kbd mode

    push edx
    push ecx
    push ebx

    mov eax, SYS_IOCTL ; get current mode
    mov ebx, STDIN
    mov ecx, TCGETS
    lea edx, [ebp - termios_size]
    int 80h
                              ; monkey with it
    and dword [ebp - termios_size + termios.c_lflag], ~(ICANON | ECHO)

    mov eax, SYS_IOCTL
    mov ebx, STDIN
    mov ecx, TCSETS
    lea edx, [ebp - termios_size]
    int 80h

    xor eax, eax
    push eax ; this is the buffer to read into

    mov eax, SYS_READ
    mov ebx, STDIN
    mov ecx, esp ; character goes on the stack
    mov edx, 1 ; just one
    int 80h ; do it

                     ; restore normal kbd mode
    or dword [ebp - termios_size + termios.c_lflag], ICANON | ECHO

    mov eax, SYS_IOCTL
    mov ebx, STDIN
    mov ecx, TCSETS
    lea edx, [ebp - termios_size]
    int 80h

    pop eax ; get character into al

    pop ebx ; restore caller's regs
    pop ecx
    pop edx

    mov esp, ebp ; leave
    pop ebp
    ret
;-------------------------

%endif



Relevant Pages