To start with, this probably belongs in the General Programming forum, even if you are planning a systems dialect of Lisp. See the thread
"Language Design: Sibilance" for a (brief) discussion on this topic.
Your existing code, as best as I can follow it, does seem to do the job after a fashion: it takes a pointer to a cons cell as an argument (in eax), checks that the cons cell is valid (by checking the tag of eax), then returns the value of the first item in the cons cell (it copies the value of eax to ebx, then dereferences ebx and stores the result in eax).
This having been said, there are some specific critiques I would add regarding your particular design. These critiques are from the point of view of classic Lisp and Scheme implementations; if you are intending a system-programming subset (as seems likely), then they would be less critical, though other limitations would apply.
To begin with, I don't think that using the primary accumulator for argument passing is a great idea, esp. on a register-poor architecture like the x86 (which really needs a good dozen or so more for that sort of thing). This is particularly problematic in a language that uses recursion as it's primary means of repetition; even on an architecture where register windowing were an option, you would quickly reach a call depth where you would be using the stack
more using register passing than with stack passing, since you would have to push the whole register set pre-emptively in the calling function rather than selectively pushing single registers in the called function. It also would make TCO a nightmare, as you wouldn't know ahead of time which registers could be elided and which could not be. Besides, what would you need the save the frame pointer for if you aren't using the stack for arguments?
Conversely, using a conventional environment frame - either using the stack or register windowing - would rule out the possibility of closures. Since closures are the traditional form of persistent objects in Lisp (and especially in Scheme), this would be a serious handicap. It would also make continuations impossible (though that is only of interest in Scheme-like implementations). In the end, you would need to have free environment tables to support a full Lisp, I think, or at the very least the ability to copy the local variables off of the stack onto a persistent table in order to pass a function state variable.
As for using tagged pointers, it's hard to say if it is worth it. While it would be really useful in many ways, it would also mean reducing the effective address space to an 8th of the total memory, or 512M in a 32-bit system. While this is still a huge amount of memory - even today, many systems have less than 256M of RAM - it would still mean a considerable loss for a language as memory-intensive as Lisp. Also, two type bits and a sweep bit may not be enough to make it worthwhile; with only four types, one of which would by necessity be null, it would be too little information at too much cost. Tagged memory is very useful when there is hardware support, but it is far less so when done in software alone. Here, too, it may be better just to eat the overhead on a table for the variables.
Finally, I don't know if the approach you've taken captures the concept generally enough. In most Lisps, (car) is defined as taking a proper list - a cons cell whose second element points to another cons cell, or to null - and returns a the pointer in the first value. This last part is important, as (in most Lisps), a returned value is a pointer to a structure allocated on the heap. In the design you've chosen, it looks as if you mean to 'snap the pointers', that is to say, to replace pointers to literals with the pointed values themselves. While this would be very speed efficient, it presents problems whe handling shared data structures, as well as adding overhead in that it requires you to test the type of the return value.