News:

MASM32 SDK Description, downloads and other helpful links
MASM32.com New Forum Link
masmforum WebSite

lift control..

Started by randomnumber, November 16, 2009, 07:59:43 PM

Previous topic - Next topic

randomnumber

ps: i am in no need of new code.. algorithm would be best for me.. :bg

i am making a parallel port controlled lift for skul assignment. the lift has 2 relays connected to bit  1 and 2 of the data register. when 1fires, lift goes up, when 2 fires, lift goes down. when lift is in ground floor, 8th status bit goes low, when lift is in 1st floor, 6th bit goes low, when lift is in ground 2nd floor, bit 0 of status reg goes low. destination of lift is entered through keyboard. push buttons will be used later.

problems:
--  i can't use irq7 or irq5 i have tried and tried and given up.. i have instead used irq8 to read status register recurrently. the isr will get  current floor of lift and stop lift if its equal to destination floor. user can then input another destination. irq 7 and 5 wud have made things easier.. i cud have solved it easily...

--i need to make lift stop at least 5seconds on destination b4 going to next destination. i thus can't put the delay inside the isr. it must be outside. but then i also need to make a queue.. servicing 1st come 1st serve(this is not the appropriate but its a start). maximum of que is 3 unique destinations..

-- i have to use computer parallel ports.. (lecturers demands). :tdown :(

question:
-- so far i have managed to make the lift move to its destination handling one destination at a time. NO QUEUE. i wan't to make a queue.. use can input any number of entries, but the que should only store 3 unique entries.. since there are only 3 possible destinations. i need help here..  :wink

-- is it possible to make the program have 2 processes,, sharing the queue.. one gets the values, another moves lift. wud it mean 2 processes.. i have no idea on multi-process programmin on masm32..

my algo is like so..


set currentfloor of lift
start:  get destination from user and save it..
         if destination is above or below current floor, fire corrent relay to move up or down
         save previous isr location
         replace isr of irq8

here:  {isr checks current floor of lift from status register} ; this is the new isr for
          when current floor is not equal to destination,       ; irq8.
          loop here
         
when current floor is equal to destination.. stop lift...
         
          restore previous isr..
loop start




dedndave

what you want is a "circular buffer"
the data wraps from the end of the buffer to the beginning
you then keep two index pointers
one for "buffer start" and one for "buffer end"
usually, if buffer start = buffer end, it signals the buffer is empty
you can use the BIOS keyboard buffer in the BIOS data segment at 40:0000 as an example
do the lecturers also confine you to using XP ?
if you can get away with windows 98, the parallel port stuff is easy

MichaelW

Splitting the algorithm up into two "processes" would make your task more difficult than it needs to be. Your algorithm could be implemented in a single loop, something like this:

do
    read keyboard
    if key == escape key
        exit from loop
    end if
    if key_value >= 0 and key_value <= 2
        if current_time > timeout_time
            destination_floor = key_value
        end if
    end if
    if current_floor < destination_floor
        if current_time > timeout_time
            close up relay
            state_variable = up
        end if
    elseif current_floor > destination_floor
        if current_time > timeout_time
            close down relay
            state_variable = down
        end if
    end if
    update current_floor
    if state_variable == up
        if current_floor == destination_floor
            open up relay
            state_variable = off
            timeout_time = current_time + 5s
        end if
    elseif state_variable == down
        if current_floor == destination_floor
            open down relay
            state_variable = off
            timeout_time = current_time + 5s
        end if
    end if
loop


One important detail is that the loop must run more or less continuously. The function that you use to read the keyboard must return immediately to the caller, even when there is no key in the keyboard buffer.

eschew obfuscation

randomnumber

\\dedndave
Quotedo the lecturers also confine you to using XP
only restriction is assembly language preferably tasm32 or masm32. i was also thinkin that 98 would be better.. if only i can find it :bg

\\MichaelW
thaks for the algo.. just finished the code will try it practically in the morning..circular buffer will definately work well with it. :cheekygreen:

dedndave

with win95/98, you can easily program the interrupt handlers as you were saying
wth XP/vista, i think you need ring 0 access to do it

randomnumber

this
is the 3rd week running and i have not seen anyone nor heard from anyone who has win98.. :tdown

i broke the algo in to two.. one task is running in isr and another is in the main program.. its will act like 2 processes.. :dance:
since am bound to irq8, i can use it to keep time by incrementing a variable.. this will help me get my delay of 5 seconds on each floor.. resetting it each time i reach a destination and incrementing it until i reach a different destination. I have a floor variable which is able  to store 3 values,, the 3 values keep the floor number in que.. the 3 values are initially given x value.

isr will 1st read the end of the flooor variable[2] and if this value not "x" it will put this as the current destination.. it will then put an "x" in the floor variable[2].  it then fires the right relay for up or down motion.. if floor variable[2] is "x" it rill do variable[2]=variable[1] and variable[1]=variable[0] and variable[0]='x' sorta like a shift right..

when the end of que is x. isr will also check current location of lift and stop lift if
destination is arrived at..

new destination is only picked from que if timeout>5seconds and lift is not moving.. i.e 0378h == 00h

second part will be getting the values from the user,, storing them at the floor que used by the isr.. it will check if any location is x
( is empty) and store the value there.

improved algo is like so..
1st part
get old isr
patch irq8 with new isr
reading: read value from keyboard check que for the same value.. if
value is already there,, destination is awaiting service check floor
variable for empty location and store destination here..
loop reading

2nd part..
isr if 5 seconds have elapsed and lift is not moving
get next destination from floor[2] and replace an 'x' there. if floor[2] has an 'x' shift values to the right.
check status register and update current floor
check floor[1] and floor[0] for values and break relays if values there equal current floor
and set timeout==0;
if destination ==current floor break relays.
set timeout==0.

i have run the program but it only works once.. then i put 210 it goes to 2nd floor then 1st floor but doesn't go to ground..
can this program be improved?

dedndave

i was just having a look
one thing i see right off the bat....
the DS register is not set to the data segment
so, when you store the original INT 8 vector, lord knows where it goes - lol
(it must overwrite some critical piece of data in the PSP - lol)
when you try to restore it, it probably gets messed up nicely (set to 0000:0000 i am guessing)

start:
        mov     ax,@DATA
        mov     ds,ax

then, you can remove the lines

       mov ax,seg txt      
       mov ds,ax

when you do restore it

   mov   dx, WORD PTR prevISR      ;restore procedure back to 08h in vector table
   mov   ds, WORD PTR prevISR[2]

may be replaced with

        lds     dx,prevISR

i really like LDS and LES - hardly ever get to use them
if you want to load a dword into DX:AX, you can sometimes get away with

        les     ax,DwordVal
        mov     dx,es

dedndave

ok - here is a big problem - lol
when you enter the handler, you have to save the DS register and make it local as well

        push    dx
        push    ax
        push    ds
        mov     ax,@DATA
        mov     ds,ax
.
.
.
.
        pop     ds
        pop     ax
        pop     dx
        iret

dedndave

one more thing.....
when you exit the INT 8 handler, you should chain to the original interrupt handler, rather than IRET
that allows the BIOS/DOS/whatever else to execute their handlers (not really an ISR by the way - it's a handler)

        jmp far ptr cs:prevISR  ;this assumes the prevISR data is in the CS segment

a better way.....
you could store the original vector directly into the branch instruction (code segment)
(under XP, you may have to acquire access rights to modify data in the code segment
seeing as how you can't revector INT 8 under XP, anyways - not a problem - lol)

        mov     ax,3508h
        int     21h
        mov word ptr cs:prevISR,bx
        mov word ptr cs:prevISR[2],es

then, in the handler routine.....

        pop     ds
        pop     ax
        pop     dx

        db      0EAh    ;jmp far instruction
prevISR dd ?

then, to restore the original vector...

        lds     dx,cs:prevISR
        mov     ax,2508h
        int     21h

randomnumber

Thanks for the pointers.. i almost forgot about the old isr..


Quote
        db      0EAh    ;jmp far instruction
prevISR dd ?

i don't get wat exactly is happening..

also if i were to use

jmp cs:prevISR

should use it instead if IRET or will i need both?

dedndave

if you were to disassemble the instruction JMP 1122:3344 (where 1122 is the segment and 3344 is the offset)
it would be 0EAh, 44h, 33h, 22h, 11h
so - we just "hard-code" the far jmp instruction and supply the address
no need for IRET
IRET returns from the interrupt
when an interrupt occurs, the processor pushes the flags, then the current code segment, then the return offset
the equivalent of a CLI is also performed to prevent other interrupts from happening before the current one is handled
IRET does the reverse except it does not alter the interrupt enable flag

to use your own jmp, i think you have to add "dword ptr" so the assembler knows it is a far pointer

        jmp dword ptr cs:prevISR

by hard coding it with 0EAh, it is a few clock cycles faster because the address is immediate instead of direct addressed

the timer interrupt occurs once every 18.2 ms
keep it short and sweet
do as little as you can inside the handler and let the rest of the program do the work
you may consider simply incementing a counter value inside the handler and leave all the decision making in the program

when accessing data shared with the handler, it is good to CLI, access the data, then STI
that way, the program and the handler are not trying to access the data at the same time (so to speak)

the reason the code only works one time is probably because you are not chaining to the BIOS interrupt
it takes care of clearing the interrupt from the PIC (Programmable Interrupt Controller) chip
until that interrupt is cleared from the PIC, no further timer interrupts are allowed

MichaelW

randomnumber,

If your handler is the last one installed (likely for what you are doing) then IRET will return directly to whatever called the interrupt, or for a hardware interrupt whatever was executing when the interrupt occurred, bypassing any other handlers that are hooked into the interrupt. If the interrupt is a hardware interrupt (as IRQ0/Interrupt 8 is) then the last handler in the chain must send an End-Of-Interrupt (EOI) command to the interrupt controller to prepare it to accept further interrupts from the current IRQ, or from any lower-priority interrupt. So in other words, without the EOI one or more hardware interrupts will cease to function, and if the current IRQ is IRQ0 (the highest priority hardware interrupt) then all hardware interrupts will cease to function. So if you exit a hardware interrupt handler with an IRET, it needs to be preceded with code to send an EOI command to one or both of the interrupt controllers, depending on the current IRQ. The alternative is to chain to the previous handler, so a handler further up the handler chain will take care of the EOI for you. The chaining can be done at the end of your handler, with a far jump as the last instruction in the hander. There is another method that will allow your handler to effectively call the previous handler, so that it will receive control after the chain returns, but I can see no reason to get into that here.

I still think that by using interrupt handlers you are making your project more difficult than necessary. The BIOS maintains a count of the timer ticks that you can access with only a few instructions, and you can derive your timing from that.
eschew obfuscation

dedndave

as for INT 8, Michael is probably correct
however, you may want to revector INT 7 to detect activity on the parallel port
that way, the program is notified immediately if something occurs (like a limit switch or other "life-threatening" event)
a good example of this is a safety issue
i don't know what you have in the way of hardware
but, it sounds to me as if the lift were moving down and someone were under it, it could be dangerous - lol
it would be nice to have a photo-electric eye to detect the presence of an obstruction and stop the lift
that is an ideal example of the need for interrupts
also, things should be wired so if the parallel cable is disconnect for some reason, the lift stops immediately
if the computer power cord is kicked out of the wall socket, the lift stops immediately
you get the idea

randomnumber

\\Michaelw

QuoteI still think that by using interrupt handlers you are making your project more difficult than necessary.

i had made the same version of the program using a loop but it was extremely slow and sometimes wasn't quick enough to get readings from sensors before they changed.. i thus opted for the handlers.

Quote
There is another method that will allow your handler to effectively call the previous handler

know that u mentioned it .. am curious.. just point me al try to follow..

\\dedndave
Quotehowever, you may want to revector INT 7 to detect activity on the parallel port.

int 7 wud have been best but i haven't got it to work..http://www.masm32.com/board/index.php?topic=12658.0

nyce idea bout the photo eye n power cord.. :U i wuz thinking of adding a small ups such that when power is out,, lift goes to ground floor automatically..

MichaelW

Quote from: randomnumber on December 03, 2009, 08:53:03 PM
Quote
There is another method that will allow your handler to effectively call the previous handler

know that u mentioned it .. am curious.. just point me al try to follow..

The attachment here includes an handler that does that, Isr8_0 in test.asm.
eschew obfuscation