Tuesday, January 31, 2023

Tesla lost $140M trading Bitcoin in 2022

For some reason, Porsche decided they needed to release a set of Porsche 911 NFTs so that customers could buy "the opportunity to co-create Porsche's future in the Web3 universe" (whatever that means). The set of 7,500 NFTs were available to mint for 0.911 ETH apiece, or around $1,490. If the project sold out, Porsche would have been looking at a windfall of more than $11 million.

Unfortunately for them, things didn't quite go as planned, with collectors balking at the high pricetag. Mints slowed to a crawl far before the 7,500 limit was reached, and the NFTs quickly began trading at a discount on secondary markets (meaning it was cheaper to buy a resold NFT than mint a new one).

Porsche decided to pump the brakes on the mint when fewer than 2,000 had sold. However, they botched that too — they announced they had stopped the mint before they actually did so, which caused the collection's secondary floor price to rise back above the mint price in anticipation of higher scarcity. Observant traders who noticed this were able to arbitrage the price difference, minting new NFTs and immediately flipping them for a profit on secondary markets.

NFT collectors criticized Porsche for appearing to try to jump into web3 without knowing the space, and asking for an exorbitant mint price without a clear plan.



from Hacker News https://ift.tt/VNj24I1

Show HN: GPTMinus1 – Fool OpenAIs AI Detector by Randomly Replacing Words

Fool GPT by randomly replacing words with synonyms in your text. Try it out 👇



from Hacker News https://ift.tt/Rxap70Y

Show HN: DocAsker – Use LLMs to ask documentation questions



from Hacker News https://ift.tt/T6aNb4r

Console Screendumps

“Get it down on wax! Or paper. Or a PPM file.”

Coding new ioctls to produce screendumps from the console

Screenshots are easy if you're running the X Window system or perhaps working within a virtual machine, but what about those times when you need to capture some console output straight from real hardware?

That's exactly what Crystal needed to do during her recent development work on the wscons code, and today she is here again to explain the whole process of adding a new ioctl to the kernel to make screenshots of the console possible.

If you're not interested in learning about the programming, (shame on you!), then scroll to the bottom to download some some ready-to-apply patches.

What we'll learn today:

  • Adding a new ioctl to the wscons code
  • Copying data from kernel memory space to user memory space
  • A few quirks of the wscons and rasops code

Producing a screenshot, or as I prefer to call it a screendump given that we are dumping the 'raw' data from the display device, is at least in theory quite straightforward.

We just need to read the data from the framebuffer byte-by-byte, write it to a file, and then do any post-processing to make it usable.

The difficulty arises from the fact that on pretty much any modern desktop operating system, a user-mode program can't just directly access the hardware or kernel data structures in this way.

Certainly in the case of OpenBSD, we need to add some code to the kernel to make this all happen. And even if you're somewhat experienced with C programming it still might not be immediately obvious where exactly this code needs to be plugged in.

To make things worse, although we could in theory write a file containing our screendump data to the filesystem directly from within the kernel, this is generally considered a very bad idea. In fact, doing anything in the kernel that could be achieved just as well in userspace is usually a bad design choice.

So the correct way to solve this problem and add the missing feature is to write a small amount of kernel code to pass the required data to userspace, then code a separate regular userspace program to process it and finally write the data out to disk.

That might sound complicated, but fear not! It's actually fairly easy, so let's get straight to work implementing a new screendump facility.

Two types of screenshots

Since the console is character-based, there are actually two ways that we can record the information displayed at any moment:

Text-based screenshots

This involves copying the ASCII character at each location to a text file, and optionally preserving the attribute data, (such as underlining, bold text, and colors), in some way as well.

The advantages of this approach are that we end up with editable text, and that the actual amount of screendump data is quite small, typically around 4 - 8 kilobytes. It's also somewhat easier to implement, as we only need to deal with the wscons code.

The obvious disadvantage is that it doesn't preserve the exact 'look' of the screen, with the original font, exact original colors and way of rendering bold, italic, and so on.

Graphical screenshots

With a graphical screenshot, we'll be converting the actual pixel data from the framebuffer into a PPM graphics file.

This means that we can preserve the original font, as well as any rendering effects.

The files will be much larger, perhaps 4 - 8 megabytes, but the biggest disadvantage is the added complexity of actually implementing it, since we need to deal with both the wscons and rasops code.

In this article, we'll be looking at and implementing both of these approaches.

Common concepts and background information

In both cases, we'll be adding a new ioctl to the wscons subsystem. This ioctl can then be called from a standard usermode C program, and then kernel will dutifully copy the requested data into a buffer that the usermode program has pre-allocated and then passed to it.

If you're not familiar with the concept of an ioctl, you might want to read the manuals page for ioctl and wsdisplay before continuing with this article.

The current list of ioctls for wscons can be found in sys/dev/wscons/wsconsio.h, and the code to handle our new ones will be added to wsdisplay.c.

Kernel code

The format of the character cell data in the wscons subsystem is fairly typical of a character based terminal. Two values are stored for each character, one being the ASCII character value, and the other being an attribute value. Technically, the character value is a unicode value rather than ASCII, but to keep things simple we'll treat the values as unsigned chars in the 0 - 255 range.

Within the existing wscons code, these pairs of values are usually stored in a struct wsdisplay_charcell.

To begin with, we can simply ignore the attribute value and copy the raw character values out.

First of all, we need to add a new structure to wsconsio.h:

struct wsdisplay_screendump_text {

int rows;

int cols;

unsigned char * textdata;

unsigned int * attrdata;

};

The rows and cols values will be filled in by our new piece of kernel code, and returned to userland so that we know how many characters make up the entire screendump as well as where the line breaks should come. The pointers will be malloc'ed in userland before calling the kernel code.

We also need to add a define for our new ioctl to wsconsio.h:

#define WSDISPLAYIO_SCREENDUMP_TEXT _IOWR('W', 160, struct wsdisplay_screendump_text)

The parameters we are supplying are:

  • The code for wscons ioctls, which is always 'W'.
  • A unique number for this ioctl.
  • The type of data that will be passed to, (and back from), the code that implements the ioctl. This is usually available within the ioctl handling function as 'data'.

The range of ioctl numbers 160-255 is marked as reserved for future use. Since our new ioctl operates on the display, it would have made more sense to use the 64-95 range, but this is already full.

The _IOWR macro is defined in sys/sys/ioccom.h, along with other ioctl-related macros. The choice of _IOWR, _IOR, or _IOW, determines whether the values that are defined in our struct wsdisplay_screendump_text are copied from userland to the kernel, from the kernel back to userland, or both.

Since in this case we need to pass pointers from userland to the kernel and receive the values for rows and cols back, we need data to move in both directions, so we use _IOWR.

Note that the actual content of textdata and attrdata will be copied from kernel memory space to our userland memory allocation by our own code, (specifically a call to the copyout kernel function), and not by this _IOWR mechanism, which is only responsible for passing the values of the pointers.

Now that we have a mechanism in place to call our new ioctl, we can add the code to implement it to wsdisplay.c. The function wsdisplay_internal_ioctl mostly consists of a large switch statement, and it is here that we can add our new routine:

case WSDISPLAYIO_SCREENDUMP_TEXT:

{

struct wsdisplay_charcell cell;

#define SCREENDUMP ((struct wsdisplay_screendump_text *)data)

#define DISP_SIZE (SCREENDUMP->rows * SCREENDUMP->cols)

unsigned char * text_data;

unsigned int * attr_data;

int i;

/* The values of the following assignments will be copied back to userspace upon exit. */

SCREENDUMP->rows=N_ROWS(scr->scr_dconf);

SCREENDUMP->cols=N_COLS(scr->scr_dconf);

text_data=malloc(DISP_SIZE, M_IOCTLOPS, M_WAITOK);

attr_data=malloc(DISP_SIZE*sizeof(unsigned int), M_IOCTLOPS, M_WAITOK);

for (i=0; i<DISP_SIZE; i++) {

GETCHAR(scr, i, &cell);

*(text_data + i)=cell.uc;

*(attr_data + i)=cell.attr;

}

/* Only need to use copyout for things passed as pointers */

copyout (text_data, SCREENDUMP->textdata, DISP_SIZE);

copyout (attr_data, SCREENDUMP->attrdata, DISP_SIZE*sizeof(unsigned int));

free (text_data, M_IOCTLOPS, DISP_SIZE);

free (attr_data, M_IOCTLOPS, DISP_SIZE*sizeof(unsigned int));

return (0);

}

In terms of kernel code, this is basically all that we need!

Points to note:

The kernel versions of the malloc and free functions have different semantics to their userspace equivalents.

Malloc requires us to specify a type argument, which is basically used for accounting and diagnostic purposes. The code above would still work whichever type was used, but the most relevant one seems to be M_IOCTLOPS. More importantly, the third flags argument must include either M_WAITOK or M_NOWAIT, otherwise the malloc call will trigger an assertion and panic the kernel. Refer to the manual pages, malloc(9), and free(9) for further details.

The GETCHAR macro is actually defined in wsmoused.h.

This is likely because it's mainly used in conjunction with displaying the mouse cursor and implementing copy and paste using the mouse.

On a machine with a framebuffer console that is using the rasops code, it ultimately calls the function rasops_getchar. This reads from the rs_bs array, which is a struct wsdisplay_charcell. In the case of the vga text mode driver, the function called is pcdisplay_getchar, via vga_getchar.

The copyout kernel function is what does the heavy lifting

This is the function that actually writes the data to usermode-accessible memory, ready for a userspace program to access it.

Updating /usr/include

Since we have changed a kernel header file which is also used by userland programs, we need to perform an additional step along with the normal kernel compile and installation.

The file /usr/src/sys/dev/wscons/wsconsio.h needs to be copied to /usr/include/dev/wscons/wsconsio.h:

# cp -p /usr/src/sys/dev/wscons/wsconsio.h /usr/include/dev/wscons/

Failure to perform this step will result in compilation errors for userland programs, as they will still be using the old version of wsconsio.h.

Userland code

At a minimum, all we need to do from userland is to call the new ioctl and write the data that it returns to a file:

#include <stdio.h>

#include <unistd.h>

#include <stdlib.h>

#include <sys/ioctl.h>

#include <sys/time.h>

#include <dev/wscons/wsconsio.h>

#include <fcntl.h>

int main()

{

int fd;

int res;

struct wsdisplay_screendump_text screendump;

screendump.textdata=malloc(65536);

screendump.attrdata=malloc(65536);

res=ioctl (STDIN_FILENO, WSDISPLAYIO_SCREENDUMP_TEXT, &screendump);

printf ("ioctl returned %d\nrows %d, cols %d\n", res, screendump.rows, screendump.cols);

if (res == -1) {

return (1);

}

fd=open ("screendump", O_WRONLY | O_CREAT | O_TRUNC, 0600);

write (fd, screendump.textdata, screendump.rows * screendump.cols);

close (fd);

return (0);

}

Here we are using the struct wsdisplay_screendump_text that we added to wsconsio.h, and creating a variable 'screendump' of that type.

The two pointers to memory that will be used to hold the userland copy of the screendump characters and attributes need to have memory allocated. If we wanted to allocate exactly the right amount of memory then we could make a separate call to find out the actual screen dimensions, however since the memory required is small in this instance, we just allocate 64K which should be enough to cover any reasonably-sized text console.

Note that since we are not using the attribute data, we could also just set the attrdata pointer to NULL:

screendump.attrdata=NULL;

This would cause the corresponding call to copyout to fail and return EFAULT, which is fine because our kernel code doesn't care and will just continue regardless.

Compiling and running the above code will result in the creation of a text file containing each of the characters from the display and nothing else. So in the case of a 160 x 49 display, we would get a file of exactly 7840 bytes.

That's not particularly useful, because it doesn't even contain newline characters at the end of each line. In other words, all of the content is on one long line of 7840 characters. As a result, if we viewed it on a display of a different size then the lines would appear to wrap at the wrong points.

We can easily modify our program above to add newlines at the right places, based on the value of screendump.cols:

#include <stdio.h>

#include <unistd.h>

#include <stdlib.h>

#include <sys/ioctl.h>

#include <sys/time.h>

#include <dev/wscons/wsconsio.h>

#include <fcntl.h>

int main()

{

int fd;

int res;

int i;

int j;

unsigned char * buffer;

struct wsdisplay_screendump_text screendump;

screendump.textdata=malloc(65536);

screendump.attrdata=malloc(65536);

screendump.attrdata=NULL;

res=ioctl (STDIN_FILENO, WSDISPLAYIO_SCREENDUMP_TEXT, &screendump);

printf ("ioctl returned %d\nrows %d, cols %d\n", res, screendump.rows, screendump.cols);

if (res == -1) {

return (1);

}

/* Write line-delimited output to a file */

buffer=malloc(screendump.rows * (screendump.cols + 1));

j=0;

for (i=0 ; i < screendump.rows * screendump.cols ; i++) {

*(buffer + j++)=*(screendump.textdata + i);

if ((i + 1) % screendump.cols == 0) {

*(buffer + j++)=10;

}

}

fd=open ("screendump.asc", O_WRONLY | O_CREAT | O_TRUNC, 0600);

write (fd, buffer, screendump.rows * (screendump.cols+1));

close (fd);

free (buffer);

return (0);

}

With this new version of the code we get a text file that has the correct number of rows and columns to match the original display.

Of course, each line of the output is still padded with spaces to the full length of the line, but this can also easily be fixed with a slight change to the main routine:

/* Write line-delimited output to a file, trimming trailing blanks from each line */

buffer=malloc(screendump.rows * (screendump.cols + 1));

j=0;

for (i=0 ; i < screendump.rows * screendump.cols ; i++) {

*(buffer + j++)=*(screendump.textdata + i);

if ((i + 1) % screendump.cols == 0) {

while (j>0 && *(buffer + j - 1)==32) { j--; }

*(buffer + j++)=10;

}

}

fd=open ("screendump.asc", O_WRONLY | O_CREAT | O_TRUNC, 0600);

write (fd, buffer, j);

Doing something with the attributes

The above works quite well for capturing text data that happens to be displayed on the console. But we're still missing the attributes, so we can't see text in different colors, and details such as bold and underlining are lost.

One solution is to parse the attribute data, and convert it into CSS that we can use together with an HTML encoded version of the text. This allows us to represent the whole display in a form that preserves virtually everything except the actual font.

Unfortunately, this process is complicated somewhat by the fact that the raw attribute data is device specific. The correct way to convert it back to device-inspecific values is to use the 'unpack attribute' functions, but doing so adds somewhat to the complexity of the kernel code. For this proof of concept we'll stick to assuming that the attributes are in the format used by the rasops code, which should cover the vast majority of users on the amd64 platform.

The following program produces a 'screendump.html' file in the current directory when run, which contains a fairly accurate rendering of the text currently on the console. It even includes support for 256 colors as implemented by some of the console enhancement patches I recently created.

#include <stdio.h>

#include <unistd.h>

#include <stdlib.h>

#include <sys/ioctl.h>

#include <sys/time.h>

#include <dev/wscons/wsconsio.h>

#include <fcntl.h>

#include <dev/wscons/wsdisplayvar.h>

#define BLUE(x) (48*((x-16)%6))

#define GREEN(x) (48*(((x-16)/6)%6))

#define RED(x) (48*(((x-16)/36)%6))

#define HEX(i) ((i & 0xf) <10 ? (i & 0xf)+'0' : (i & 0xf)-10+'a')

#define GREY(i) (1+((i-232)*11))

void color_html(int cindex, unsigned char * return_col)

{

if (cindex < 16) {

*(return_col+3)=0;

*return_col=(cindex & 1 ? (cindex & 8 ? 'f' : '8') : '0');

*(return_col+1)=(cindex & 2 ? (cindex & 8 ? 'f' : '8') : '0');

*(return_col+2)=(cindex & 4 ? (cindex & 8 ? 'f' : '8') : '0');

if (cindex==7) {

*(return_col)='d';

*(return_col+1)='d';

*(return_col+2)='d';

}

if (cindex==8) {

*(return_col)='8';

*(return_col+1)='8';

*(return_col+2)='8';

}

return;

}

*(return_col+6)=0;

if (cindex < 232) {

*(return_col)=HEX(RED(cindex) >> 4);

*(return_col+1)=HEX(RED(cindex));

*(return_col+2)=HEX(GREEN(cindex) >> 4);

*(return_col+3)=HEX(GREEN(cindex));

*(return_col+4)=HEX(BLUE(cindex) >> 4);

*(return_col+5)=HEX(BLUE(cindex));

return ;

}

*(return_col)=HEX(GREY(cindex) >> 4);

*(return_col+1)=HEX(GREY(cindex));

*(return_col+2)=HEX(GREY(cindex) >> 4);

*(return_col+3)=HEX(GREY(cindex));

*(return_col+4)=HEX(GREY(cindex) >> 4);

*(return_col+5)=HEX(GREY(cindex));

return ;

}

int main()

{

int fd;

int res;

int i, j;

int pos_in;

int spanopen;

unsigned int cattr;

struct wsdisplay_screendump_text screendump;

unsigned char * buffer;

unsigned char * pos_out;

unsigned char * return_col;

screendump.textdata=malloc(65536);

screendump.attrdata=malloc(65536);

res=ioctl (STDIN_FILENO, WSDISPLAYIO_SCREENDUMP_TEXT, &screendump);

if (res == -1) {

printf ("Ioctl WSDISPLAYIO_SCREENDUMP_PIXELS not supported for this device\n");

return (1);

}

printf ("ioctl returned %d\nrows %d, cols %d\n", res, screendump.rows, screendump.cols);

/* Write HTML output to a file */

buffer=malloc(1024*1024);

pos_out=buffer;

spanopen=0;

#define LINEAR_POS (i*screendump.cols+j)

/* Write HTML preamble */

pos_out+=sprintf(pos_out, "<html><body><div style='font-family:monospace;white-space:pre;'>");

return_col=malloc(1024);

/* Process the output line-by-line */

for (i=0; i<screendump.rows; i++) {

pos_out+=sprintf(pos_out, "<div>");

for (j=0; j<screendump.cols; j++) {

if (j==0 || screendump.attrdata[LINEAR_POS]!=cattr) {

cattr=screendump.attrdata[LINEAR_POS];

if (j>0) {

pos_out+=sprintf(pos_out, "</span>");

}

pos_out+=sprintf(pos_out, "%s", "<span style='");

color_html((cattr >> 16) & 0xff, return_col);

pos_out+=sprintf(pos_out, "background:#%s;", return_col);

color_html((cattr >> 24) & 0xff, return_col);

pos_out+=sprintf(pos_out, "color:#%s;", return_col);

#ifdef ENHANCED_CONSOLE

pos_out+=sprintf(pos_out, "font-weight:%d;", (cattr & WSATTR_HILIT ? 800 : 400));

pos_out+=sprintf(pos_out, "text-decoration:%s;", (cattr & WSATTR_UNDERLINE ? "underline" : "none"));

#else

pos_out+=sprintf(pos_out, "text-decoration:%s;", (cattr & 1 ? "underline" : "none"));

#endif

pos_out+=sprintf(pos_out, "%s", "'>");

}

switch (*(screendump.textdata+LINEAR_POS)) {

case '>':

pos_out+=sprintf(pos_out, "%s", "&gt;");

break;

case '<':

pos_out+=sprintf(pos_out, "%s", "&lt;");

break;

case '&':

pos_out+=sprintf(pos_out, "%s", "&amp;");

break;

default:

*(pos_out++)=*(screendump.textdata+LINEAR_POS);

}

}

pos_out+=sprintf(pos_out, "</span></div>");

}

/* Write HTML trailer */

pos_out+=sprintf(pos_out, "</div></body></html>");

fd=open ("screendump.html", O_WRONLY | O_CREAT | O_TRUNC, 0600);

write (fd, buffer, pos_out-buffer);

close (fd);

free (buffer);

free (return_col);

free (screendump.textdata);

free (screendump.attrdata);

return (0);

}

The generated CSS is not particularly efficient, since it repeats all of the parameters every time there is any change in the attributes. Nevertheless, it works well as a proof of concept, and the results are certainly suitable for use on webpages.

Important note

The code required to correctly interpret the attributes depends on the version of OpenBSD that you are using, as some other work that I have done on the console code which changes the way that the attributes are passed to the rasops code has now been committed to -current:

If you are testing this code on OpenBSD 7.2-release, (or earlier), then leave ENHANCED_CONSOLE undefined.

If you are testing this code on a system built from -current sources after 20230118, or a future release version of OpenBSD, then define ENHANCED_CONSOLE.

Additionally, bold text will currently only be rendered as true bold, (rather than just a brighter color), in the html output if you have applied one of my kernel patches which causes the WSATTR_HILIT attribute to be passed through to the rasops code.

Security consideration

Obviously, the ability to take screendumps has potential security implications.

In these examples we are performing the ioctl on standard input, which does not require any special permissions. However, if the we are on a system which is using virtual consoles, then the code as written will dump the contents of the currently active console.

Consider the case where we invoke the screendump capturing code with a slight delay:

Then we can easily switch to another virtual terminal where somebody else might be logged in, and capture their current screen contents.

This can be done either with a command key combination, such as ctrl-alt-F1, or using wsconsctl:

$ wsconsctl -f /dev/stdout display.focus=0

If we have root access to the system, then we can open /dev/ttyC0, (or in fact any of the /dev/ttyC? devices), remotely via a network connection, and capture the contents of the currently selected virtual terminal. Note that ioctls on these devices act on the virtual terminal system as a whole, so for example, opening ttyC2 and performing a screendump won't automatically provide the contents of that virtual terminal.

Since both of these scenarios require either physical access to the console or remote root access to the system, they shouldn't really be considered exploits. The first example doesn't actually allow us to access any data that we can't already access, and whilst the second example does, it requires root access so it's scope for abuse is severely limited. However we should be aware of potential security issues if we decide to further expand the capabilities of the new ioctl.

Whilst text-based screenshots are very useful, to capture the exact rendering of the screen we obviously need to produce a graphics file from a pixel-by-pixel readout of the framebuffer.

In principle, this isn't too much different from what we've already done above, except that we'll be reading RGB pixel data instead of ASCII character values and attribute bytes. However, we can't really do this directly from the wscons code because we need to access all sorts of data structures that are specific to the graphics hardware in use - there is no convenient equivalent to the GETCHAR macro to read individual pixel values.

In practice, for graphics hardware that is common on the amd64 platform this is all handled through yet another abstraction layer which is known as rasops, (raster operations). This effectively sits between the higher-level wscons code and the various low-level graphics drivers, and provides us with a unified way to access the framebuffer regardless of the actual hardware in use. The rasops code also implements a few other details such as the virtual terminals functionality, (in other words, if we're looking at virtual terminal 1 and a program outputs to virtual terminal 2, that output doesn't actually get painted to the screen), and display rotation, (which is very limited in scope and virtually unused on OpenBSD systems today).

So even though we only want access some parameters to determine the display size and the address of the framebuffer, then copy the pixel data out, we need to write a new rasops function.

To do this we need to delve deep into the rasops code, and here in lie some interesting surprises.

Setting up the second new ioctl

Just like with the first new ioctl that performs text-based screenshots, we need to add some code to wsconsio.h:

struct wsdisplay_screendump_pixels {

int width;

int height;

int stride;

int depth;

unsigned char * data;

};

#define WSDISPLAYIO_SCREENDUMP_PIXELS _IOWR('W', 161, struct wsdisplay_screendump_pixels)

This is all directly equivalent to the code we added before.

Instead of rows and columns, we have width and height in pixels, along with 'stride', which will be explained shortly but is basically the width in bytes rather than pixels, taking into account any padding and alignment. We also include the display bit depth so that we can eventually apply the correct post-processing to the pixel data to make it usable.

Since we're reading a single block of RGB pixel data, we only have one pointer this time rather than the two that we required before for the separate character and attribute values.

Calling an existing rasops function

Before we write our own new rasops function, let's look at how to call an existing one.

To actually call the rasops routines from the wscons code, we use the WSEMULOP macro. This is defined in wsemulvar.h, and accepts the following arguments:

  • An int to store the return value.
  • A pointer to one of several possible structures, (typically an 'emulation data' structure, although we will be using a struct wsscreen_internal), which itself contains a struct wsdisplay_emulops, from which the WSEMULOP macro then extracts the relevant function pointer.
  • A pointer to a struct wsemul_abortstate.
  • The name of the rasops function to call, or more correctly, the name of a function pointer defined in the struct wsdisplay_emulops passed as argument two above.
  • Any number of arguments to that rasops function.

None of this is really documented, except for the source code itself, so expect to do a lot of reading if you want to do anything new with the rasops code.

A simple but practical example might make things clearer:

case WSDISPLAYIO_SCREENDUMP_PIXELS:

{

int result;

struct wsemul_abortstate abortstate;

abortstate.skip=0;

WSEMULOP(result, scr->scr_dconf, &abortstate, putchar, (scr->scr_dconf->emulcookie, 10, 10, '!', 0x01020000));

return (0);

}

All this does is to call the existing rasops putchar routine with some hard-coded arguments. Specifically, row 10, column 10, character '!', and an attribute byte that specifies red text on a green background.

We can invoke this ioctl with a very simple userland program:

#include <stdio.h>

#include <unistd.h>

#include <sys/ioctl.h>

#include <sys/time.h>

#include <dev/wscons/wsconsio.h>

int main()

{

int res;

struct wsdisplay_screendump_pixels screendump;

res=ioctl (STDIN_FILENO, WSDISPLAYIO_SCREENDUMP_PIXELS, &screendump);

printf ("Ioctl WSDISPLAYIO_SCREENDUMP_PIXELS returned %d\n", res);

}

Running the above program should result in a single exclamation mark being plotted at row 10, column 10, along with the expected output from the printf statement.

Where is the pixel data actually stored?

The main thing that we want to know is the kernel virtual memory address corresponding to the start of the framebuffer. This, along with other useful information such as the height and width of the display, is stored in a struct rasops_info, as defined in rasops.h. Here in this definition we can see the pointer ri_bits, along with ri_width, and ri_height.

What is not necessarily so obvious is how we actually get to the relevant rasops_info from what we are supplied in the wsdisplay_internal_ioctl function where our ioctl kernel code will be inserted.

We're supplied with a pointer to a struct wsscreen, scr. This contains an element scr_dconf, which is a struct wsscreen_internal. This struct wsscreen_internal has an element emulcookie, which is a void pointer but which will point to a struct rasops_screen. Finally, this struct rasops_screen has a pointer to a rasops_info structure.

So the whole chain looks something like this:

struct wsscreen scr -> struct wsscreen_internal * scr_dconf -> void * emulcookie -> struct rasops_info * rs_ri -> u_char * ri_bits

Code quality

At this point, I'd like to take a moment out to say that the rasops code should absolutely not be used a guide to good C programming practices.

As previously mentioned, there is no real documentation. Whereas the manual pages for wscons and wsdisplay do actually explain some details of those subsystems, the manual page for rasops gives an incomplete and therefore inaccurate description of just one of the many structures used, a summary of ri_flag, and a brief synopsis of two functions. This is woefully insufficient documentation for anybody who is new to the rasops codebase, and so the only practical way to get an understanding of it is to study the source directly.

Whilst it's fair to say that the source code is in many ways the ultimate documentation, learning anything from it is not made any easier when it is sparsely commented, and the names of structures and variables don't clearly explain their purpose. To add insult to injury, there are also cases of confusingly similarly named variables with different but related purposes, such as can be seen with the various function pointers in rasops_init. We have a set of pointers under ri->ri_*, and a corresponding set under ri->ri_ops.*. Where is the comment or documentation explaining the reasoning behind this?

The rasops code has clearly grown in complexity over time. Some of the advanced C programming techniques, such as function pointers burried deep within several layers of structures and the use of the C pre-processor to expand function names and share common code between similar functions (*), were probably more manageable without propper documentation when the codebase was smaller. However, at this point the lack of documentation really serves to waste developer time repeatedly re-reading existing code to fully understand it's interactions with the rest of the system, and ultimately slows down the development process.

(*) This can be seen, for example, in rasops1.c and rasops4.c. Both files import rasops_bitops.h, which defines various functions but names them using the NAME macro. So when it's imported from rasops1.c, we get rasops1_copycols, rasops1_erasecols, etc. When it's imported from rasops4.c, we get almost identical code as rasops4_copycols, rasops4_erasecols, and so on. This is a valid technique, and to be fair it is mentioned in a comment at the bottom of both rasops1.c and rasops4.c, but for anybody who is not familiar with the codebase a comment at the top near the function prototypes would be more easily noticed.

For the task in hand, we have little choice but to work with what we're given, (and try to improve it along the way). But take the experience of working with the rasops code as a clear example of why new projects, (and new code added to existing codebases), should be written with care and attention to good organisation and documentation practices.

* Once code starts to grow in complexity, ensure that it is either self-documenting with functions and structures that have a clearly defined and unambiguous purpose, or write and maintain documentation for it, (preferably do both).

* If adding a new feature makes the existing code harder to follow, take the time to re-structure the existing code, (the rotation code is a good case in point here).

Writing our own rasops function

We'll call our new rasops function 'screendump'. The code to call it from the ioctl handler in wsdisplay.c is very simple and almost the same as what we have in the example above:

case WSDISPLAYIO_SCREENDUMP_PIXELS:

{

int result;

struct wsemul_abortstate abortstate;

abortstate.skip=0;

WSEMULOP(result, scr->scr_dconf, &abortstate, screendump, (scr->scr_dconf->emulcookie, ((struct wsdisplay_screendump_pixels *)data)));

return (0);

}

The only parameters we need to pass are 'emulcookie', which represents the specific display device we want to talking to, and the whole wsdisplay_screendump_pixels structure that we will eventually pass back to userland.

Important note

The cookie 'emulcookie', is not the same cookie that is eventually passed to the low-level rasops routines. This is a good example of where using similar function names and not adequately documenting complex chains of function calls can easily cause confusion for somebody trying to understand the code simply by reading the source.

Recall our trivial example above, where we called the putchar routine:

WSEMULOP(result, scr->scr_dconf, &abortstate, putchar, (scr->scr_dconf->emulcookie, 10, 10, '!', 0x01020000));

Searching the source code, it's easy to find where the low-level putchar functions are defined. In fact, there are separate functions for each bit-depth, (which should not come as a surprise), so for example the code to implement putchar on 32bpp displays is in rasops32.c.

Reading the function definition for rasops32_putchar, we can see that it accepts a cookie, a row value, a column value, a character, and an attribute. These might seem like exactly the same values that we are passing to the WSEMULOP macro, but closer inspection shows us that the low-level putchar function is expecting the cookie to be a pointer to a struct rasops_info, whereas the cookie supplied to WSEMULOP varies depending on what emulation we are using. In most cases this will be the vt100 emulation code, so the cookie will be a struct wsemul_vt100_emuldata. As long as it has a struct wsdisplay_emulops * emulops, WSEMULOP doesn't care, but it's certainly never a struct rasops_info.

Since when we call from within the ioctl handler we don't have an emulation-specific structure to work with, we pass the struct wsscreen_internal that we have as scr_dconf.

Fun fact

The variable 'edp' is frequently used throughout the wscons code as a pointer to a structure that is specific to the particular emulation in use.

Reading the source code doesn't tell us what 'edp' actually stands for, since it's not documented anywhere.

We can only guess at it's true meaning, but it seems reasonable to assume that it actually stands for Emulation Data Pointer.

Next, we need to add a function pointer for our new function to struct wsdisplay_emulops, defined in wsdisplayvar.h:

int (*screendump)(void *c, void *data);

Note: Although we could add this extra function pointer anywhere within the definition of struct wsdisplay_emulops, some of the device drivers, (such as the vga driver), have their wsdisplay_emulops structure hard-coded as a constant. As a result, any re-ordering of the existing entries would break those drivers. To avoid this, add the new entry to the end of the current list, after unpack_attr.

At this point we can finally move to the rasops code itself.

The generic rasops functions, in other words those which are not specific to a particular bit-depth, are in rasops.c. This is where we will put our screendump function, as although the format of the data will obviously be different depending on the bit-depth, actually reading out the entire content of the framebuffer will can be done in exactly the same way regardless.

If we were going to do any post-processing of the pixel data within the new rasops screendump function itself, we might want to write separate functions for each bit-depth. However, this functionality is much better done in userspace - we want to do as little as possible within the kernel.

The following code is what we are going to add to rasops.c:

/*

* Screendump

*/

int

rasops_screendump(void * cookie, void * data)

{

#define screendump ((struct wsdisplay_screendump_pixels *)data)

#define FB_SIZE (ri->ri_stride * ri->ri_height)

struct rasops_info * ri;

unsigned char * rp;

ri=(struct rasops_info *) cookie;

rp=(unsigned char *)(ri->ri_bits);

/* Fill in various display parameters to pass back to wsdisplay, and eventually to userland. */

screendump->width=ri->ri_width;

screendump->height=ri->ri_height;

screendump->stride=ri->ri_stride;

screendump->depth=ri->ri_depth;

/* Copy the raw framebuffer data to userland. */

copyout (rp, screendump->data, FB_SIZE);

return (0);

}

Obviously we should add a function prototype near the top of the file along with the existing ones, underneath the 'generic functions' comment:

int rasops_screendump(void *, void *);

The new function should be fairly self-explanatory. We cast the supplied cookie to a struct rasops_info, from which we can obtain the width, height, stride, and depth of the display. These values are assigned to the corresponding members of the struct wsdisplay_screendump_pixels that will soon be passed back to the calling program.

Finally, we use the kernel copyout function to copy the pixel data directly from the framebuffer to the userland memory address supplied in the screendump->data pointer.

Fun fact

Calculating the screen size

You might have expected that we'd calculate the size of the display in bytes by using width * height * depth, (obviously with the necessary conversion to ensure that depth is expressed in bytes rather than bits). Instead we're using this 'stride' parameter, so what exactly is 'stride', and how does it differ from the width?

For some display sizes, the stride will indeed be the same as width * height * depth. This is typically when the width in pixels is a whole multiple of 32 or some other value required for memory alignment. So a 1920 × 1080 display at 32bpp will usually have a stride value of 1920 × 4 = 7680, and each line of pixel data will immediately follow the previous one.

Not so for resolutions such as 1366 × 768. In this case, at 32bpp, a single line of pixels would be 5464 bytes. This doesn't align to a 128-byte boundary, (32 pixels * 32 bpp = 128 bytes), so we leave some unused bytes at the end of the line and start the next line of pixel data after, say, 5504 bytes. In effect, the screen has a width of 1376 pixels, (5504 bytes / 32 bpp = 1376 pixels), but only the first 1366 are displayed. Visually, if we create a screendump from the full dataset, we will see an unused band of 10 pixels down the right hand side. This might be filled with 0x00 bytes, or might contain random data depending on the behaviour of the underlying graphics hardware.

Our function dumps the whole contents of the framebuffer memory, and passes both the stride, (actual width), and width, (intended visual width), back to userland. The decision as to whether to crop any extraneous pixels from the final output can then be made from the calling program.

On a system where the graphics hardware operates in different modes, each with a different memory layout for the framebuffer, it's plausible that these extraneous pixels might contain old data preserved from the contents of the display when it was in a different mode in which those locations were actually active pixels. Potentially, on a multi-user system this could be considered a security issue since it could in theory leak private data.

If we wanted to avoid this, we could modify the new kernel code to first copy the framebuffer contents to a kernel-allocated memory buffer, then overwrite the non-active pixels with 0x00 bytes before copying that buffer back to userspace.

Setting the function pointers and additional vcons consider­ations

Now that we have our new function in place, we're almost ready to go.

The initial assignment of the function pointers is performed in rasops_reconfig, which is called from rasops_init.

It might seem that we just need to set the new function pointer to point to rasops_screendump, by placing this assignment underneath the 'Fill in defaults' comment:

ri->ri_ops.screendump = rasops_screendump;

But this is not enough. Now we have to deal with a strange complexity of the rasops code, which can be seen if we continue reading the source for rasops_init past it's call to rasops_reconfig.

The pointers in ri->ri_ops are copied elsewhere, and then if the RI_VCONS flag is set then the original values are replaced with calls to similarly named functions, which differ by having 'vcons' in their names. So rasops_copycols becomes rasops_vcons_copycols, and so on. There is also a further check for the RI_WRONLY flag, so that further replacement routines can be substituted that avoid writing to the framebuffer.

Our screendump routine doesn't really need any additional code to be vcons-aware, and it obviously wouldn't make much sense to try to create a version that doesn't read from the framebuffer, so we don't need any of this added complexity for our function to work. However the easiest way to ensure that our new code works as expected is to provide a rasops_vcons_screendump wrapper function that just calls rasops_screendump, and fill in the extra function pointers as required.

The rasops_vcons_screendump does nothing other than to call rasops_screendump via a new rs_ri->ri_screendump function pointer:

int

rasops_vcons_screendump(void * cookie, void * data)

{

struct rasops_screen *scr = cookie;

return scr->rs_ri->ri_screendump(scr->rs_ri, data);

}

Of course, we should add the function prototype as well, along with the existing ones for the other vcons functions:

int rasops_vcons_screendump(void *, void *);

We also need to add that new function pointer to the definition of struct rasops_info in rasops.h:

int (*ri_screendump)(void *, void *);

Next we modify rasops_init to copy the original pointer:

ri->ri_screendump = ri->ri_ops.screendump;

Finally, we also modify rasops_init to re-assign ri->ri_ops.screendump to the vcons version:

ri->ri_ops.screendump = rasops_vcons_screendump;

At this point, we've finished the kernel code. After re-compiling and re-booting into the kernel we can continue on to the userspace program that calls it.

Userland code

Here is a sample program that calls WSDISPLAY_SCREENDUMP_PIXELS, and converts the data it receives from the common 32bpp BGR0 format to a 24-bit RGB ppm file:

#include <stdio.h>

#include <unistd.h>

#include <stdlib.h>

#include <sys/ioctl.h>

#include <sys/time.h>

#include <dev/wscons/wsconsio.h>

#include <fcntl.h>

int main()

{

int fd_out;

int header_len;

int pos_in;

int pos_out;

int res;

int x;

int y;

struct wsdisplay_screendump_pixels screendump;

struct wsdisplay_fbinfo dispinfo;

unsigned char * header;

unsigned char * file_out;

unsigned char * processed_pixel_data;

/* Call WSDISPLAYIO_GINFO to get the size of the framebuffer. */

res=ioctl (STDIN_FILENO, WSDISPLAYIO_GINFO, &dispinfo);

if (res == -1) {

printf ("Ioctl WSDISPLAYIO_GINFO not supported for this device\n");

return (1);

}

/* Allocate the correct amount of memory for the screendump. */

screendump.data=malloc(dispinfo.stride * dispinfo.height);

res=ioctl (STDIN_FILENO, WSDISPLAYIO_SCREENDUMP_PIXELS, &screendump);

if (res == -1) {

printf ("Ioctl WSDISPLAYIO_SCREENDUMP_PIXELS not supported for this device\n");

return (1);

}

printf ("Ioctl WSDISPLAYIO_GINFO returned %d\nwidth %d, height %d, stride %d, depth %d\n", res, dispinfo.width, dispinfo.height, dispinfo.stride, dispinfo.depth);

printf ("Ioctl WSDISPLAYIO_SCREENDUMP_PIXELS returned %d\nwidth %d, height %d, stride %d, depth %d\n", res, screendump.width, screendump.height, screendump.stride, screendump.depth);

if (screendump.depth != 32) {

printf ("Only 32bpp input data is supported.\n");

return (1);

}

/*

* Simple conversion to 24-bit RGB

*

* This assumes that the raw screendump data is in BGR0 format.

*/

processed_pixel_data=malloc(screendump.width * screendump.height * 4);

pos_out=0;

pos_in=0;

for (y=0; y<screendump.height; y++) {

for (x=0; x<screendump.stride/4; x++) {

if (x<(screendump.width)) {

*(processed_pixel_data+pos_out++)=*(screendump.data+pos_in+2);

*(processed_pixel_data+pos_out++)=*(screendump.data+pos_in+1);

*(processed_pixel_data+pos_out++)=*(screendump.data+pos_in);

}

pos_in+=4;

}

}

header=malloc(1024);

header_len=sprintf (header, "P6\n%d %d\n255\n", screendump.width, screendump.height);

fd_out=open ("rgb_screendump.ppm", O_CREAT | O_WRONLY, 0644);

write (fd_out, header, header_len);

write (fd_out, processed_pixel_data, pos_out);

close (fd_out);

return (0);

}

All we do here is to cycle over the raw framebuffer data one pixel at a time, and re-arrange each set of four bytes into a three byte RGB value.

For each line, once we reach the width as specified in screendump.width, further data is ignored until the end of the line as defined by the value of screendump.stride.

The resulting ppm file is written to rgb_screendump.ppm in the current directory, and can be viewed with any standard image viewer that supports the ppm format.

If the colors are inverted, or one of the red, green, or blue channels is missing altogether, then the input is not in BGR0 format. In this case, the code that re-arranges the bytes will need to be modified.

Compatibility with other graphics drivers

The new ioctl that we've added to wsdisplay.c can potentially be called even when we are not using graphics hardware that uses the rasops routines. A few different graphics drivers exist where this is the case, most of them for uncommon display devices, but one exception that we can easily use for testing purposes is the plain vga text mode driver.

With our code in it's current form, if we try to access the WSDISPLAYIO_SCREENDUMP_PIXELS ioctl whilst running with the vga driver, we'll cause a kernel panic:

attempt to execute user address 0x0 in supervisor mode

kernel: page fault trap, code=0

This shows that we've tried to execute code at address 0x0. This shouldn't come as much of a surprise, since we added a new function pointer to struct wsdisplay_emulops and this hasn't been initialized by the code in vga.c, (which initializes vga_emulops as a constant with the function pointers hard-coded).

Since the vga driver declares this struct wsdisplay_emulops in global scope, (in other words, outside of any function), the C standard guarantees that it's elements will be initialized to NULL.

However other device drivers don't necessarily do this, and instead declare their struct wsdisplay_emulops in a similar way to the code in rasops.c.

The upshot of this is that we can't simply change our ioctl handling code to check for a NULL function pointer:

case WSDISPLAYIO_SCREENDUMP_PIXELS:

{

int result;

struct wsemul_abortstate abortstate;

if (scr->scr_dconf->emulops->screendump == NULL) {

return (EINVAL);

}

abortstate.skip=0;

WSEMULOP(result, scr->scr_dconf, &abortstate, screendump, (scr->scr_dconf->emulcookie, ((struct wsdisplay_screendump_pixels *)data)));

return (0);

}

This would certainly work to catch the case of accessing the function whilst using the vga driver, but it's not a general solution.

The definitive and most compatible way to fix this, of course, is to add a screendump function of some sort to each of the display drivers, even if it just returns EINVAL without providing any framebuffer data.

We can easily do this for the vga driver:

int vga_screendump(void * cookie, void * unused)

{

return (EINVAL);

}

Adding this function, along with it's function prototype and an entry for it in the declaration for vga_emulops will cause our call to WSEMULOP in the ioctl handler to return EINVAL in the result variable.

Now we just need to change our code to return this value instead of always returning zero:

case WSDISPLAYIO_SCREENDUMP_PIXELS:

{

int result;

struct wsemul_abortstate abortstate;

abortstate.skip=0;

WSEMULOP(result, scr->scr_dconf, &abortstate, screendump, (scr->scr_dconf->emulcookie, ((struct wsdisplay_screendump_pixels *)data)));

return (result);

}

Of course, since our userland screen dumping program always checks for a successful call to WSDISPLAYIO_GINFO before calling WSDISPLAYIO_SCREENDUMP_PIXELS, running it unmodified even with the vga driver in use will not trigger a kernel panic anyway.

We could even write a routine to produce graphical output from the contents of the vga text display by rendering each character manually ourselves, even though the underlying hardware was not operating in a graphical mode.

Downloads

A tar archive containing the kernel patches and userland utilities can be downloaded here.

All of the files within the tar archives are signed using our signify key.

Assuming that you have the tar archive and our public key in /root, you can apply the kernel patch as follows:

# cd /root

# tar -xf console_screendumps.tar

# cd /usr/src/

# signify -Vep /root/exoticsilicon.pub -x /root/screendump_patches/screendump_kernel_patches.sig -m - | patch

At this point you can re-compile and re-install the kernel in the normal way.

You should also copy the modified wsconsio.h header file to the correct place in the /usr/include/ hierarchy:

# cp -p /usr/src/sys/dev/wscons/wsconsio.h /usr/include/dev/wscons/

If you don't have our signify key, (why not?), you can also apply the patch directly with the patch utility:

# patch < /root/screendump_patches/screendump_kernel_patches.sig

The userland utilities can be compiled with no special options, after verifying their checksums:

# cd /root/screendump_utilities/

# signify -Cp /root/exoticsilicon.pub -x checksums.sig

# cc -o screendump_graphical screendump_graphical.c

# cc -o screendump_text screendump_text.c

Note that the screendump utilities can be run as a regular user, and do not require root permissions to perform screendumps of the display associated with standard input. Performing screendumps against other devices may require additional access permissions.

Summary

Today we've seen how to implement two different types of screendumping facility in OpenBSD by adding new ioctls to the wscons subsystem along with supporting code in the kernel and userland programs.

Website copyright 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023, Exotic Silicon. All rights reserved.



from Hacker News https://ift.tt/hIadmHb

Ask HN: Getting back into an IC role as a manager

I've been in senior manager and director roles for the last 7 years. I miss coding at work and have enjoyed the coding I've been doing outside of work a lot more than anything substantial I've done as a manager. However, I also increasingly feel that my concrete tech chops are getting outdated or are just fading away. I used to be an excellent full-stack Rails engineer in full control of anything from AWS infrastructure to DB, Rails app server and front end. I am increasingly tempted to take a substantial, temporary pay cut moving back into an IC role. However, that would need to be at a different company, since I have zero interest working on anything my current employer has.

Has anyone made this jump back into an IC role? How did you find a role? What did you do to get the role?

Edit: Most importantly: Would you do it again?



from Hacker News https://ift.tt/QpPomu5

The Law Does Not Require Legalese

The Law Does Not Require Legalesea reassuring dive into contract interpretation

People think legal writing sucks because it has to. All the terms they see hurt their eyes and fog their brains. But all the lawyers seem to do it. It must just have to be that way. To be precise. Enforceable. Respectable. Something.

Wrong.

Courts do follow legal rules for reading contract terms. There are a bunch of them. But the most important ones all say exactly the opposite. They say your language rules, not ours.

Plain Meaning

The most important concept for interpreting contracts is “plain meaning”, sometimes called “ordinary meaning”. “Plain” and “ordinary” mean what you think here. No surprises.

Here’s the rule in California, which has it spelled out in its civil code:

The words of a contract are to be understood in their ordinary and popular sense, rather than according to their strict legal meaning; unless used by the parties in a technical sense, or unless a special meaning is given to them by usage, in which case the latter must be followed.

Don’t think “technical sense” is any gaping loophole. The very next section:

Technical words are to be interpreted as usually understood by persons in the profession or business to which they relate, unless clearly used in a different sense.

Ordinary words, popular meaning. So much for mandatory lawyerspeak.

Intent at the Time

Plain meaning is a tool, a tactic. For what? Under the law, a court’s job is clear: figuring out what both sides meant by the words they chose.

Again from Cali’s code:

A contract must be so interpreted as to give effect to the mutual intention of the parties as it existed at the time of contracting, so far as the same is ascertainable and lawful.

Notice what’s missing here: any mention of lawyers. The question is what the parties intended, not what their lawyers intended. After all, there may not have been any lawyers. Lawyers aren’t required to make contracts. Lawyers are just there to help. Hopefully.

But notice what is mentioned above: timing. Courts ask what both sides meant back when they agreed. Not what they say they meant now, when they’re in some lawsuit fighting each other about it. If we ask them now, they’ll say both sides intended whatever wins the case for them. Or have their lawyers say it.

Plain meaning and parties’ intent are the core of contract interpretation. If you were going to sum up contract interpretation in a couple hundred words, these are the phrases you’d absolutely have to get in there.

I am feeling long-winded all of a sudden.

Tuning

There are more rules, of course. The whole contracts part of the California civil code is worth skimming—fairly quick and easy to do. If you do skim, keep an eye on the notes below the sections, which work a bit like legal git blame:

1635. All contracts, whether public or private, are to be interpreted by the same rules, except as otherwise provided by this Code.

(Enacted 1872.)

Fun Fact: All the language I’ve quoted so far was passed in in 1872. You could probably tell the sentences felt a bit stodgy. But they were also relatively manageable, with just one sentence per section. The endless run-on sentences and (a) imposing (b) enumerated (c) lists came in later centuries. No further comment.

Setting aside the new carve-outs for specifics kinds of contracts—sales tax reimbursement, heavy equipment rentals, real estate sales—we can spot two themes running through the general sections. First, where there’s a gap in what a contract says, the law tries to fill in what most would probably expect there. Second, the rules help keep the lawsuit process practically workable.

In California, the most important process defenses read like this:

When a contract is reduced to writing [written down —KEM], the intention of the parties is to be ascertained from the writing alone, if possible…

The whole of a contract is to be taken together, so as to give effect to every part, is reasonably practicable, each clause helping to interpret the other.

Several contracts relating to the same matters, between the same parties, and made as parts of substantially one transaction, are to be taken together.

More 1872? You bet.

In modern English: When a contract’s written down, try to decide what it means just based on the terms. All of them in context, not just snippets. If several contracts were all part of one big deal, read them together.

In legalese, for nerds going deep? Search “parol evidence rule”—name from old Law French. Then try “four corners”.

Implementations and names aside, what do these rules do, functionally? They restrict the evidence we can use to argue intent.

Ruling evidence and arguments out of bounds risks excluding something important. We could imagine situations where a court could need something beyond the terms alone to truly understand what was meant way back when. But just as we can expect both sides of a contract lawsuit to argue whatever meaning wins for them, we can also expect them to drag in literally whatever they think helps their case.

This would make it hard to know for sure what you agreed to looking back, since you’d need not just a copy of your contract but records of whatever else the other side might use to try and bend or break them. It would also turn contract lawsuits into even longer, more expensive, less well bounded slug fests, which could easily gum up the courts.

Actually, that’s kind of California’s rep on contracts cases. It has the same basic rule as most other states, but its courts are somewhat more willing to hear about fraud, deceit, and other exceptions that undermine the terms. Court backlogs are crazy. If you’re playing for literally all the points in this blog post, try to point out the civil code section that heavily foreshadows this turn of legal events.

Write the Future

California being California aside, we’ve seen general contract rules sacrifice some accuracy so we can know what we agreed to or fight out in court relatively quick. This trade-off, not any quizzical dictate to write like a whole shelf of hornbooks fell on your head in some dusty law library, accounts for the inherent specialness of contract terms.

Words in contracts bear more weight. Not just because they describe what someone else—a court—ought to do, but because of the rules those courts have for reading them. Terms can’t rely on readers to fill from context, mine personal history, or interrupt with clarifying questions, like a good friend texting on a Thursday night. Contract words must be sturdy and stand on their own. Not sound older. Not be “settled”. Not seem fancy somehow, like they’re wearing tiny black Oxfords on all their little serifed feet.

Load-bearing, free-standing writing is hard brain work no matter whose brain does it. The skill of a good contract lawyer lies in doing it a lot, writing as clearly and completely as possible where the effort serves the client.

Judging what to cover and when it’s worthwhile is more than half the game. It takes knowing the business, the deal, the industry, often way more than the law. The law usually matters most through its defaults: what courts will fill in on their own if you don’t cover it. And, occasionally, for remembering what you can’t say, because it’s illegal. Not how to say all the things that are enforceable. You can say those in English and the jargon of your field.

Even with the best drafting help, sometimes it’s just not clear what terms meant or how to apply them. It’s also normal and common to mean to leave some points vague, or leave them out entirely, when pinning them down could delay or kill a deal. And sometimes terms that seemed totally predictable one day become utterly ambiguous when applied to some new situation. Nobody sees the future, but everyone writing contracts is trying to boss it around a little.

There are rules for vagueness and ambiguity, of course. More about who can argue what, and how. If all else fails, there are even some deep fallbacks, like interpreting wonky language against whoever wrote it. But focusing on those exceptions is a bit like Googling brain surgery when you could be buying a helmet. The clearest, most reliable way to avoid some judge going haywire on you is to write out what you want clearly and get the other side to sign off on it.

For the record, the worst way is writing what you mean in affected lawyerish, which is how sad French clowns would talk if they weren’t also mimes. Second worst is expecting some lawyer to read your mind, then paying them to write it down in a language you can’t or won’t try to read. “Here is your card, sir. No, you can’t flip it over.”

My law school barely had a class on contract drafting. If they could read minds, they’d never ask me for a dollar.

Moral?

Expect better from lawyers. We’ve been glazing eyes ahead of sharper, more literate minds than our own for many generations. But only because we’re lazy and insecure as the rest of you, and you let us.

Please stop. We shovel this shit all day.

Your thoughts and feedback are always welcome by e-mail.

back to topedit on GitHubrevision history



from Hacker News https://ift.tt/7tfdup8

Monday, January 30, 2023

Towards A Token-Free Future In NLP (2022)

For an AI model to understand languages, the most prominent approach so far is to train a so-called language model on a massive amount of text and let the model learn from context the meaning of words, and how those words compose sentences. For an AI model to recall and learn the relation between words, a vocabulary needs to be embedded in the model and then store these mappings as parameters.

Since there are over 170 000 words in the English dictionary and the model needs to learn the weights of each word, it is not feasible for it to store all of those in its vocabulary.

To decrease the number of words to learn, they are instead split into sub-words or tokens. We can use a so-called tokenizer to decide these splits/tokens should be done in the text. A tokenizer is trained to identify a certain number of words from a large corpus. The tokens it learns to identify will be the longest and most common sub-word up to some fixed token vocabulary size; usually 50.000 tokens. 

When we train a language model, we can use the tokenizer to reduce the number of tokens the model needs to store in memory and, therefore, reduce the model size significantly. This is important since reduced model size means faster training, lower training cost and less expensive hardware is required.

Below is an illustration of how a tokenizer splits a sentence into a sequence of tokens.



from Hacker News https://ift.tt/2GPSfjK

Stephen and Ayesha Curry oppose multi-family development near their $30M home

Famed NBA marksman Stephen Curry appears more comfortable with 3-point daggers than 3-story developments.

Along with his influencer wife Ayesha, Curry has objected to the establishment of multi-family housing on a property next to his sprawling California mansion, according to reports.

In an email, the couple told officials in Atherton — one of the nation’s most exclusive enclaves — that the three-story town houses would encroach on their privacy.

Routinely vocal on matters of social justice, the Bay Area power couple indicated that joining the well-heeled chorus of objection made them uneasy.

“We hesitate to add to the ‘not in our backyard’ (literally) rhetoric, but we wanted to send a note before today’s meeting,” they wrote on Jan. 18. “Safety and privacy for us and our kids continues to be our top priority and one of the biggest reasons we chose to live in Atherton.”

Home to tech moguls, athletes and international business titans, Atherton officials will reluctantly submit a plan to the state this week outlining their efforts to increase housing density.

Stephen and Ayesha Curry
Stephen and Ayesha Curry are opposing a multi-unit residence that abuts their home.
Jordan Strauss/Invision/AP

The parcel in question — which abuts the Curry residence — is set to be rezoned in order to accommodate several multi-family units sought by the owner.

“With the density being proposed for 23 Oakwood, there are major concerns in terms of both privacy and safety with three-story townhomes looming directly behind us,” their email read.

The couple noted that the development would not add to low-income housing to the area, only increase density.

If their municipal buzzer beater falls short, the Currys asked the town to erect “considerably taller fencing and landscaping to block sight lines onto our family’s property.”

23 Oakwaood Ave. in Atherton
23 Oakwood Ave. in Atherton, Calif.
Google Maps

The couple recently sold their prior Atherton home for $31 million and bought their current 17,000 square foot spread for $30 million, according to reports.

Other local heavyweights — including billionaire investor Marc Andriessen — have previously lobbied against the introduction of more affordable housing into the area.

With a median home price of more than $7 million, Atherton ranked first on Forbes’ priciest zip code list last year.



from Hacker News https://ift.tt/C1IPvnX

Open source implementation of Google's MusicLM in PyTorch

MusicLM - Pytorch (wip)

Implementation of MusicLM, Google's new SOTA model for music generation using attention networks, in Pytorch.

They are basically using text-conditioned AudioLM, but surprisingly with the embeddings from a text-audio contrastive learned model named MuLan. MuLan is what will be built out in this repository, with AudioLM modified from the other repository to support the music generation needs here.

Appreciation

  • Stability.ai for the generous sponsorship to work and open source cutting edge artificial intelligence research

Citations

@inproceedings{Agostinelli2023MusicLMGM,
  title     = {MusicLM: Generating Music From Text},
  author    = {Andrea Agostinelli and Timo I. Denk and Zal{\'a}n Borsos and Jesse Engel and Mauro Verzetti and Antoine Caillon and Qingqing Huang and Aren Jansen and Adam Roberts and Marco Tagliasacchi and Matthew Sharifi and Neil Zeghidour and C. Frank},
  year      = {2023}
}
@article{Huang2022MuLanAJ,
    title   = {MuLan: A Joint Embedding of Music Audio and Natural Language},
    author  = {Qingqing Huang and Aren Jansen and Joonseok Lee and Ravi Ganti and Judith Yue Li and Daniel P. W. Ellis},
    journal = {ArXiv},
    year    = {2022},
    volume  = {abs/2208.12415}
}


from Hacker News https://ift.tt/67koM2V

Ask HN: Did improving conversation and listening skills win you anything big?

We hear that improving how we converse or listen one to one with people says a lot about how we treat or empathize with them in real life situations. Is that actually true?

What other aspects of life improvements have you seen with improving your skills in this domain?



from Hacker News https://ift.tt/5oXRZym

The Last Mustard Maker in Dijon

On a sunny afternoon in the heart of Dijon, just steps from the lucky stone owl that gives rue de la Chouette its name, the last Dijon mustard maker in the city has been at work for just under an hour. By the time I step into the diminutive shop, Nicolas Charvy has already soaked the tiny mustard seeds in a mixture of water, salt, vinegar, and white wine to make what he terms “our verjuice,” a stand-in for the more traditional juice of the tart Bourdelas grape (a historic variety that once grew throughout Burgundy, but has now been largely abandoned, in part due to the fact that, according to Les Cépages, it makes wine that is “frankly bad”).

In the middle of the afternoon, the Edmond Fallot shop is bustling with activity: Tourists fill their baskets with local specialties such as gingerbread or crème de cassis, but they mainly flock to the mustard, available in a range of varieties. In the heart of the small store, Charvy is hard at work, pouring the soaked mustard seeds into a custom-made stone-grinder, which dominates the space. A thick paste oozes in irregular dollops from the grinder’s spout, plopping into a large ceramic jar placed underneath. As tempting as it looks, Charvy tells me, it’s far from palatable: It will take at least a week of fermenting before the natural spice of the mustard overtakes its bitterness, and it will be ready to enjoy.

Charvy is the latest in a long line of local mustard makers in Dijon, a status first protected here in the 1600s. Following the 2009 closure of the Amora-Maille factory, he also became the last.

Fallot sources its mustard seeds from Burgundy farms, a rarity among Dijon mustard producers.
Fallot sources its mustard seeds from Burgundy farms, a rarity among Dijon mustard producers.

If mustard has long been linked to Dijon, it’s mainly thanks to the local availability of mustard seeds, first coplanted with grapevines by ancient Romans and persisting thanks to 17th-century charbonniers, who produced coal in open fields, providing natural fertilizer for cruciferous plants such as mustard. But following World War II, farmers turned instead to the production of botanically similar (and subsidized) colza, and Burgundian mustard seed cultivation fell nearly into extinction.

It was thanks in large part to efforts by Charvy’s business partner, Marc Désarménien, the current owner of the family-run Moutarderie Edmond Fallot, that the trade has been recovered, with about 300 independent farmers cultivating mustard across 6,000 hectares of Burgundian land, mainly in the Côte-d’Or.

Despite being a Dijon native, Charvy did not always intend to be a moutardier. After a first career in IT, he transitioned to work purveying local specialties ranging from wine to gingerbread at the nearby shop B Comme Bourgogne. It wasn’t until 2014 that he teamed up with Guillaume Vieillard and Désarménien to open this boutique—a satellite of the nearly two-centuries-old Moutarderie Edmond Fallot—and restore mustard-making to its rightful place in the heart of the historic city.

Dijon mustard is traditionally a blend of ground mustard seeds, salt, water, and white wine vinegar or <em>verjus</em>, the juice of tart grapes.
Dijon mustard is traditionally a blend of ground mustard seeds, salt, water, and white wine vinegar or verjus, the juice of tart grapes.

Oddly, Fallot has never been a Dijon-based brand. Founded in nearby Beaune, 50 kilometers away, by Léon Bouley in 1840, the company was purchased by Désarménien’s maternal grandfather, Edmond Fallot, in 1928. It has, however, always been a bastion of the recipe named for Dijon but beloved throughout Burgundy. These days, at its flagship factory, the company still relies on time-tested stone-grinding techniques that notably allow for cold processing, a boon for the heat-sensitive seeds. As a result, and as compared to other local Dijon mustards such as Maille or Amora, Fallot stands out for its slightly grainier texture and more potent flavor.

Unlike Désarménien, Charvy does not come from a mustard-making dynasty. Despite recently being sworn in as a member of the confrérie de la moutarde—the brotherhood of mustard—his career as a maître moutardier seems to be something the erstwhile IT professional stumbled into nearly by accident. But his previous experience has lent him a natural predilection for problem-solving that’s useful given the trial-and-error nature of his work.

“Each mustard, each batch, is a little bit different,” he says, evoking the “small adjustments” he is frequently called to make.

“Mustard production is a balance of the height [of the stone], of energy, and of the quantity of seeds you use,” he says. “That all contributes to getting to a proper mustard.”

Even though the freshly made mustard looks delicious, it needs a week of fermenting before it's palatable.
Even though the freshly made mustard looks delicious, it needs a week of fermenting before it’s palatable.

Today’s batch (108, if you’re counting), however, is proving to be far from proper, emerging far too runny from the spout. But Charvy is unperturbed.

“I add some more seeds, I adjust it a bit,” he says with a shrug and a smile. “It takes time to get to the right consistency. We’ll need an hour or so for it to be perfect.”

This estimate stems from experience rather than any formal training. Charvy’s crash-course in mustard-making took place at Moutarderie Edmond Fallot’s flagship factory, where he learned the time-tested recipe and sought-after texture. But to hear him tell it, this initial introduction was just the tip of the iceberg. In Beaune, after all, mustard is being made on a far larger scale: about 20,000 jars of mustard per day, amounting to a yearly average of 2,300 tons, sold both at the company’s Dijon store and in specialty food shops and grocery stores across France. Charvy, by comparison, makes just 60 to 80 kilos at a time, a rhythm that, he says, has led him to be far more “interventionist” in tinkering with his recipe on each of his twice-monthly visits to the shop.

And he’s not just making mustard on those visits, either. “He’s also our electrician,” pipes in Florine Humbert, store manager.

Humbert and Charvy make a perfect pair of opposites, Charvy’s reserved, shy smile juxtaposed against Humbert’s bubbly exuberance. But they share more than a workplace. Humbert, too, came to mustard after a first career in accounting.

Humbert, left, and Charvy pose in front of the shop’s stone-grinder.
Humbert, left, and Charvy pose in front of the shop’s stone-grinder.

“I never thought to myself, growing up, ‘What if I worked with mustard?’” she says. But these days, she’s proud of the path her career has taken her on. “Especially with the artisan process. We really respect the work of master mustard makers of yore.”

They also seek to show it off. Charvy’s work at the shop is spurred less by the company’s production needs and more by a desire to return to tradition, both in bringing the time-tested craftsmanship to the heart of the city and, perhaps most importantly, in sharing these techniques with interested visitors. Locals and tourists alike linger by the massive machine as Charvy works, sometimes watching shyly, sometimes stepping forward with questions or simply to take a photo.

Compared to well-known Dijon mustard brands such as Amora and Maille, Fallot is relatively tiny—perhaps another reason why a presence in the center of Dijon was so important.

But the company’s smaller size has also been a boon, making it far easier to transition to exclusively Burgundian mustard seeds (a rarity in the French Dijon mustard industry, which currently sources about 80 percent of its seeds from Canada). Fallot’s commitment to local seeds meant that when international supply-chain disruptions left French mustard aisles empty this past summer, Fallot was the last Dijon mustard purveyor standing.

Of course, as a result, demand spiked and Fallot’s shelves emptied as well. Humbert spent the summer fending off miffed regulars.

“‘There’s no more Dijon mustard…even for us Dijonnais?’” she recalls them demanding.

This August, she even opted to close the shop for three days when the only available flavor of the 37 varieties they produce was a limited-edition cacao bean.

“It’s not everyone’s cup of tea,” she admits.

These days, however, stocks have returned at the shop. The shelves are lined with flavors ranging from mustard spiked with gingerbread spice to a sweet-and-savory marriage of honey and balsamic vinegar, the latter of which both Charvy and Humbert cite as their favorite.

Humbert looks over the array of mustard flavors offered at the store.
Humbert looks over the array of mustard flavors offered at the store.

But the shop isn’t quite back to business as usual.

“We have to limit people to two jars per flavor per household,” says Humbert. “We want to make sure there’s enough for everyone.”

While quantities remain limited, Charvy, at least, is finally back to producing his signature: a coarse-ground mustard sold in terra cotta pots complete with an old-fashioned cork stopper, the label proudly boasting the AOC Meursault wine at its base.

“Since it’s a prestigious shop, we used a prestigious white wine,” says Humbert, who notes that the mustard also stands out thanks to its texture, which is grainier than most produced by Fallot. At the Dijon shop, sieving is foregone due to space constraints, resulting in a mustard halfway between smooth and grainy, with a profound spiciness and that balanced acidity Dijon mustard fans love.

“It’s unique to this shop,” says Humbert proudly. “You can’t find it anywhere else. Not in Beaune, not anywhere.”

Gastro Obscura covers the world’s most wondrous food and drink.
Sign up for our email, delivered twice a week.



from Hacker News https://ift.tt/boJ1WX9

Sunday, January 29, 2023

Determine durations with monotonic clocks if available

Determine durations with monotonic clocks if available

Sometimes, on a lazy weekend afternoon, I use apt-get to pull down the source of something and start grepping it for things that are bound to be interesting. This is one of those afternoons, and I found something silly. While looking for uses of time_t in bash, I found a variable called "time_since_start". Uh oh.

bash supports a dynamic variable called "SECONDS" (bet you didn't know that - I didn't), and it's documented as "the number of seconds since shell invocation". I'm sorry to say, that's not quite true. You can totally make it go negative since it's based on wall time. Just set the system clock back.

root@rpi4b:/tmp# systemctl stop chrony
root@rpi4b:/tmp# echo $SECONDS
11
root@rpi4b:/tmp# date -s "2023-01-01 00:00:00Z"
Sat 31 Dec 2022 04:00:00 PM PST
root@rpi4b:/tmp# echo $SECONDS
-2500987

That's an extreme demonstration, but backwards-going wall time happens every time we have a leap second. Granted, we're in a long dry spell at the moment, but it'll probably happen again in our lifetimes. The difference there is just one second, but it could break something if someone relies on that value in a shell script.

Or, how about if the machine comes up with a really bad time for some reason (did your hardware people cheap out on the BOM and leave off the 25 cent real-time clock on the brand new multi-thousand-dollar server?), the shell gets going, and later chrony (or whatever) fixes it? Same deal, only then it might not be a second. It might be much more.

In the case where the machine comes up with a past date and then jumps forward, SECONDS on a still-running shell from before it's fixed will be far bigger than it should be. I'm pretty sure every Raspberry Pi thinks it's time=0 for a few moments when it first comes up because there's no RTC on the board. Run "last reboot" on one to see what I mean.

I should also mention that bash does other similar things to (attempt to) see how much time has passed. Have you ever noticed that it'll sometimes say "you have new mail", for those rare people who actually use old-school mail delivery? It only checks when enough time has elapsed. I imagine a "negative duration" would mean no more checks.

The lesson here is that wall time is not to be used to measure durations. Any time you see someone subtracting wall times (i.e., anything from time() or gettimeofday()), worry. Measure durations with a monotonic clock if your device has one. The actual values are a black box, but you can subtract one from the other and arrive at a count of how many of their units have elapsed... ish.

Be sure to pay attention to which monotonic clock you use if you have a choice and there's any possibility the machine can go to sleep. "Monotonic time I have been running" and "monotonic time since I was booted" are two different things on such devices.

Here's today's bonus "smash head here" moment. From the man pages for clock_gettime on a typical Linux box:

CLOCK_MONOTONIC: "This clock does not count time that the system is suspended."

Here's the same bit on a current (Ventura) Mac:

CLOCK_MONOTONIC: "...and will continue to increment while the system is asleep."

Ah yes, portability. The cause of, and solution to, all of life's software issues.



from Hacker News https://ift.tt/PqFTLpC

The Calculator Drawer

1,328 1.3K

Mar 14, 2018 03/18

by Vtech

software

eye 1,328

favorite 2

comment 0

Keys: Power On: F1 Power Off: F2 Numbers: Number Pad Arithmetic: +, -, /, - Left,. Right: Arrow Keys Choose Divide: F Choose Multiple: D Choose Minus: S Choose Add: A Quiz E Count: Q Game: W Equals: Return/Enter Calculate/Clear: R

0 0.0

Jan 27, 2023 01/23

by Hewlett-Packard and MAME Team

software

eye 0

favorite 2

comment 0

The HP 48 is a series of graphing calculators designed and produced by Hewlett-Packard from 1990 until 2003. The series includes the HP 48S, HP 48SX, HP 48G, HP 48GX, and HP 48G+, the G models being expanded and improved versions of the S models. The models with an X suffix are expandable via special RAM (memory expansion) and ROM (software application) cards. In particular, the GX models have more onboard memory than the G models. The G+ models have more onboard memory only. The SX and S...

0 0.0

Jan 27, 2023 01/23

by Hewlett-Packard and MAME Team

software

eye 0

favorite 5

comment 0

The HP 48 is a series of graphing calculators designed and produced by Hewlett-Packard from 1990 until 2003. The series includes the HP 48S, HP 48SX, HP 48G, HP 48GX, and HP 48G+, the G models being expanded and improved versions of the S models. The models with an X suffix are expandable via special RAM (memory expansion) and ROM (software application) cards. In particular, the GX models have more onboard memory than the G models. The G+ models have more onboard memory only. The SX and S...

0 0.0

Jan 27, 2023 01/23

by Hewlett-Packard and MAME Team

software

eye 0

favorite 1

comment 0

The HP 49/50 series are Hewlett-Packard (HP) manufactured graphing calculators. They are the successors of the popular HP 48 series. There are five calculators in the 49/50 series of HP graphing calculators. These calculators have both algebraic and RPN entry modes, and can perform numeric and symbolic calculations using the built-in Computer Algebra System (CAS), which is an improved ALG48 and Erable combination from the HP 48 series. Released in August 1999, the HP 49G (F1633A, F1896A)...

0 0.0

Jan 27, 2023 01/23

by Hewlett Packard and MAME Team

software

eye 0

favorite 1

comment 0

The HP 38G (F1200A, F1892A) is a programmable graphing calculator by Hewlett-Packard (HP). It was introduced in 1995 with a suggested retail price of US$80. HP credits a committee of eight high school, community college, and university teachers with assisting in the design of the calculator. The calculator is derived from and the hardware is based on the more powerful science/engineering oriented HP 48 series of machines. Unlike the HP 48 series, which offered both reverse Polish notation and...

0 0.0

Jan 29, 2023 01/23

by Texas Iator:Texas Instruments and MAME Team

software

eye 0

favorite 0

comment 0

The TI-83 series is a series of graphing calculators manufactured by Texas Instruments. The original TI-83 is itself an upgraded version of the TI-82. Released in 1996, it was one of the most popular graphing calculators for students. In addition to the functions present on normal scientific calculators, the TI-83 includes many features, including function graphing, polar/parametric/sequence graphing modes, statistics, trigonometric, and algebraic functions, along with many useful applications....

0 0.0

Jan 27, 2023 01/23

by Texas Instruments and MAME Team

software

eye 0

favorite 0

comment 0

The TI 73 series is a series of graphing calculators made by Texas Instruments, all of which have identical hardware. The original TI-73 graphing calculator was originally designed in 1998 as a replacement for the TI-80 for use at a middle school level (grades 6-9). Its primary advantage over the TI-80 is its 512 KB of flash memory, which holds the calculator's operating system and thereby allows the calculator to be upgraded. Other advantages over the TI-80 are the TI-73's standard sized...

0 0.0

Jan 27, 2023 01/23

by Texas Instruments and MAME Team

software

eye 0

favorite 0

comment 0

The TI-81 was the first graphing calculator released by Texas Instruments. It was designed in 1990 for use in algebra and precalculus courses. Since its original release, it has been superseded several times by newer calculators: the TI-85, TI-82, TI-83, TI-86, TI-83 Plus, TI-83 Plus Silver Edition, TI-84 Plus, TI-84 Plus Silver Edition, TI-84 Plus C Silver Editon TI-Nspire, TI-Nspire CAS, TI-84 Plus CE, and most recently, the TI-84 Plus CE Python. Most of these share the original feature set...

0 0.0

Jan 24, 2023 01/23

by Texas Instruments and MAME Team

software

eye 0

favorite 16

comment 2

The TI-82 is a graphing calculator made by Texas Instruments. The TI-82 was designed in 1993 as a stripped down, more user friendly version of the TI-85, and as a replacement for the TI-81. It was the direct predecessor of the TI-83. It shares with the TI-85 a 6 MHz Zilog Z80 microprocessor. Like the TI-81, the TI-82 features a 96x64 pixel display, and the core feature set of the TI-81 with many new features. The TI-82 is powered by the same processor that powered its cousin, the TI-85, a 6 MHz...
favoritefavoritefavoritefavorite ( 2 reviews )

0 0.0

Jan 27, 2023 01/23

by Texas Instruments and MAME Team

software

eye 0

favorite 2

comment 0

The TI-85 is a graphing calculator made by Texas Instruments based on the Zilog Z80 microprocessor. Designed in 1992 as TI's second graphing calculator (the first was the TI-81), it was replaced by the TI-86, which has also been discontinued. The TI-85 was significantly more powerful than the TI-81, as it was designed as a calculator primarily for use in engineering and calculus courses. Texas Instruments had included a version of BASIC on the device to allow programming. Each calculator came...

0 0.0

Jan 27, 2023 01/23

by Texas Instruments and MAME Team

software

eye 0

favorite 0

comment 0

The TI-86 is a programmable graphing calculator introduced in 1996 which was produced by Texas Instruments. The TI-86 uses the Zilog Z80 microprocessor. It is partially backwards-compatible with its predecessor, the TI-85. In addition to having a larger screen than the TI-83, the TI-86 also allows the user to type in lower case and Greek letters and features five softkeys, which improve menu navigation and can be programmed by the user for quick access to common operations such as...

0 0.0

Jan 27, 2023 01/23

by Texas Instruments and MAME Team

software

eye 0

favorite 3

comment 2

The TI-89 and the TI-89 Titanium are graphing calculators developed by Texas Instruments (TI). They are differentiated from most other TI graphing calculators by their computer algebra system, which allows symbolic manipulation of algebraic expressions—equations can be solved in terms of variables, whereas the TI-83/84 series can only give a numeric result. The TI-89 is a graphing calculator developed by Texas Instruments in 1998. The unit features a 160×100 pixel resolution LCD and a large...
favoritefavoritefavoritefavoritefavorite ( 2 reviews )

0 0.0

Jan 27, 2023 01/23

by Texas Instruments and MAME Team

software

eye 0

favorite 4

comment 1

The TI-92 series of graphing calculators are a line of calculators produced by Texas Instruments. They include: the TI-92 (1995), the TI-92 II (1996), the TI-92 Plus (1998, 1999) and the Voyage 200 (2002). The design of these relatively large calculators includes a QWERTY keyboard. Because of this keyboard, it was given the status of a "computer" rather than "calculator" by American testing facilities and cannot be used on tests such as the SAT or AP Exams while the similar...
favoritefavoritefavoritefavoritefavorite ( 1 reviews )

0 0.0

Jan 27, 2023 01/23

by Texas Instruments and MAME Team

software

eye 0

favorite 5

comment 0

The TI-92 series of graphing calculators are a line of calculators produced by Texas Instruments. They include: the TI-92 (1995), the TI-92 II (1996), the TI-92 Plus (1998, 1999) and the Voyage 200 (2002). The design of these relatively large calculators includes a QWERTY keyboard. Because of this keyboard, it was given the status of a "computer" rather than "calculator" by American testing facilities and cannot be used on tests such as the SAT or AP Exams while the similar...

Like a small pile inside the drawer of calculators, this is a small collection of reference manuals for emulated calculators, allowing a studious reader to learn to use the calculators.



from Hacker News https://ift.tt/QL3OHJU